feat: add v2 task control and asynchronous isolated workers
This commit is contained in:
@@ -20,6 +20,9 @@
|
||||
#include "cst_agent.h"
|
||||
#include "cst_attach.h"
|
||||
#include "cst_models.h"
|
||||
#include "cst_runtime.h"
|
||||
#include "cst_subagent.h"
|
||||
#include "cst_worktree.h"
|
||||
#include "cst_provider_switch.h"
|
||||
#include "cst_web_md.h"
|
||||
|
||||
@@ -233,8 +236,13 @@ QString firstLine(const QString &s) {
|
||||
} // namespace
|
||||
|
||||
void AgentCore::send(const QString &prompt) {
|
||||
if (!m_agent || m_running || prompt.trimmed().isEmpty())
|
||||
if (!m_agent || prompt.trimmed().isEmpty())
|
||||
return;
|
||||
if (m_running) {
|
||||
cst_agent_steer(m_agent, prompt.toUtf8().constData(), -1, -1);
|
||||
emit statusChanged(QStringLiteral("Applying latest instruction…"));
|
||||
return;
|
||||
}
|
||||
/* The prompt takes the pending images with it: the view shows them on the
|
||||
* message the composer just appended, before the turn can say anything. */
|
||||
const QString sending = pendingImages();
|
||||
@@ -255,6 +263,36 @@ void AgentCore::cancel() {
|
||||
if (m_agent)
|
||||
cst_agent_request_cancel(m_agent);
|
||||
}
|
||||
QString AgentCore::taskState() const {
|
||||
if (!m_agent) return QStringLiteral("{}");
|
||||
char *s = cst_runtime_json(cst_agent_config(m_agent)->runtime);
|
||||
QString result = QString::fromUtf8(s ? s : "{}"); free(s); return result;
|
||||
}
|
||||
void AgentCore::taskControl(bool ro, bool delegation, bool paused) {
|
||||
if (m_agent) cst_agent_control(m_agent, ro, delegation, paused);
|
||||
}
|
||||
void AgentCore::cancelJob(const QString &id, bool force) {
|
||||
if (m_agent) cst_job_cancel(cst_agent_config(m_agent)->runtime, id.toUtf8().constData(), force, "user");
|
||||
}
|
||||
QString AgentCore::workerConfig() const {
|
||||
if (!m_agent) return QStringLiteral("{}");
|
||||
char *s = cst_workers_config_get(cst_agent_config(m_agent)->workspace_root);
|
||||
QString result = QString::fromUtf8(s ? s : "{}"); free(s); return result;
|
||||
}
|
||||
QString AgentCore::saveWorkerConfig(const QString &json) {
|
||||
if (!m_agent || m_running) return QStringLiteral("Wait for the active task before changing model profiles.");
|
||||
char err[256];
|
||||
return cst_workers_config_set(cst_agent_config(m_agent)->workspace_root, json.toUtf8().constData(), err, sizeof(err)) ? QString::fromUtf8(err) : QString();
|
||||
}
|
||||
QString AgentCore::worktreeDiff(const QString &path, bool integrate) {
|
||||
if (!m_agent || m_running) return QStringLiteral("Wait for the active task before reviewing or integrating changes.");
|
||||
const auto *cfg = cst_agent_config(m_agent);
|
||||
if (cst_runtime_jobs_pending(cfg->runtime) || (integrate && !cst_runtime_check(cfg->runtime, 0, 1, 0)))
|
||||
return QStringLiteral("Integration is blocked by active jobs or task controls.");
|
||||
int code;
|
||||
char *s = cst_worktree_diff(cst_agent_config(m_agent)->workspace_root, path.toUtf8().constData(), integrate, &code);
|
||||
QString result = QString::fromUtf8(s ? s : "error"); free(s); return result;
|
||||
}
|
||||
|
||||
/* Qt suspends the application when the phone puts it away, and a desktop
|
||||
* window can be gone too (--uitest runs headless). Either way a consent
|
||||
|
||||
@@ -157,6 +157,12 @@ class AgentCore : public QObject {
|
||||
/* Ask a question; emits the streaming signals, then turnFinished(). */
|
||||
Q_INVOKABLE void send(const QString &prompt);
|
||||
Q_INVOKABLE void cancel();
|
||||
Q_INVOKABLE QString taskState() const;
|
||||
Q_INVOKABLE void taskControl(bool readOnly, bool delegation, bool paused);
|
||||
Q_INVOKABLE void cancelJob(const QString &id, bool force);
|
||||
Q_INVOKABLE QString workerConfig() const;
|
||||
Q_INVOKABLE QString saveWorkerConfig(const QString &json);
|
||||
Q_INVOKABLE QString worktreeDiff(const QString &path, bool integrate);
|
||||
|
||||
/* Consent: answers map to CST_PERM_*. */
|
||||
Q_INVOKABLE void answerPermission(int choice);
|
||||
|
||||
Reference in New Issue
Block a user