Product subagents expose minimal actionable diagnostics
产品 subagent 公开最小可行动诊断
The Claude Code and Codex providers receive structured failures from independently versioned product runtimes. Mirroring every member of those upstream error unions into model-visible diagnostics makes each runtime upgrade expand the Provider contract even when the parent agent would take the same next action for several categories. Raw SDK errors, app-server payloads, stderr, commands, paths, task content, environme
English
Problem
The Claude Code and Codex providers receive structured failures from independently versioned product runtimes. Mirroring every member of those upstream error unions into model-visible diagnostics makes each runtime upgrade expand the Provider contract even when the parent agent would take the same next action for several categories.
Raw SDK errors, app-server payloads, stderr, commands, paths, task content, environment values, and credentials cannot replace the structured mapping because they cross the product process boundary without a stable safety guarantee. The parent still needs enough information to distinguish a product limit, access restriction, service or transport problem, invalid result, and managed process failure.
Decision
Each product Provider derives a small action category from the current operation and safe structured product facts. The existing SubagentResult.diagnostic string remains the only public representation: consumers display it but do not parse it, and the shared subagent result boundary continues to enforce the complete 4096-byte UTF-8 limit.
Claude Code maps Agent SDK results into five categories:
| Category | Safe input |
|---|---|
limit | Turn, budget, or structured-output retry limits |
product-error | A general SDK execution failure |
invalid-result | An error-marked or blank success, or no terminal result |
process | The managed CLI exits before a terminal result |
unknown | Startup, teardown, unrecognized SDK values, or failures without a more specific safe fact |
The diagnostic also retains the derived query-start, query-run, process, or teardown stage and independently observed exit code and signal. A contributing permission decision follows the failure line. Successful completion and local cancellation expose no failure diagnostic, and original SDK text remains only on the internal cause chain and in Host observation.
Codex maps app-server failures into eight categories:
| Category | Safe input |
|---|---|
limit | Context, session-budget, or usage limits |
access-policy | Authentication, cyber-policy, product-policy, or sandbox failures |
service | Overload or internal service failures |
transport | HTTP and response-stream connection failures or exhausted attempts |
product-error | Invalid requests, rollback, active-turn, or other product failures |
invalid-result | A completed turn without a nonblank final answer |
process | The managed app-server exits before another terminal result |
unknown | Startup, teardown, malformed protocol values, or failures without a more specific safe fact |
The Codex diagnostic retains initialize, thread-start, turn-start, turn, process, or teardown, plus applicable numeric HTTP status and independently observed exit code and signal. contextWindowExceeded still maps the shared stop reason to max-tokens; every other category remains error. Only structured protocol facts contribute permission detail. Product stderr is Host-only observation and is neither classified nor copied into the result.
Ownership and lifecycle
| Fact or operation | Owner | Result |
|---|---|---|
| Product error interpretation | Official product runtime | The Provider consumes only structured facts exposed by its pinned integration |
| Action category and stage | One product Provider run | Derived at the failure site and discarded after result settlement |
| Exit code and signal | dsh-subprocess handle | Displayed independently when observed, without inferring missing values |
| Permission decision | Product Provider permission callbacks or protocol | Appended only when it contributed to the failed run |
| Diagnostic delivery and byte limit | dsh-subagent and its foreground or Job consumers | One bounded optional string remains separate from assistant output |
Verification
Claude Code package tests cover every coarse category, all four stages, unknown structured values, permission ordering, raw-text exclusion, success and cancellation omission, concurrent-run isolation, and independent exit code and signal fields. The real Agent SDK 0.3.241 and Claude Code 2.1.241 fixture produces an actual max-turns limit, process failure, permission denial, strict final answer, cancellation, and whole-tree quiescence. Codex package tests cover every coarse category, all six stages, applicable HTTP status, structured permission ordering, stderr exclusion, success and cancellation omission, concurrency, and cleanup aggregation. The real 0.149.1 app-server fixture produces service, product-error, process, final-answer, model-isolation, cancellation, and quiescence evidence. Loader and keyless product compositions continue to expose static tools without a diagnostic parser or model-visible category input.
Alternatives considered
Preserve every upstream enum member. Several members lead to the same parent action, while the display-only consumer gains no behavior from their exact names. Keeping them would make routine product upgrades redefine a larger Harness-facing promise.
Return raw product text or classify general stderr. Free-form text can contain sensitive task and environment data and has no stable versioned meaning. Only structured product facts, Provider stage, permission decisions, and managed process outcomes qualify as input.
Add a shared structured error type. The products version their failures independently, and current consumers only present a bounded string. A shared enum would move product release churn into the provider-neutral seam without a consumer that needs it.
Retry or fall back based on the category. The Provider owns one one-shot run and has no retry authority, fallback model, persistent product session, or recovery state. The category informs the parent; it does not start another product action.
Consequences
Product runtime upgrades no longer require a model-visible promise for every SDK or app-server error member. Parents still distinguish limits, access and policy restrictions, service and transport failures, general product failures, invalid results, managed process exits, and unknown failures while retaining applicable stage, permission, HTTP, and process facts.
The diagnostic remains safe display text rather than a recovery protocol. This change adds no raw error forwarding, fallback model, automatic retry, product session persistence, public structured result field, or dynamic provider and model selection.
中文
Problem
Claude Code 与 Codex 提供方会从彼此独立版本化的产品运行时接收结构化失败。如果把这些上游错误联合的每个成员都镜像进模型可见诊断,那么即使多个类别会让父 agent 采取同一种下一步动作,每次运行时升级仍会扩大提供方约定。
原始 SDK 错误、app-server payload、stderr、命令、路径、任务内容、环境值与凭证都不能取代结构化映射,因为这些内容跨越产品进程边界时没有稳定的安全保证。父 agent 仍需要足够信息来区分产品限制、访问限制、服务或传输问题、无效结果与受管进程失败。
Decision
每个产品提供方都会根据当前操作与安全的结构化产品事实派生少量行动类别。现有 SubagentResult.diagnostic 字符串仍是唯一公共表示:消费方只展示而不解析,共享 subagent 结果边界继续对完整文本执行 4096 字节 UTF-8 上限。
Claude Code 会把 Agent SDK 结果映射为五种类别:
| 类别 | 安全输入 |
|---|---|
limit | 轮次、预算或结构化输出重试限制 |
product-error | 一般 SDK 执行失败 |
invalid-result | 标记为错误或内容空白的成功结果,或缺失终态结果 |
process | 受管 CLI 在终态结果前退出 |
unknown | 启动、清理、无法识别的 SDK 值,或没有更具体安全事实的失败 |
诊断还会保留派生的 query-start、query-run、process 或 teardown 阶段,以及分别观测到的退出码与信号。参与失败的权限决定会跟在失败行之后。成功完成与本地取消不公开失败诊断,原始 SDK 文本只留在内部 cause 链和 Host 观测中。
Codex 会把 app-server 失败映射为八种类别:
| 类别 | 安全输入 |
|---|---|
limit | 上下文、会话预算或用量限制 |
access-policy | 身份验证、网络安全策略、产品策略或 sandbox 失败 |
service | 过载或内部服务失败 |
transport | HTTP 与响应 stream 连接失败或尝试耗尽 |
product-error | 无效请求、回滚、活动轮次或其他产品失败 |
invalid-result | 已完成轮次没有非空白最终答案 |
process | 受管 app-server 在其他终态结果前退出 |
unknown | 启动、清理、格式错误的协议值,或没有更具体安全事实的失败 |
Codex 诊断会保留 initialize、thread-start、turn-start、turn、process 或 teardown,以及适用的数值 HTTP status 和分别观测到的退出码与信号。contextWindowExceeded 仍把共享终止原因映射为 max-tokens;其他所有类别仍使用 error。只有结构化协议事实会贡献权限说明。产品 stderr 只供 Host 观测,既不会被分类,也不会复制进结果。
所有权与生命周期
| 事实或操作 | 责任方 | 结果 |
|---|---|---|
| 产品错误解释 | 官方产品运行时 | 提供方只消费其锁定集成公开的结构化事实 |
| 行动类别与阶段 | 单次产品提供方运行 | 在失败位置派生,并在结果结算后丢弃 |
| 退出码与信号 | dsh-subprocess 句柄 | 观测到时分别展示,不推断缺失值 |
| 权限决定 | 产品提供方权限回调或协议 | 只在参与失败运行时追加 |
| 诊断传递与字节上限 | dsh-subagent 及其前台或 Job 消费方 | 一个有界可选字符串继续与 assistant 输出分离 |
Verification
Claude Code 包测试覆盖全部粗粒度类别、四个阶段、未知结构化值、权限顺序、原始文本排除、成功与取消时省略、并发运行隔离,以及彼此独立的退出码与信号字段。真实 Agent SDK 0.3.241 与 Claude Code 2.1.241 fixture 会产生实际 max-turns 限制、进程失败、权限拒绝、严格最终答案、取消与整棵进程树完全停稳。Codex 包测试覆盖全部粗粒度类别、六个阶段、适用 HTTP status、结构化权限顺序、stderr 排除、成功与取消时省略、并发及清理聚合。真实 0.149.1 app-server fixture 会产生服务、产品错误、进程、最终答案、模型隔离、取消与完全停稳证据。Loader 与无密钥产品组合继续公开静态工具,不增加诊断解析器或模型可见类别输入。
Alternatives considered
保留每个上游枚举成员。 多个成员会触发同一种父级动作,而只负责展示的消费方无法从准确名称获得额外行为。继续保留会让常规产品升级重新定义更大的 Harness 可见承诺。
返回原始产品文本或分类一般 stderr。 自由文本可能包含敏感任务与环境数据,也没有稳定的版本化含义。只有结构化产品事实、提供方阶段、权限决定与受管进程结果可以成为输入。
增加共享结构化错误类型。 两个产品独立版本化失败,而当前消费方只呈现有界字符串。共享枚举会在没有消费需求的情况下,把产品发布变动移入提供方无关的 seam。
根据类别重试或 fallback。 提供方只拥有一次 one-shot 运行,没有重试权限、fallback 模型、持久产品会话或恢复状态。类别只用于通知父级,不会启动另一项产品动作。
Consequences
产品运行时升级不再要求为每个 SDK 或 app-server 错误成员提供模型可见承诺。父级仍可区分限制、访问与策略限制、服务与传输失败、一般产品失败、无效结果、受管进程退出与未知失败,并保留适用的阶段、权限、HTTP 与进程事实。
诊断仍是安全展示文本,而不是恢复协议。本改动不增加原始错误转发、fallback 模型、自动重试、产品会话持久化、公共结构化结果字段或动态提供方与模型选择。