fix: 统一恢复建议使用 session send

This commit is contained in:
AgentRun Codex
2026-06-11 22:47:26 +08:00
parent 90d4bc8cd2
commit 1a9b6debbb
4 changed files with 8 additions and 9 deletions
+4 -4
View File
@@ -1,13 +1,13 @@
# v0.1 AipodSpec 规格
`AipodSpec` 是 AgentRun `v0.1` 的声明式 agent 装配规格。它把已有的 `imageRef``backendProfile``executionPolicy.secretScope``ResourceBundleRef.kind="gitbundle"`、Queue task 和 Session turn 装配入口集中到 YAML 文件中,避免把某个 agent 的 work-ready env image、模型、SecretRef、gitbundle、skill 或 tool 写死在 manager、runner 或 CLI 源码里。
`AipodSpec` 是 AgentRun `v0.1` 的声明式 agent 装配规格。它把已有的 `imageRef``backendProfile``executionPolicy.secretScope``ResourceBundleRef.kind="gitbundle"`、Queue task 和 Session send 装配入口集中到 YAML 文件中,避免把某个 agent 的 work-ready env image、模型、SecretRef、gitbundle、skill 或 tool 写死在 manager、runner 或 CLI 源码里。
## 设计边界
- `AipodSpec` 只声明装配意图,不保存 API key、SSH private key、token、`auth.json``config.toml` 或其他 Secret 明文。
- manager 通过 `/api/v1/aipod-specs` 对 YAML 做增删改查;默认目录为仓库 `config/aipods/`,可用 `AGENTRUN_AIPOD_SPEC_DIR` 覆盖。
- CLI 通过 `aipod-specs list|show|render|apply|delete` 管理规格,通过 `queue submit --aipod <name>``sessions turn --aipod <name>` 使用规格。
- `render` 只把规格展开为标准 Queue task / Session turn 输入,输出必须脱敏,只显示 imageRef source 摘要、SecretRef 名称、key、projection、gitbundle 摘要和 `valuesPrinted=false`
- CLI 通过 `aipod-specs list|show|render|apply|delete` 管理规格,通过 `queue submit --aipod <name>``sessions send --aipod <name>` 使用规格。
- `render` 只把规格展开为标准 Queue task / Session send 输入,输出必须脱敏,只显示 imageRef source 摘要、SecretRef 名称、key、projection、gitbundle 摘要和 `valuesPrinted=false`
- `AipodSpec` 不引入第二套 scheduler、runner、backend adapter 或 Code Queue;最终执行仍走 AgentRun Queue、Sessions、runner Job 和 Codex app-server stdio backend。
## YAML 结构
@@ -144,7 +144,7 @@ CLI
./scripts/agentrun aipod-specs apply --yaml-stdin
./scripts/agentrun aipod-specs delete Artificer
./scripts/agentrun queue submit --aipod Artificer --prompt-stdin --idempotency-key <key>
./scripts/agentrun sessions turn --aipod Artificer --prompt-stdin
./scripts/agentrun sessions send --aipod Artificer --prompt-stdin
./scripts/agentrun tool-credentials set-github-ssh --private-key-file <id_ed25519> --known-hosts-file <known_hosts> [--config-file <ssh_config>]
```
+1 -2
View File
@@ -93,8 +93,7 @@ Session 命令负责输出、trace 和会话控制:
```bash
./scripts/agentrun sessions ps [--state default|running|unread|terminal|idle|all] [--profile codex|deepseek|minimax-m3|M3] [--reader-id <reader>]
./scripts/agentrun sessions show <sessionId> [--reader-id <reader>]
./scripts/agentrun sessions turn [sessionId] [--json-stdin|--json-file <run-base.json>] [--prompt-stdin|--prompt-file <file>|--prompt <text>] [--profile codex|deepseek|minimax-m3|M3]
./scripts/agentrun sessions steer <sessionId> [--prompt-stdin|--prompt-file <file>|--prompt <text>]
./scripts/agentrun sessions send [sessionId] [--json-stdin|--json-file <run-base.json>] [--prompt-stdin|--prompt-file <file>|--prompt <text>] [--profile codex|deepseek|minimax-m3|M3]
./scripts/agentrun sessions cancel <sessionId> [--reason <text>]
./scripts/agentrun sessions output <sessionId> [--after-seq <seq>] [--limit <limit>]
./scripts/agentrun sessions trace <sessionId> [--after-seq <seq>] [--limit <limit>]
+1 -1
View File
@@ -171,7 +171,7 @@ function recoveryActionsForDiagnosis(input: { run: RunRecord; command: CommandRe
if (input.command) actions.push({ action: "inspect-command", commandId: input.command.id, command: `./scripts/agentrun commands result ${input.command.id} --run-id ${input.run.id}`, valuesPrinted: false });
actions.push({ action: "poll-events", runId: input.run.id, afterSeq: input.lastSeq, command: `./scripts/agentrun runs events ${input.run.id} --after-seq ${input.lastSeq} --limit 100 --tail-summary`, valuesPrinted: false });
const sessionId = stringValue(input.session.sessionId);
if (sessionId) actions.push({ action: "resume-session", sessionId, command: `./scripts/agentrun sessions turn ${sessionId} --prompt-stdin`, valuesPrinted: false });
if (sessionId) actions.push({ action: "continue-session", sessionId, command: `./scripts/agentrun sessions send ${sessionId} --prompt-stdin`, valuesPrinted: false });
else actions.push({ action: "session-unavailable", reason: "sessionRef=null", hint: "当前 run 没有 sessionRef,管理者只能从 run/events/command/runner-job 读取 trace 后重新提交;这表示该任务不可同 session 续跑。", valuesPrinted: false });
if (input.runnerLost || input.staleClaimed || input.terminalCommandOpenRun) actions.push({ action: "refresh-queue-or-resubmit", reason: input.failureKind ?? "stale-runner-state", hint: "先用 queue refresh/show 对齐 attempt;有 sessionId 时继续同一 session,没有 sessionId 才重新派发。", valuesPrinted: false });
return actions.slice(0, 6);
+2 -2
View File
@@ -50,7 +50,7 @@ const selfTest: SelfTestCase = async (context: SelfTestContext) => {
assert.equal(((terminalResult.terminalClassification as JsonRecord).idleTimeout), true);
assert.equal(((terminalResult.terminalClassification as JsonRecord).providerEvidence), "insufficient");
assert.equal(((terminalLive.terminalClassification as JsonRecord).providerInterruptionKnown), false);
assert.ok((terminalLive.recoveryActions as JsonRecord[]).some((action) => action.action === "resume-session"));
assert.ok((terminalLive.recoveryActions as JsonRecord[]).some((action) => action.action === "continue-session"));
assert.ok((terminalLive.recoveryActions as JsonRecord[]).some((action) => action.action === "split-task"));
const noSession = await createActiveRun(client, context, "timeout-liveness-no-session", 50, { session: false });
@@ -70,7 +70,7 @@ const selfTest: SelfTestCase = async (context: SelfTestContext) => {
assert.ok((noSessionDiagnosis.recoveryActions as JsonRecord[]).some((action) => action.action === "session-unavailable"));
assert.match(String(noSessionClassification.providerInterruptionReason), /cannot distinguish provider outage/u);
assert.equal((noSessionLive.transportDisconnect as JsonRecord).sourceSeq, 4);
assert.equal((noSessionLive.recoveryActions as JsonRecord[]).some((action) => action.action === "resume-session"), false, "sessionId=null must not suggest session-only resume");
assert.equal((noSessionLive.recoveryActions as JsonRecord[]).some((action) => action.action === "continue-session"), false, "sessionId=null must not suggest session-only continuation");
assert.equal((noSessionLive.recoveryActions as JsonRecord[]).some((action) => action.action === "poll-output"), false, "sessionId=null must not suggest session output path");
assert.ok((noSessionLive.recoveryActions as JsonRecord[]).some((action) => action.action === "poll-trace" && String(action.command).includes("runs events")));