Merge commit 'f1a9ab14fe882f9b855ea9cc3f16ddc9a3b0284d' into codex/subprocess-process-exit-cleanup-v2
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-07-20-error-cause-chain-diagnostics.md
|
||||
2026-07-20-error-cause-chain-diagnostics.md: b80dd08d79a57738a6eef2f8638b0336ca80d4bf
|
||||
2026-07-20-error-cause-chain-diagnostics.md: d1944b3b435f2e1c64e1612adf0e61dd8e7401ff
|
||||
2026-07-20-error-cause-chain-diagnostics.zh.md: 914e983d7ea81eef8bca8fd5aa3791f2f44d4080
|
||||
|
||||
@@ -13,7 +13,7 @@ A TUI run against an unreachable DeepSeek endpoint failed with the single notice
|
||||
|
||||
## Decision
|
||||
|
||||
- `dsh-llm` exports `errorChain(value)`: renders a thrown value with its full `cause` chain (`outer: inner: …`) and AggregateError members (`msg [m1; m2]`), with circular-cause and hostile-coercion containment. It is a diagnostic-surface renderer only; routing stays on `HarnessError.code`.
|
||||
- `dsh-llm` exports `errorChain(value)`: renders a thrown value with its full `cause` chain (`outer: inner: …`) and AggregateError members (`msg [m1; m2]`), with circular-cause and hostile-coercion containment. It is a diagnostic-output renderer only; routing stays on `HarnessError.code`.
|
||||
- The DeepSeek adapter wraps a pre-response transport failure in `LlmError('TRANSPORT')` naming the configured `baseURL` and chaining the original rejection as `cause`. An aborted request becomes `LlmError('ABORTED')`; because the turn signal is already aborted, the loop still classifies the turn as cancellation rather than recovery.
|
||||
- Every diagnostic boundary renders through `errorChain` instead of `error.message`/`String(error)`: the agent-loop's durable `turn/end` error message (`errorData`), its logger warnings, the TUI's `agent/error` notice and startup-failure line, and `dsh-stdio`'s startup-failure log lines. The live `agent/error` event and `SettleReason` preserve the thrown value as `unknown`; each diagnostic Consumer renders it instead of the loop wrapping it into another error. The per-package `renderThrown` copies in `dsh-agent-loop`, `dsh-stdio`, and `dsh-tui` are deleted in favor of the one shared renderer.
|
||||
- `dsh-stdio` renders failure `turn/end` reasons: `[turn failed <code>] <message>`, `[turn aborted] <reason>`, `[turn rejected] <reason>`, `[turn interrupted by a previous process exit]`, and the output-token-limit notice. Unknown merge-extended kinds fall through as ordinary turn ends.
|
||||
@@ -24,7 +24,7 @@ A TUI run against an unreachable DeepSeek endpoint failed with the single notice
|
||||
|
||||
**Chain rendering inside each error's constructor (bake the cause into `message`).** Rejected: it double-renders once consumers also walk `cause` (the first draft of the adapter fix produced `… fetch failed: bad port: fetch failed: bad port`), and it destroys the structured chain for consumers that want to route on the inner error.
|
||||
|
||||
**A `cause`-aware logger exporter only.** Rejected: the durable `turn/end` reason and the TUI notice are not logger lines; the masked message would persist in the session log — the single durable record of an in-turn failure — and in the primary UI surface.
|
||||
**A `cause`-aware logger exporter only.** Rejected: the durable `turn/end` reason and the TUI notice are not logger lines; the masked message would persist in the session log — the single durable record of an in-turn failure — and in the primary UI.
|
||||
|
||||
**Per-package `renderThrown` upgrades.** Rejected: three packages already carried near-identical private copies; upgrading each separately entrenches the duplication the shared renderer removes.
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-07-24-empty-model-response-is-retryable.md
|
||||
2026-07-24-empty-model-response-is-retryable.md: 3ecb106fc3a53070f66d1de351120aaf99d4d0de
|
||||
2026-07-24-empty-model-response-is-retryable.md: 16e4a79ec64a1abb5489690f5e064ccb104ec5bc
|
||||
2026-07-24-empty-model-response-is-retryable.zh.md: 2573ef66d9ca1e6c0610686d0051c738774af588
|
||||
|
||||
@@ -31,6 +31,6 @@ The classification uses the existing loop machinery — `finishError` → `agent
|
||||
|
||||
## Consequences
|
||||
|
||||
- A transiently misbehaving provider consumes a bounded retry instead of a turn with no output; a persistently empty model surfaces an actionable `EMPTY_RESPONSE` turn failure.
|
||||
- A transiently misbehaving provider consumes a bounded retry instead of a turn with no output; a persistently empty model produces an actionable `EMPTY_RESPONSE` turn failure.
|
||||
- A model that genuinely intends to say nothing (rare, but possible after a tool result) is retried and, if consistently empty, fails the turn. This trade was accepted deliberately: an empty assistant message is indistinguishable from the provider defect and has no value to the user.
|
||||
- The `empty-response-retry` ACP snapshot (an authored keyless scenario with a deterministic 1 ms zero-jitter retry overlay, `examples/acp-agent/retry.cordis.yml`) pins the product-visible behavior: a durable `llm/retry` event, no ACP output for the discarded attempt, the recovered reply, and a clean completed turn.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-08-06-list-agents-residency-vocabulary.md
|
||||
2026-08-06-list-agents-residency-vocabulary.md: 01fff958921465909f5cd300dc355fdac6ec0772
|
||||
2026-08-06-list-agents-residency-vocabulary.zh.md: d68b9857de23ccb0b68f0bfafba153fde63303a7
|
||||
@@ -0,0 +1,40 @@
|
||||
# Agent Note: `list_agents` uses `ready` for resumable children
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-08-06-list-agents-residency-vocabulary.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
`list_agents` projected a continuable child's process residency as `running | idle | complete`. `complete` reads as a terminal unit of work with a result somewhere, but the underlying fact says only that no Activation is resident: the conversation is intact, `send_message` can continue it, and nothing about the child's outcome is being claimed. A model that reads `complete` reasonably looks for a result to collect or sends replacement work to a conversation it believes has ended.
|
||||
|
||||
The word is especially misleading alongside [manager-owned settlement delivery](../feature/2026-08-06-manager-owned-subagent-settlement-delivery.md). Completion reaches the parent as a notice; listing exists to recall durable conversations, not to poll for that notice.
|
||||
|
||||
## Decision
|
||||
|
||||
The model-facing projection reports `running | idle | ready`:
|
||||
|
||||
- **`running`** means the resident Agent has an active driver.
|
||||
- **`idle`** means the Agent is resident between turns and may be waiting on agents it started.
|
||||
- **`ready`** means only the durable conversation remains. `send_message` starts the next turn on the same conversation; the status is resumable rather than terminal and does not mean a result is waiting to be collected.
|
||||
|
||||
The tool description states those distinctions and directs the model away from polling: it says the parent is told when a child finishes and that listing is for recalling which children it started. `send_message` remains the authoritative delivery check because either snapshot may race another process or a later message.
|
||||
|
||||
The service layer is unchanged. `SubagentListEntry.activity` retains `'running' | 'inactive'`, which accurately describes corpus residency for consumers such as a UI. The model-facing adapter maps `inactive` to `ready` because that word communicates the action available to the model without inventing an outcome.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Keep `complete` and qualify it in the description.** A description saying that `complete` does not mean complete fights the rendered status on every read. The line the model scans must carry the correct distinction itself.
|
||||
|
||||
**Use `active | dormant`.** This removes the useful distinction between a resident Agent that is between turns and a storage-only conversation, and makes the storage-only state sound unavailable. `ready` states the useful fact: the same conversation accepts another turn.
|
||||
|
||||
**Drop the status entirely.** Residency remains useful when a parent decides whether to send more work. Removing it trades one misleading status for no signal.
|
||||
|
||||
**Rename the service activity values.** `running | inactive` is correct at the service layer and has non-model consumers. Renaming it would churn a general contract to fix one adapter's presentation; the [durable catalog note](../feature/2026-07-22-durable-subagent-catalog-and-list-agents.md) continues to own that service vocabulary.
|
||||
|
||||
## Consequences
|
||||
|
||||
- The rendered line uses `<id> [running] — <label>`, `<id> [idle] — <label>`, or `<id> [ready] — <label>`.
|
||||
- The output schema's `status` enum changes with the rendered contract. The generated tool catalog picks up the new description; it renders each tool's `parameters` only and never carried the output schema.
|
||||
- Unit coverage pins all three mappings and the description clauses that direct the model to the settlement notice instead of polling this tool.
|
||||
- The assembled ACP `subagent-list-agents` scenario renders `ready` for a settled, resumable child.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Agent Note: `list_agents` uses `ready` for resumable children
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-08-06-list-agents-residency-vocabulary.md) | 中文
|
||||
|
||||
## Problem
|
||||
|
||||
`list_agents` 把可继续 child 的进程驻留状态投影为 `running | idle | complete`。`complete` 读起来像一项终态工作,且结果就在某处,但底层事实只表示没有驻留的 Activation:对话完好无损,`send_message` 可以继续它,而且它对 child 的结果不作任何断言。读到 `complete` 的模型会合理地寻找可收集结果,或向一个它以为已经结束的对话发送替代工作。
|
||||
|
||||
这个词与[由管理器负责的结算投递](../feature/2026-08-06-manager-owned-subagent-settlement-delivery.md)同时出现时尤其容易误导。完成会以通知到达 parent;列表用于回忆持久化对话,而不是轮询该通知。
|
||||
|
||||
## Decision
|
||||
|
||||
面向模型的投影报告 `running | idle | ready`:
|
||||
|
||||
- **`running`** 表示驻留 Agent 存在活跃 driver。
|
||||
- **`idle`** 表示 Agent 驻留但处于轮次之间,也可能正在等待它启动的 agent。
|
||||
- **`ready`** 表示只剩下持久化对话。`send_message` 会在同一对话上启动下一轮;该状态表示可恢复而非终态,也不表示有结果等待收集。
|
||||
|
||||
工具描述会陈述这些区别,并引导模型远离轮询:它说明 child 结束时 parent 会收到通知,而列表用于回忆自己启动过哪些 child。由于任一快照都可能与另一进程或后续消息竞态,`send_message` 仍是投递时的权威检查。
|
||||
|
||||
服务层不变。`SubagentListEntry.activity` 保留 `'running' | 'inactive'`,对 UI 等消费方而言,这准确描述了语料驻留状态。面向模型的适配器把 `inactive` 映射为 `ready`,因为这个词表达了模型可执行的操作,而没有虚构结果。
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**保留 `complete`,并在描述中限定它。** 一段解释 `complete` 不代表完成的描述,每次被读取时都在与渲染状态对抗。模型扫读的那一行必须自身表达正确区别。
|
||||
|
||||
**使用 `active | dormant`。** 这会删除处于轮次之间的驻留 Agent 与仅存于存储的对话之间的有效区别,并让仅存于存储的状态听起来不可用。`ready` 直接表达有用事实:同一对话可以接受下一轮。
|
||||
|
||||
**完全移除状态。** parent 决定是否发送更多工作时,驻留状态依然有用。移除它只是用没有信号替代一个误导性状态。
|
||||
|
||||
**重命名服务活动值。** `running | inactive` 在服务层是正确的,并且有非模型消费方。为了修复一个适配器的呈现而搅动通用契约并不合理;[持久化目录 Agent Note](../feature/2026-07-22-durable-subagent-catalog-and-list-agents.md) 继续拥有该服务词汇。
|
||||
|
||||
## Consequences
|
||||
|
||||
- 渲染行使用 `<id> [running] — <label>`、`<id> [idle] — <label>` 或 `<id> [ready] — <label>`。
|
||||
- 输出 schema 的 `status` 枚举与渲染契约一同变化。生成的工具目录会带上新描述;它只渲染每个工具的 `parameters`,从来不收录输出 schema。
|
||||
- 单元覆盖固定三种映射,以及引导模型等待结算通知而非轮询本工具的描述条款。
|
||||
- 整体组装的 ACP `subagent-list-agents` 场景会为已结算且可恢复的 child 渲染 `ready`。
|
||||
Reference in New Issue
Block a user