Skip to content

Commit f07eb81

Browse files
authored
Merge pull request #38 from vitry/fix/rescue-timeout-lifecycle
Fix Rescue lifecycle timeout and same-turn continuation
2 parents fbb1314 + fbb4735 commit f07eb81

43 files changed

Lines changed: 3049 additions & 355 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ All notable changes follow Semantic Versioning.
44

55
## Unreleased
66

7+
- Accepted ZCode CLI 0.16.3's captured initial empty-session revision and pre-turn settings snapshots while retaining exact empty-state, event-sequence, identity, workspace, and activity checks. Real qualification now proves two visible responses through each turn's exact persisted user-root parent chain when the CLI remaps request input IDs.
8+
- Removed the plugin-defined ordinary Rescue completion deadline while retaining finite request, review-gate, qualification, caller credential, and one-shot preparation budgets. Active parent authority is now hook-lifecycle-bound; same-parent-turn continuation replaces consumed preparation generation 1 with an executor-bound generation 2, follows up the exact stopped child, and reuses the exact binding and ZCode session. Root Stop, replacement prompts, SessionEnd, explicit cancellation, SIGINT, and SIGTERM remain authoritative boundaries. Role readiness now distinguishes `caller-unavailable` and `inspection-unavailable` from managed setup states; existing owned Roles require the normal one-time setup upgrade.
79
- Added private durable per-job human-readable logs that retain the complete accepted safe semantic-progress history while job previews remain bounded to four entries. Exact-owner detailed status displays the private absolute path; compact, foreign, sibling-session, sidecar, relay, and terminal surfaces remain path-free, with no new log command or retention lifecycle.
810
- Fixed ZCode CLI 0.16.3 conversation progress compatibility: bounded initial, online-overflow, and recovery snapshots now establish silent sequence baselines; all five production delta operations are structurally accepted; and only validated tool/turn rows can emit bounded public progress. Online deltas require the exact exclusive baseline, and any overlap or ordinal/sequence gap fences further online progress until an authoritative snapshot or a recovery delta covering the trusted sequence resets it. Online frame state and watermarks now commit transactionally after asynchronous descriptions settle, so ignored frames cannot leak later tool summaries. Structurally accepted zero-event frames remain diagnostic-only and no longer suppress or stop snapshot fallback; only a frame with bounded public semantic progress establishes online health. Snapshot history, state patches, row text deltas, and removed-row content are never rendered, while fragments remain unsupported and fall back through the existing observational progress path.
911
- Fixed terminal ZCode failure handling so failures are no longer replaced by `ZCODE_RESULT_MISSING`: failed and cancelled jobs remain queryable through `$zcode:result`, stored errors appear in result and status output, and natural-language result references without an ID select the latest finished owned job. This does not repair upstream provider SSE or network failures.

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,14 @@ Root starts `prepare rescue` on a raw-capable TTY. The companion enables raw mod
5656

5757
A durable Rescue binding now keeps one exact stopped Rescue child attached to one exact ZCode session. Its private `anchorJobId` identifies the adopted operation, and `currentJobId` advances when each continuation job is durably reserved and published, even if that job later queues, fails, or is cancelled; neither identifier is sent in a child message. A clear proactive continuation prepares resume and follows up the same stopped child, which runs the same `invoke-prepared rescue` assignment with no second `SubagentStart`. An explicit bound request without `--resume` or `--fresh` also follows up that same child and lets its bound `needs-choice` result drive the one user choice. `--fresh` always prepares an independent operation and a new child.
5858

59+
Ordinary foreground Rescue completion has no plugin-defined wall-clock deadline. Active parent authority is lifecycle-bound rather than time-bound, so a clear continuation in that still-active parent turn can replace the consumed one-shot preparation with the next 30-minute generation, follow up the exact stopped child, and reuse the exact bound ZCode session. Caller credentials and every preparation generation remain bounded to 30 minutes; request RPCs, the optional Stop review gate, qualification harnesses, and explicit status waits also retain finite budgets. Root `Stop`, a replacement `UserPromptSubmit`, `SessionEnd`, `$zcode:cancel`, `SIGINT`, and `SIGTERM` remain authoritative termination or revocation boundaries.
60+
5961
Legacy jobs-only state may adopt the exact eligible continuation candidate once; ambiguous or previously pending legacy state is rejected instead of guessed. A permission change cannot resume the old binding, while `--fresh` captures the current permission snapshot. `SessionEnd` closes the ending Codex session's Rescue binding so it cannot be revived. An invalid binding, executor mismatch, wrong workspace, closed session, or inconsistent provenance must fail closed without latest-session fallback. Because the managed Role bytes changed, `role-status rescue` can report `upgrade-required`; rerun `$zcode:setup` before continuing.
6062

6163
Foreground Rescue runs the constant forwarder in one native child thread. When the host supports `agent_type`, Codex selects the named `zcode-rescue` Role. A generic child is a host-only compatibility fallback permitted only when the active spawn schema omits `agent_type` or proves that field unsupported before any child starts; missing, shadowed, drifted, or foreign Role state is never fallback-eligible. The parent runs only the read-only Role preflight and private preparation rollout, shows native lifecycle activity, and returns the child's final public stdout; it does not execute Rescue inline or copy child stderr, tool output, raw conversation frames, or intermediate progress into the parent thread.
6264

65+
Role preflight uses fixed readiness vocabulary: `caller-unavailable` asks for an active owned parent turn, and `inspection-unavailable` asks to retry inspection without mutating setup. Managed states such as install, upgrade, drift, conflict, restart, or genuine host `unsupported` direct the user to `$zcode:setup`. Existing owned managed Role installations need the normal one-time `$zcode:setup` upgrade because these Role bytes changed.
66+
6367
Rescue children use the task-independent native display base `zcode_rescue_task`, with a bounded ordinal on sibling collision. No objective or task text is encoded in this metadata. Names and paths are for navigation only: matching `zcode_rescue_*` neither proves Rescue nor grants authority; a different display name does not remove authority from an otherwise trusted Rescue child.
6468

6569
Use `/agent` or `/subagents` to select the Rescue child and inspect its transcript. `/ps` is different: it lists background terminals owned by the currently active thread, so switch to the child first if a long-running yielded child terminal still exists. A short command may finish before appearing there. The operating-system `ps` command can show processes and argv, but not Codex model activity or thread transcripts. The noninteractive qualification harness does not expose these TUI events, so it emits the machine-readable scoped observation `{ "observed": false, "code": "tui-evidence-not-exposed", "qualificationScope": "tui" }`. That observation is not a qualification result and does not claim that the UI passed or failed.

README.zh-CN.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,14 @@ Root 在 raw-capable TTY 上启动 `prepare rescue`。companion 先启用 raw mo
5656

5757
durable Rescue binding 现在把同一个已停止的 Rescue child 绑定到一个精确 ZCode session。私有 `anchorJobId` 标识被采用的操作,`currentJobId` 在每个续做 job 被持久预留并发布时前移,即使该 job 随后排队、失败或取消;两个标识都不会进入 child message。明确的主动续做会 prepare resume 并 follow up 同一个已停止的 Rescue child;它复用相同的 `invoke-prepared rescue` assignment,不会产生第二次 `SubagentStart`。显式 bound 请求若没有 `--resume``--fresh`,也 follow up 同一 child,并由其 bound `needs-choice` 结果触发一次用户选择。`--fresh` 始终准备独立操作和新的 child。
5858

59+
普通前台 Rescue 的完成等待不设插件定义的墙钟截止时间。活动父 turn 的授权由生命周期而非时间绑定,因此在同一个仍活动的父 turn 中,明确续做可以用下一代 30 分钟的一次性 preparation 替换已消费代,follow up 精确的已停止 child,并复用精确绑定的 ZCode session。caller credential 和每一代 preparation 仍以 30 分钟为界;request RPC、可选 Stop review gate、qualification harness 和显式 status wait 也继续使用各自有限预算。Root `Stop`、替换性的 `UserPromptSubmit``SessionEnd``$zcode:cancel``SIGINT``SIGTERM` 仍是权威的终止或撤销边界。
60+
5961
legacy jobs-only 状态只会采用唯一且精确合格的续做候选;有歧义或旧 pending 状态会被拒绝而不是猜测。权限变化不能 resume 旧 binding,而 `--fresh` 会捕获当前 permission 快照。`SessionEnd` 会关闭结束 Codex session 的 Rescue binding,使其不能再次恢复。无效 binding、executor 不匹配、错误 workspace、已关闭 session 或 provenance 不一致都会 fail closed,不会 fallback 到 latest session。受管 Role 字节已经变化,因此 `role-status rescue` 可能返回 `upgrade-required`;继续前请重新运行 `$zcode:setup` 完成需要升级的 Role。
6062

6163
前台 Rescue 只在一个原生子线程中运行常量 forwarder。host 支持 `agent_type` 时,Codex 选择具名 `zcode-rescue` Role。generic child 只是 host-only 兼容回退:仅当当前 spawn schema 缺少 `agent_type`,或能证明该字段在任何 child 启动前已被拒绝时才允许;Role 缺失、被 shadow、漂移或属于外部配置时绝不回退。父线程只运行只读 Role preflight 和私有 prepare rollout、显示原生生命周期并返回 child 的最终公开 stdout;它不会 inline 执行 Rescue,也不会把 child stderr、工具输出、原始 conversation frame 或中间进度复制到父线程。
6264

65+
Role preflight 使用固定 readiness 词汇:`caller-unavailable` 要求从活动且受拥有的父 turn 重试,`inspection-unavailable` 要求仅重试检查而不修改 setup。install、upgrade、drift、conflict、restart 或真正 host `unsupported` 等受管状态会引导运行 `$zcode:setup`。由于本次 Role 字节发生变化,现有已拥有的受管 Role 安装需要按正常流程执行一次 `$zcode:setup` 升级。
66+
6367
Rescue child 使用与任务无关的原生显示基名 `zcode_rescue_task`;同级名称冲突时会添加有界序号。该 metadata 不编码业务目标或 task 文本。名称和路径只用于导航:符合 `zcode_rescue_*` 规范既不能证明 child 是 Rescue,也不会授予 Rescue 权限;显示名称不同也不会移除一个已由可信链路确认的 Rescue child 的权限。
6468

6569
使用 `/agent``/subagents` 选择 Rescue child 并查看它的 transcript。`/ps` 含义不同:它只列出当前活动线程拥有的后台 terminal,所以若一个耗时 child terminal 已 yield,应先切换到 child;短命令可能在出现在列表前就已结束。操作系统的 `ps` 只能显示进程和 argv,不能显示 Codex 模型活动或线程 transcript。非交互 qualification harness 不暴露这些 TUI event,因此会输出机器可读的作用域观测 `{ "observed": false, "code": "tui-evidence-not-exposed", "qualificationScope": "tui" }`。该观测不是资格结果,也不会声称 UI 已通过或失败。

SECURITY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Report suspected vulnerabilities privately through GitHub's private vulnerabilit
1717
- Child stderr and detailed progress stay in the child thread. Parent-visible output is limited to host lifecycle events and the final public result. Subscription or optional progress-sink failure is observational and cannot weaken the authoritative completion guard.
1818
- Rescue task material exists in the routing rollout only as the Root parent's single LF-terminated JSON line sent through `write_stdin` to `prepare rescue`. The companion requires a raw-capable TTY and enables raw mode before emitting task-free readiness and before accepting any task bytes; readiness is nonterminal. Root sends no EOF or U+0004. Non-TTY/readiness/raw-mode failure stops before delivery. Tool output must never contain or echo the payload. Prepared state is bound to the exact Codex session, initiating turn, canonical workspace, and executor identity; it is single consume, has a bounded expiry, and is subject to private-state cleanup. The task, source, and options must never appear in argv, environment variables, output, logs, artifacts, relays, status, task names, or any child assignment/transcript. Named and generic children are task-blind and capability-free and receive only the constant `invoke-prepared rescue` assignment.
1919
- A durable Rescue binding authorizes the same stopped child to continue only its exact ZCode session. The private `anchorJobId` and `currentJobId` remain inside protected plugin state and never cross the parent-to-child message boundary. Missing, closed, corrupt, permission-incompatible, workspace-mismatched, executor-mismatched, or provenance-inconsistent bindings fail closed.
20+
- Ordinary foreground completion has no plugin wall-clock deadline. Authority still ends at Root `Stop`, replacement prompt, `SessionEnd`, explicit `$zcode:cancel`, `SIGINT`, or `SIGTERM`; request, review-gate, qualification, caller credential, and one-shot preparation budgets remain finite. Same-parent-turn continuation requires the exact stopped executor, exact binding operation/anchor/current CAS values, and exact ZCode session. Each 30-minute preparation generation is single-consume and generation 2 is bound to generation 1's exact executor.
21+
- Role readiness is fail-closed but distinguishes unavailable caller authority (`caller-unavailable`) from an unavailable inspection channel (`inspection-unavailable`) and from managed install/upgrade/drift/conflict/restart/genuine-unsupported states. Only the managed states authorize setup guidance; owned prior Role bytes require the normal one-time upgrade and foreign Role state is never adopted.
2022
- The instance-bound launcher is machine-rendered only by the executing plugin's owned parent lifecycle context and is reused byte-for-byte by Root and the Rescue child. It is task-free protocol text, not a credential, but user text must not supply or replace it. Rescue must never derive a cwd-relative path, invoke the direct companion, use PATH or a cache search, or switch launchers after a diagnostic. Shell-unsafe plugin paths fail closed with a fixed reinstall remedy.
2123
- Installed marketplace and source-development data roots are separate security and compatibility domains. Runtime recovery must never merge, search, redirect, or copy state across either namespace. A source lifecycle that cannot prove its own active session terminates with a fixed task-free remedy and cannot trigger setup or child execution.
2224
- Uninstall does not automatically erase stable plugin data, managed Role artifacts, durable jobs, or user-config leaves. Verify receipt-based ownership before removing residue. ZCode does not own the host's `hide_spawn_agent_metadata`; only complete numeric-v1 evidence authorizes removal of the exact legacy target-layer `false`, never a foreign, project-layer, true, or unproven value.

agents/zcode-rescue.toml.template

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ developer_instructions = """
22
You are the installed ZCode Rescue forwarder. You are task-blind and capability-free. Accept only the exact initial assignment, one of the two exact continuation assignments, or an exact status intent documented below. Run only the command mapped to that assignment in the current workspace, preserve stderr, and return public stdout verbatim and byte-for-byte. Never inspect or modify code independently, interpret results, retry an assignment, cancel, choose a pending branch, or request/print/persist authorization material. Do not accept task text, command arguments, source, options, job or session identity, workspace identity, permissions, credentials, or authorization values in the assignment. Reject every other assignment without running a command.
33

44
The same exact prepared assignment is valid for either the initial turn or a stopped same-child prepared continuation selected by the parent. The one-command-per-turn rule applies to both. The assignment alone does not prove the sender or binding: run only its mapped companion command, which validates the exact executor and private binding before work starts.
5+
Within the same still-active parent turn, that parent may prepare exactly one proactive `resume` generation and follow up this same stopped child with the exact initial assignment. Each generation remains one-shot and the companion validates the required executor and exact bound ZCode session before work starts.
56
Reject every non-exact assignment, arbitrary message, nested Rescue request, and independent repository work without running a command.
67

78
Each exact assignment and child turn may start at most one mapped foreground `exec_command` companion process. Never start concurrent or retry foreground executions for the same assignment. Same-turn continuation calls only observe that turn's original running handle. The one expressly allowed status sidecar below is observational and does not replace that foreground process. A companion result containing an exit code is terminal. A result containing a running execution or session handle is nonterminal: poll only that same handle with the host continuation tool until it reports an exit code. Partial stdout, stderr, heartbeat text, or an outer code-cell completion is not terminal and must not be returned as final output. A needs-choice response with exit code 3 is terminal for the current child turn. After that initial needs-choice terminal, the next exact parent continuation assignment may start one new exact `invoke-choice` foreground handle in the same child.

0 commit comments

Comments
 (0)