fix(agent-loop): park empty turn batches, keep max-tokens sticky, and settle headless at idle

- turn boundary now returns false for an empty admitted batch (claimed
  input removed before the wake) instead of opening a turn and spending a
  model call on nothing; the step boundary already had the symmetric guard.
- a max-token step stays sticky when steering or injected work continues
  the turn: a later completed step no longer downgrades the outcome,
  matching the TurnEndReasonMap contract.
- session/queue wire schema accepts the context placement (previously the
  zod union rejected injected-context snapshots wholesale and the client
  silently dropped the whole frame); schema tests cover all placements.
- headless runs settle at whole-agent idle instead of the first turn/end,
  honoring the one-shot idle-to-idle contract.
- flush JSDoc names the real callers (checkpoint policy, goal-session,
  teardown, self-flushing consumers); apiproxy zh README loses its stale
  duplicate history section; ACP note/README record the delivered error
  rejection and turnless-cancelled behaviors.
This commit is contained in:
_Kerman
2026-08-03 20:36:26 +08:00
parent 81ac6db785
commit ab56e0df0a
15 changed files with 101 additions and 50 deletions
+2 -2
View File
@@ -24,7 +24,7 @@ Both fields are optional so another agent/request listener may supply the target
| `initialize` | Negotiates the supported version and advertises baseline-only prompts (no image, audio, or embedded-context capability). No session, editor, terminal, filesystem, or MCP capability is advertised. |
| `authenticate` | No-op because the server advertises no authentication methods. |
| `session/new` | Creates a fresh agent with an absolute primary `cwd`; empty `additionalDirectories` and `mcpServers` are accepted, non-empty values reject. |
| `session/prompt` | Concatenates text blocks, renders baseline resource links as bracketed textual references, rejects empty or beyond-baseline input, permits one in-flight request per session, and waits for the whole agent to become idle. Normal quiescence reports `end_turn`; explicit ACP cancellation or disposal reports `cancelled`. |
| `session/prompt` | Concatenates text blocks, renders baseline resource links as bracketed textual references, rejects empty or beyond-baseline input, permits one in-flight request per session, and waits for the whole agent to become idle. Normal quiescence reports `end_turn`; explicit ACP cancellation, disposal, or a prompt whose admission was discarded (a turnless slot) reports `cancelled`. |
| `session/cancel` | Cancels only the addressed agent and settles its pending prompt as `cancelled`; unknown ids are no-ops. |
| `session/update` | Emits one `agent_message_chunk` per non-empty text block in a committed `assistant/message`. Raw deltas and non-message events are omitted. |
| `session/request_permission` | Offers one-shot allow/reject choices for bridge-owned approval requests carrying a tool call id. Clients may answer automatically. |
@@ -37,7 +37,7 @@ Committed-message output intentionally trades token-by-token latency for a clean
Client disconnect and Cordis disposal share one memoized teardown. The bridge first rejects new sessions and prompts, settles pending prompts, then drains continuable descendants only below this connection's exact owned Agents before disposing those handles in parallel and awaiting every result before reporting any failure. Other frontends sharing the Context retain their continuable forests and admission. An ACP-only plugin reload therefore leaves no orphan agent.
ACP requires each prompt response to carry a `stopReason`, but the bridge does not claim a prompt-specific turn outcome. Committed assistant messages stream across the owned activity, and steering or injected work may contribute before idle. Token-limit and model-error turn endings therefore do not become prompt-level ACP stop reasons.
ACP requires each prompt response to carry a `stopReason`, but the bridge does not claim a prompt-specific turn outcome. Committed assistant messages stream across the owned activity, and steering or injected work may contribute before idle. Token-limit turn endings therefore do not become prompt-level ACP stop reasons (they settle as `end_turn`); a model error on the correlated turn rejects the prompt immediately.
## Running