feat: add v2 task control and asynchronous isolated workers

This commit is contained in:
WHC
2026-10-20 14:46:57 +08:00
parent 143dba203b
commit d971cb02b0
26 changed files with 1590 additions and 66 deletions
+61
View File
@@ -24,6 +24,8 @@
#include "cst_sandbox.h"
#include "cst_skills.h"
#include "cst_subagent.h"
#include "cst_runtime.h"
#include "cst_worktree.h"
#include "cst_tools.h"
#include "cst_util.h"
#include "cst_web_md.h"
@@ -2358,6 +2360,61 @@ static int h_worker_cancel(cst_http_conn_t *c, void *ud) {
return cst_http_send_str(c, 200, "application/json", "{\"ok\":true}");
}
static int h_task_get(cst_http_conn_t *c, void *ud) {
wui_t *w = ud;
if (wui_claim_read(w)) return cst_http_send_err(c, 409, "session is changing");
char *s = cst_runtime_json(cst_agent_config(w->agent)->runtime);
cJSON *j = s ? cJSON_Parse(s) : NULL; free(s); wui_release(w);
return wui_send_obj(c, 200, j ? j : cJSON_CreateObject());
}
static int h_task_control(cst_http_conn_t *c, void *ud) {
wui_t *w = ud;
if (wui_claim_read(w)) return cst_http_send_err(c, 409, "session is changing");
cJSON *j = wui_body(c);
const cJSON *msg = cJSON_GetObjectItemCaseSensitive(j, "message");
const cJSON *ro = cJSON_GetObjectItemCaseSensitive(j, "read_only");
const cJSON *delegate = cJSON_GetObjectItemCaseSensitive(j, "delegation");
const cJSON *paused = cJSON_GetObjectItemCaseSensitive(j, "paused");
int valid = cJSON_IsObject(j) && (!msg || cJSON_IsString(msg)) && (!ro || cJSON_IsBool(ro)) && (!delegate || cJSON_IsBool(delegate)) && (!paused || cJSON_IsBool(paused));
int rc = -1;
if (valid) {
pthread_mutex_lock(&w->mu); int running = w->busy; pthread_mutex_unlock(&w->mu);
if (msg && msg->valuestring[0] && !running) {
if (ro || delegate || paused) cst_agent_control(w->agent, ro ? cJSON_IsTrue(ro) : -1, delegate ? cJSON_IsTrue(delegate) : -1, paused ? cJSON_IsTrue(paused) : -1);
char error[256]; rc = wui_start_turn(w, msg->valuestring, 1, error, sizeof(error));
} else if (msg && msg->valuestring[0]) rc = cst_agent_steer(w->agent, msg->valuestring, ro ? cJSON_IsTrue(ro) : -1, delegate ? cJSON_IsTrue(delegate) : -1);
else rc = cst_agent_control(w->agent, ro ? cJSON_IsTrue(ro) : -1, delegate ? cJSON_IsTrue(delegate) : -1, paused ? cJSON_IsTrue(paused) : -1);
}
cJSON_Delete(j); wui_release(w);
if (rc) return cst_http_send_err(c, 400, "expected message and/or boolean task controls");
return cst_http_send_str(c, 202, "application/json", "{\"ok\":true}");
}
static int h_worktree(cst_http_conn_t *c, void *ud) {
wui_t *w = ud;
if (wui_claim(w)) return cst_http_send_err(c, 409, "wait for the active task");
cJSON *j = wui_body(c);
const cJSON *path = cJSON_GetObjectItemCaseSensitive(j, "worktree");
int integrate = cJSON_IsTrue(cJSON_GetObjectItemCaseSensitive(j, "integrate"));
const cst_agent_config_t *cfg = cst_agent_config(w->agent);
int code = 1; char *text = NULL;
if (cJSON_IsString(path) && (!integrate || cst_runtime_check(cfg->runtime, cst_runtime_revision(cfg->runtime), 1, 0)) && !cst_runtime_jobs_pending(cfg->runtime))
text = cst_worktree_diff(cfg->workspace_root, path->valuestring, integrate, &code);
cJSON_Delete(j); wui_release(w);
cJSON *result = cJSON_CreateObject(); cJSON_AddBoolToObject(result, "ok", !code);
cJSON_AddStringToObject(result, "output", text ? text : "task is read-only, jobs are active, or worktree is missing"); free(text);
return wui_send_obj(c, code ? 409 : 200, result);
}
static int h_job_cancel(cst_http_conn_t *c, void *ud) {
wui_t *w = ud;
if (wui_claim_read(w)) return cst_http_send_err(c, 409, "session is changing");
cJSON *j = wui_body(c); const cJSON *id = cJSON_GetObjectItemCaseSensitive(j, "job_id");
const cJSON *force = cJSON_GetObjectItemCaseSensitive(j, "force");
int rc = cJSON_IsString(id) ? cst_job_cancel(cst_agent_config(w->agent)->runtime, id->valuestring, cJSON_IsTrue(force), "user") : -1;
cJSON_Delete(j); wui_release(w);
if (rc) return cst_http_send_err(c, 404, "active job not found");
return cst_http_send_str(c, 202, "application/json", "{\"ok\":true}");
}
/* POST /api/extensions/trust {"on": bool}: whether .agent/plugins and
* .agent/mcp.json of this workspace load, then reload. */
static int h_ext_trust_x(cst_http_conn_t *c, void *ud) {
@@ -2604,6 +2661,10 @@ int cst_webui_run(cst_agent_t *agent, const cst_webui_opts_t *opts) {
static cst_http_route_t routes[] = {
{"GET", "/", h_asset, NULL, 0, 0},
{"GET", "/api/state", h_state, NULL, 0, 0},
{"GET", "/api/task", h_task_get, NULL, 0, 0},
{"POST", "/api/task", h_task_control, NULL, 0, 0},
{"POST", "/api/jobs/cancel", h_job_cancel, NULL, 0, 0},
{"POST", "/api/worktree", h_worktree, NULL, 0, 0},
{"GET", "/api/session", h_session, NULL, 0, 0},
{"GET", "/api/events", h_events, NULL, 0, 0},
{"GET", "/api/sessions", h_sessions, NULL, 0, 0},