fix: support AgentRun command cancellation

This commit is contained in:
Codex
2026-06-11 16:01:53 +00:00
parent 4057e0e856
commit 5ba46f0dc3
2 changed files with 35 additions and 9 deletions
+4 -3
View File
@@ -56,6 +56,7 @@ bun scripts/cli.ts agentrun ack session/<sessionId>
bun scripts/cli.ts agentrun dispatch task/<taskId>
bun scripts/cli.ts agentrun send session/<sessionId> --aipod Artificer --prompt-stdin
bun scripts/cli.ts agentrun cancel session/<sessionId> --reason <text> --dry-run
bun scripts/cli.ts agentrun cancel command/<commandId> --run <runId> --reason <text> --dry-run
```
日常 task manifest 优先使用 YAML heredoc`agentrun apply -f -`;单 prompt 派单优先 `agentrun create task --aipod Artificer --prompt-stdin`;同 session 续跑只使用 `agentrun send session/<sessionId>`。UniDesk 客户端按 `config/agentrun.yaml` 直连 AgentRun REST API,不经过 HWLAB runtime、SSH official CLI 或旧 bridge wrapper`send` 是唯一用户级 session follow-up 写入口,服务端按 durable session/run/command 状态自动决定内部 `steer` 或新 `turn`,旧 CLI `turn/steer` 路径不保留兼容。`--json-file``--prompt-file``--runner-json-file` 只是客户端输入来源,用于已审阅且可复用的受控文件。它不是旧 Code Queue adapter,不双写,也不迁移旧历史。
@@ -71,9 +72,9 @@ AgentRun queue 生命周期不是一个单独的 `queue lifecycle` 命令,而
1. 默认总览用 `get tasks --queue commander --limit 20`,只看 task state、queue/lane、run/cmd/rjob/session ref、age 和 attention。
2. 单任务用 `describe task/<taskId>`,读取 `latestAttempt.runId``commandId``runnerJobId``sessionId/sessionPath` 和少量 `Next:`
3. Run 级状态用 `events run/<runId>``result run/<runId> --command <commandId>`,判断 terminalClassification、failureKind、provider interruption、timeoutBudget 和 recoveryActions。
4. Command 级状态用 `describe command/<commandId> --run <runId>``result command/<commandId> --run <runId>`,确认 command state、ack、terminal status 和结果摘要。
4. Command 级状态用 `describe command/<commandId> --run <runId>``result command/<commandId> --run <runId>`,确认 command state、ack、terminal status 和结果摘要;确认为单个 active command 卡住时,用 `cancel command/<commandId> --run <runId> --reason <text>` 清理该 command,保留同一个 session 后再用 `send session/<sessionId>` 续跑
5. Runner job 只读状态用 `describe runnerjob/<runnerJobId> --run <runId>`,确认 env image reuse、jobName、namespace、phase、exitCode、retention 和 `valuesPrinted=false`。不要为了这些字段手动调用 `trans G14:k3s kubectl ...`
6. Session trace/output 只在 `describe task` 或 result 里有实际 `sessionId` 时使用 `logs|ack|send|cancel session/<sessionId>``sessionRef=null` 时不要猜 session 命令。
6. Session trace/output 只在 `describe task` 或 result 里有实际 `sessionId` 时使用 `logs|ack|send|cancel session/<sessionId>``sessionRef=null` 时不要猜 session 命令。用户级 follow-up 一律使用 `send session/<sessionId>`,不要回到旧 `turn/steer``sessions ...` 兼容路径。
7. 已创建但尚未运行的 task 使用 `dispatch task/<taskId>` 派发,不再退回旧 bridge `queue dispatch`
默认视图必须低噪声且不是 JSON envelope`-o json|yaml` 才输出稳定机器结构,`--raw` 才保留直连 AgentRun REST envelope;命令返回里的下一步应优先是 `bun scripts/cli.ts agentrun ...` 资源原语,不得把人工 k8s 查询作为日常下一步。
@@ -188,7 +189,7 @@ bun scripts/cli.ts codex interrupt <taskId>
bun scripts/cli.ts codex cancel <taskId>
```
仅用于停止旧 Code Queue 残留任务;新 AgentRun session 使用 `bun scripts/cli.ts agentrun cancel session/<sessionId>`
仅用于停止旧 Code Queue 残留任务;新 AgentRun session 使用 `bun scripts/cli.ts agentrun cancel session/<sessionId>`,单个卡住 command 使用 `bun scripts/cli.ts agentrun cancel command/<commandId> --run <runId>`
---
+31 -6
View File
@@ -198,7 +198,7 @@ function agentRunHelpText(args: string[]): string {
return "Usage: bun scripts/cli.ts agentrun ack task/<taskId>|session/<sessionId> [--reader-id cli]";
}
if (verb === "cancel") {
return "Usage: bun scripts/cli.ts agentrun cancel task/<taskId>|session/<sessionId> --reason <text> [--dry-run]";
return "Usage: bun scripts/cli.ts agentrun cancel task/<taskId>|session/<sessionId>|run/<runId>|command/<commandId> --reason <text> [--run <runId>] [--dry-run]";
}
if (verb === "dispatch") {
return "Usage: bun scripts/cli.ts agentrun dispatch task/<taskId>";
@@ -576,16 +576,41 @@ async function resourceAck(config: UniDeskConfig | null, command: string, action
async function resourceCancel(config: UniDeskConfig | null, command: string, action: string | undefined, args: string[], options: AgentRunResourceOptions): Promise<RenderedCliResult> {
const ref = parseResourceRef(action, args, "task");
const cancelArgs = ref.kind === "task" ? ["cancel", ref.name] : ref.kind === "session" ? ["cancel", ref.name] : null;
if (cancelArgs === null) throw new Error("cancel supports task/<taskId> or session/<sessionId>");
if (options.reason !== null && ref.kind === "task") cancelArgs.push("--reason", options.reason);
if (options.dryRun) cancelArgs.push("--dry-run");
const cancelArgs = ["cancel", ref.name];
if (options.reason !== null) cancelArgs.push("--reason", options.reason);
if (ref.kind === "command") cancelArgs.push("--run-id", options.runId ?? requiredContext("command cancel", "--run <runId>"));
if (options.dryRun) {
const result = agentRunResourceCancelDryRunPlan(ref, options, rerunWithoutDryRun(command));
return renderMutationSummary(command, result, options, `Planned cancel ${ref.kind}/${shortId(ref.name)}`, [rerunWithoutDryRun(command)]);
}
const result = ref.kind === "task"
? await runAgentRunRestCommand(config, "queue", cancelArgs)
: await runAgentRunRestCommand(config, "sessions", cancelArgs);
: ref.kind === "session"
? await runAgentRunRestCommand(config, "sessions", cancelArgs)
: ref.kind === "run"
? await runAgentRunRestCommand(config, "runs", cancelArgs)
: ref.kind === "command"
? await runAgentRunRestCommand(config, "commands", cancelArgs)
: null;
if (result === null) throw new Error("cancel supports task/<taskId>, session/<sessionId>, run/<runId>, or command/<commandId>");
return renderMutationSummary(command, result, options, `${options.dryRun ? "Planned cancel" : "Cancel requested"} ${ref.kind}/${shortId(ref.name)}`, options.dryRun ? [rerunWithoutDryRun(command)] : undefined);
}
function agentRunResourceCancelDryRunPlan(ref: AgentRunResourceRef, options: AgentRunResourceOptions, confirmCommand: string): Record<string, unknown> {
const body: Record<string, unknown> = {};
if (options.reason !== null) body.reason = options.reason;
if (ref.kind === "task") return agentRunDryRunPlan("task-cancel", `/api/v1/queue/tasks/${encodeURIComponent(ref.name)}/cancel`, body, confirmCommand);
if (ref.kind === "session") return agentRunDryRunPlan("session-cancel", `/api/v1/sessions/${encodeURIComponent(ref.name)}/control`, { action: "cancel", ...body }, confirmCommand);
if (ref.kind === "run") return agentRunDryRunPlan("run-cancel", `/api/v1/runs/${encodeURIComponent(ref.name)}/cancel`, body, confirmCommand);
if (ref.kind === "command") {
const runId = options.runId ?? requiredContext("command cancel", "--run <runId>");
return agentRunDryRunPlan("command-cancel", `/api/v1/commands/${encodeURIComponent(ref.name)}/cancel`, body, confirmCommand, "POST", {
commandRef: { runId, commandId: ref.name, valuesPrinted: false },
});
}
throw new Error("cancel supports task/<taskId>, session/<sessionId>, run/<runId>, or command/<commandId>");
}
async function resourceDispatch(config: UniDeskConfig | null, command: string, action: string | undefined, args: string[], options: AgentRunResourceOptions): Promise<RenderedCliResult> {
const ref = parseResourceRef(action, args, "task");
if (ref.kind !== "task") throw new Error("dispatch supports task/<taskId>");