fix(agent): correct session-prefix composition-order docs; scrub messagePrefix in snapshots

Two ds-review-bot findings:

The seam JSDoc claimed extending 'await next()' composes in
registration order — false for the append form: the waterfall unwinds
innermost-first, so appending places later-registered contributions
first. The canonical contribution is now documented as the PREPEND
'[mine, ...await next()]' (registration order on the wire), with the
append form's reverse-order behavior stated explicitly; the ordering
test now uses the canonical pattern in both listeners.

scrubRequestHeaders tokenized only system/tools, so a fixture recording
a composed session prefix would carry its raw text (workspace-specific
churn/leak). The scrubber now maps each header/delta messagePrefix
entry to a {{messagePrefix}} token — count stays a structural fact,
absence stays absent, the empty-array transition stays visible — with
normalize.spec coverage for the header, delta, absence, and odd-shape
paths.
This commit is contained in:
Yichen Jiang
2026-07-08 16:16:08 +08:00
parent b04cfe3a41
commit 77333c0a19
6 changed files with 76 additions and 24 deletions

View File

@@ -47,7 +47,7 @@ A step or turn errored. The loop reports a failure here (plus the logger) even w
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:455`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:460`](../../packages/core/agent/src/types.ts)
### `agent/pre-step` — serial
@@ -105,7 +105,7 @@ Waterfall: compose the SESSION PREFIX — request-only messages placed in front
This is the home for session-stable openers the model must always see but that must NOT become durable history — a skills catalog, an AGENTS.md digest, a workspace baseline: `Session.deriveMessages()` never returns the prefix, and the header events are its only durable record, so the request stays reconstructable from the log. Content that CHANGES mid-session belongs in the append-only history channels instead — `agent.inject()`, a `tools/post-execute` decision's `additionalContext`, prompt-submit `additionalContext` — each a durable `context/message` paid once and prefix-cached thereafter.
The seed is a frozen empty list; a contributing listener returns a NEW array extending `await next()` (`[...prefix, mine]` — never an in-place push), so contributions compose across plugins in registration order and compose deterministically for a fixed plugin set. Call `next()` to delegate, or return a list without it to short-circuit.
The seed is a frozen empty list; a contributing listener returns a NEW array — never an in-place push. The canonical contribution is a PREPEND, `[mine, ...await next()]`: the waterfall unwinds innermost-first (the LAST-registered listener's `next()` resolves first), so prepending yields registration order on the wire, and every plugin using it composes deterministically. The append form `[...await next(), mine]` is legal but places a contribution AFTER every later-registered plugin's — reverse registration order when all contributors append. Call `next()` to delegate, or return a list without it to short-circuit.
```ts cordis-catalog
'agent/session-prefix'(agent: Agent, prefix: Message[], signal: AbortSignal, next: () => Promise<Message[]>): Promise<Message[]>
@@ -113,7 +113,7 @@ The seed is a frozen empty list; a contributing listener returns a NEW array ext
Types: [Agent](../core-data-structures/core.md) · [Message](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:420`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:425`](../../packages/core/agent/src/types.ts)
### `agent/session-start` — emit
@@ -149,7 +149,7 @@ Waterfall: post-process the assembled assistant Message before tool dispatch (va
Types: [Agent](../core-data-structures/core.md) · [Message](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:430`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:435`](../../packages/core/agent/src/types.ts)
### `agent/turn-continuation` — waterfall
@@ -161,7 +161,7 @@ Waterfall: override the turn-continuation decision via a typed ContinuationDecis
Types: [Agent](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:443`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:448`](../../packages/core/agent/src/types.ts)
## `fs/*`

View File

@@ -9,16 +9,16 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| --- | --- | --- | --- | --- |
| `agent/created` | `emit` | [`packages/core/agent/src/types.ts:265`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`emit`) | [`stdio-agent`](../packages/ui/stdio-agent) |
| `agent/disposed` | `emit` | [`packages/core/agent/src/types.ts:272`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`emit`) | [`stdio-agent`](../packages/ui/stdio-agent) |
| `agent/error` | `emit` | [`packages/core/agent/src/types.ts:455`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | - |
| `agent/error` | `emit` | [`packages/core/agent/src/types.ts:460`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | - |
| `agent/pre-step` | `serial` | [`packages/core/agent/src/types.ts:350`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`compact-basic`](../packages/compact/compact-basic) |
| `agent/prompt-submit` | `waterfall` | [`packages/core/agent/src/types.ts:363`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) |
| `agent/queued` | `emit` | [`packages/core/agent/src/types.ts:290`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | - |
| `agent/request` | `waterfall` | [`packages/core/agent/src/types.ts:387`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | - |
| `agent/session-prefix` | `waterfall` | [`packages/core/agent/src/types.ts:420`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | - |
| `agent/session-prefix` | `waterfall` | [`packages/core/agent/src/types.ts:425`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | - |
| `agent/session-start` | `emit` | [`packages/core/agent/src/types.ts:305`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) |
| `agent/status` | `emit` | [`packages/core/agent/src/types.ts:281`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/ui/acp), [`invariants`](../packages/support/invariants), [`stdio-agent`](../packages/ui/stdio-agent) |
| `agent/step-result` | `waterfall` | [`packages/core/agent/src/types.ts:430`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | - |
| `agent/turn-continuation` | `waterfall` | [`packages/core/agent/src/types.ts:443`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) |
| `agent/step-result` | `waterfall` | [`packages/core/agent/src/types.ts:435`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | - |
| `agent/turn-continuation` | `waterfall` | [`packages/core/agent/src/types.ts:448`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) |
| `fs/edit-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:123`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`) | [`fs-policy`](../packages/fs/fs-policy) |
| `fs/observed` | `emit` | [`packages/fs/fs/src/index.ts:138`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`emit`) | [`fs-policy`](../packages/fs/fs-policy) |
| `fs/write-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:109`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`) | [`fs-policy`](../packages/fs/fs-policy) |