@sema-agent/core 7.6.0 → 7.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/dist/agents/agent-transcript-tool.d.ts +2 -2
- package/dist/agents/cascade.d.ts +2 -3
- package/dist/agents/repair-loop.d.ts +2 -2
- package/dist/agents/retain-ledger.d.ts +2 -3
- package/dist/agents/send-message-tool.d.ts +2 -2
- package/dist/agents/session-util.d.ts +2 -2
- package/dist/agents/subagent.d.ts +3 -4
- package/dist/agents/teacher.d.ts +2 -2
- package/dist/agents/team.d.ts +2 -2
- package/dist/agents/verify.d.ts +5 -6
- package/dist/core/agent-definition.d.ts +172 -0
- package/dist/core/agent-definition.js +1 -0
- package/dist/core/checkpoint-store.d.ts +8 -4
- package/dist/core/delegation-frames.d.ts +298 -0
- package/dist/core/delegation-frames.js +21 -0
- package/dist/core/engine-notice.d.ts +555 -0
- package/dist/core/engine-notice.js +55 -0
- package/dist/core/gate-fold.d.ts +12 -0
- package/dist/core/gate-fold.js +158 -0
- package/dist/core/gate-lanes.d.ts +93 -0
- package/dist/core/gate-lanes.js +626 -0
- package/dist/core/hands-band.d.ts +134 -0
- package/dist/core/hands-band.js +1 -0
- package/dist/core/hooks.d.ts +20 -101
- package/dist/core/hooks.js +53 -854
- package/dist/core/mcp-failure.d.ts +43 -5
- package/dist/core/mcp-failure.js +31 -14
- package/dist/core/mcp-server-spec.d.ts +217 -0
- package/dist/core/mcp-server-spec.js +1 -0
- package/dist/core/model-seat.d.ts +99 -0
- package/dist/core/model-seat.js +1 -0
- package/dist/core/reminder-mint.d.ts +10 -0
- package/dist/core/reminder-mint.js +3 -0
- package/dist/core/runner/contracts.d.ts +382 -6
- package/dist/core/runner/gate-exit.d.ts +177 -9
- package/dist/core/runner/gate-exit.js +70 -1
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +2 -7
- package/dist/core/runner/prepare-delegation-surface.d.ts +2 -7
- package/dist/core/runner/prepare-run-refs.d.ts +12 -0
- package/dist/core/runner/prepare-run-refs.js +5 -0
- package/dist/core/runner/prepare-task.d.ts +2 -2
- package/dist/core/runner/runtask.d.ts +4 -71
- package/dist/core/runner/runtask.js +18 -6
- package/dist/core/runner-deps.d.ts +1416 -0
- package/dist/core/runner-deps.js +1 -0
- package/dist/core/runtime-caps.d.ts +164 -0
- package/dist/core/runtime-caps.js +1 -0
- package/dist/core/task-event.d.ts +910 -0
- package/dist/core/task-event.js +1 -0
- package/dist/core/task-limits.d.ts +110 -0
- package/dist/core/task-limits.js +1 -0
- package/dist/core/task-result.d.ts +809 -0
- package/dist/core/task-result.js +1 -0
- package/dist/core/task-spec.d.ts +1370 -0
- package/dist/core/task-spec.js +1 -0
- package/dist/core/task-stream.d.ts +382 -0
- package/dist/core/task-stream.js +1 -0
- package/dist/core/tool-spec.d.ts +1174 -0
- package/dist/core/tool-spec.js +1 -0
- package/dist/core/types.d.ts +26 -7691
- package/dist/core/types.js +2 -76
- package/dist/core/warm-resume.d.ts +2 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/orchestration/goal.d.ts +2 -2
- package/dist/orchestration/run-spec.d.ts +2 -2
- package/dist/orchestration/run-workflow-tool.d.ts +3 -3
- package/dist/orchestration/workflow.d.ts +4 -4
- package/dist/scenarios/scenario-registry.d.ts +3 -3
- package/dist/scenarios/teacher-quickstart.d.ts +2 -2
- package/dist/server/http.d.ts +2 -2
- package/dist/stores/file/fs-atomic.d.ts +88 -12
- package/dist/stores/file/fs-atomic.js +184 -55
- package/dist/stores/file/index.d.ts +1 -0
- package/dist/stores/file/index.js +1 -0
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +9 -1
|
@@ -0,0 +1,910 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The WIRE frames: the `TaskEvent` union every consumer switches on, and every satellite it
|
|
3
|
+
* discriminates with — the per-message identity envelope, the brain-status phases and retry classes,
|
|
4
|
+
* the tool activity beat, the human-input source/delivery words, the delegation task type, the
|
|
5
|
+
* human-input projection and the compaction outcome. This is the file a change to a frame touches, and
|
|
6
|
+
* nothing else. Layer 0 vocabulary; `types.ts` re-exports every name below, so no consumer's import
|
|
7
|
+
* changes.
|
|
8
|
+
*/
|
|
9
|
+
import type { ActorAssertion } from "../internal/llm.js";
|
|
10
|
+
import type { TaskResult } from "./task-result.js";
|
|
11
|
+
import type { TaskNotificationPayload } from "./task-notification.js";
|
|
12
|
+
/**
|
|
13
|
+
* Identity fields carried by every CONTENT event of a task stream (design/99 §E2 — lightweight message
|
|
14
|
+
* identity). Both are OPTIONAL and ADDITIVE; a consumer that ignores them is unchanged.
|
|
15
|
+
*
|
|
16
|
+
* - `eventId` — a core-minted, time-sortable (uuidv7) unique id stamped on each emitted content event. Gives
|
|
17
|
+
* a live UI a stable handle to correlate/dedup streamed events and to map its own client-side message ids
|
|
18
|
+
* back to a core anchor. (The durable-anchor mapping for resume-at — which eventIds are restartable
|
|
19
|
+
* transcript positions — is finalized by the E18 `resumeAt` seam; this field is the stable handle it builds on.)
|
|
20
|
+
* - `parentToolCallId` — present ONLY when this task runs as a sub-agent spawned under a parent task's tool
|
|
21
|
+
* call (threaded via the trusted `RunInternals.parentToolCallId`, sourced from the spawning tool's
|
|
22
|
+
* `ToolExecuteContext.toolCallId`). Lets a consumer attribute streamed content to the delegation subtree it
|
|
23
|
+
* belongs to WITHOUT core physically merging the child stream into the parent (the child's RESULT still
|
|
24
|
+
* re-enters the parent MODEL context only through the `delimitUntrusted` fence — observability threading and
|
|
25
|
+
* model-context isolation are kept separate, so prompt-cache and the injection boundary are untouched).
|
|
26
|
+
*/
|
|
27
|
+
export interface TaskEventIdentity {
|
|
28
|
+
eventId?: string;
|
|
29
|
+
parentToolCallId?: string;
|
|
30
|
+
/**
|
|
31
|
+
* design/122 S2 (the `WorkflowRun.sourceTaskId` family, NOT a second `taskId` — that
|
|
32
|
+
* field already exists on `task_progress` and a duplicate would bite consumers) — the ORIGINATING task's
|
|
33
|
+
* canonical id (`spec.taskId ?? sessionId`), stamped ONLY on the content events of a task running AS A
|
|
34
|
+
* SUB-AGENT (same condition as {@link parentToolCallId}). A consumer of the C1 forward channel
|
|
35
|
+
* (`forwardSubagentEvents`) can key a child's content events by the child task directly, without an
|
|
36
|
+
* alias table. A TOP-LEVEL task's own events carry NO `sourceTaskId` (absent = the consumer's current
|
|
37
|
+
* task). Additive; a consumer that ignores it is unchanged.
|
|
38
|
+
*/
|
|
39
|
+
sourceTaskId?: string;
|
|
40
|
+
/**
|
|
41
|
+
* design/151 S2 (MED-10 live-tail) — the BACKGROUND agent's `a*` registry
|
|
42
|
+
* handle, stamped on every event a background child's lane forwards up the host `onForwardEvent`
|
|
43
|
+
* channel (content deltas, tool events, progress). THE live-tail routing key: a serving layer
|
|
44
|
+
* pipes "what is agent aXX doing right now" straight off the host forward stream — no uuid→a*
|
|
45
|
+
* alias table (the BCE spawn frame's sessionId mapping) needed. Sync/steer children carry NO tag
|
|
46
|
+
* (no a* row exists); `wa*` workflow rows stay on their own observer lane. Additive; a consumer
|
|
47
|
+
* that ignores it is unchanged.
|
|
48
|
+
* NESTED bg-in-bg (server[1523] 对等复审 4a): each background boundary OVERWRITES the tag as the
|
|
49
|
+
* frame crosses it — on the HOST wire, the whole subtree's frames carry the OUTERMOST a* handle
|
|
50
|
+
* (the host-addressable row). Per-frame nesting attribution rides `parentToolCallId`, never this
|
|
51
|
+
* field; a consumer splitting an outer stream into per-descendant views keys on that.
|
|
52
|
+
*/
|
|
53
|
+
bgAgentId?: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* design/99 §E3/§E10 — a BRAIN-LAYER liveness phase, surfaced so a UI can show why a
|
|
57
|
+
* turn is stalled (the brain otherwise absorbs these silently in its connect/retry loop). The PHASE
|
|
58
|
+
* itself is provider-NEUTRAL — a graduated bucket this engine mints, never a provider taxonomy or a
|
|
59
|
+
* `stop_reason`. The one provider-stated number that does cross this channel is the failing attempt's
|
|
60
|
+
* HTTP status, and it travels on its own named seat ({@link BrainStatus.errorStatus}) in the retry
|
|
61
|
+
* context only; the phase and the {@link BrainStatus.detail} hint stay free of it.
|
|
62
|
+
* A CLOSED union (type-safe) covering the core brain layer; a deployment that surfaces its OWN states
|
|
63
|
+
* (e.g. E25 token-refresh `authenticating`) does so on its own channel, not by widening this.
|
|
64
|
+
*/
|
|
65
|
+
export type BrainStatusPhase = "rate_limited" | "retrying" | "reconnecting" | "circuit_open"
|
|
66
|
+
/**
|
|
67
|
+
* RB-420-c — TERMINAL frames closing a retry sequence. Without them a consumer that rendered a
|
|
68
|
+
* `rate_limited`/`retrying`/`reconnecting` frame has nothing telling it the wait is over, so the
|
|
69
|
+
* warning row stays pinned until the next unrelated event. Exactly one terminal frame is emitted,
|
|
70
|
+
* and only for a call that actually waited (a retry wait, or a `waiting_first_token` trail —
|
|
71
|
+
* #530): `recovered` = the call was served after the wait (a later attempt, or the first token
|
|
72
|
+
* finally arriving), `gave_up` = the retry budget ran out (or the call was cancelled) and a
|
|
73
|
+
* terminal error follows.
|
|
74
|
+
* Both mean the same thing to a renderer — stop showing the retry state.
|
|
75
|
+
*/
|
|
76
|
+
| "recovered" | "gave_up"
|
|
77
|
+
/**
|
|
78
|
+
* #530 — the first-token WAIT, before any failure: a first-token watchdog is armed
|
|
79
|
+
* (`firstTokenTimeoutMs > 0`) and nothing has arrived yet. Emitted once the wait has lasted
|
|
80
|
+
* `WAITING_FIRST_TOKEN_AFTER_MS` and re-emitted every `WAITING_FIRST_TOKEN_EVERY_MS` while it goes
|
|
81
|
+
* on, carrying {@link BrainStatus.elapsedMs} and {@link BrainStatus.timeoutMs}; never carries a
|
|
82
|
+
* retry countdown (`retryInMs`/`retryAtMs` — no wait is being announced, a wait is being
|
|
83
|
+
* OBSERVED). CLOSED like a retry trail: once a wait frame went out, the call's terminal frame
|
|
84
|
+
* follows — `recovered` when the call went on to be served (the first token arrived, on this
|
|
85
|
+
* attempt or a later one), `gave_up` when it failed for good — so a renderer that pinned the row
|
|
86
|
+
* has its "stop" signal. No frame at all when no watchdog is armed.
|
|
87
|
+
*/
|
|
88
|
+
| "waiting_first_token";
|
|
89
|
+
/**
|
|
90
|
+
* WHY a retry wait is happening, as a closed, provider-NEUTRAL bucket — the companion to
|
|
91
|
+
* {@link BrainStatusPhase}, which says what the brain is doing about it. A consumer rendering an
|
|
92
|
+
* unattended progress line ("no answer for three minutes") needs the reason, and until this existed the
|
|
93
|
+
* only carriers of it were the HTTP status and the syscall code, neither of which crossed this channel
|
|
94
|
+
* at the time. (The status has since gained its own seat, {@link BrainStatus.errorStatus}; this bucket
|
|
95
|
+
* remains the reason a consumer RENDERS, and it is the only carrier for the transport classes, which
|
|
96
|
+
* have no status at all.) Values are about the SHAPE of the failure, never its provider taxonomy:
|
|
97
|
+
* - `connect_refused` — the attempt got a definite negative about the target itself (nothing accepts
|
|
98
|
+
* at that address, or the name has no address). This is the class the SHORT retry lane serves.
|
|
99
|
+
* - `transport` — any other transport-level failure: a connect timeout, a reset, a mid-stream
|
|
100
|
+
* tear, a stalled stream. No verdict about the target; the full ladder applies.
|
|
101
|
+
* - `rate_limit` — the provider asked the caller to slow down.
|
|
102
|
+
* - `server` — the provider reported a failure on its own side.
|
|
103
|
+
* - `http` — a response the status predicate calls terminal, retried anyway because the
|
|
104
|
+
* provider's own explicit retry verdict said to.
|
|
105
|
+
* - `output_cap` — not a failure of the connection at all: the request is being re-sent with a
|
|
106
|
+
* lowered output cap after the provider reported the context limit exceeded (no backoff).
|
|
107
|
+
*/
|
|
108
|
+
export type BrainRetryErrClass = "connect_refused" | "transport" | "rate_limit" | "server" | "http" | "output_cap";
|
|
109
|
+
/** design/99 §E3/§E10 — the payload of a {@link TaskEvent} `status` event (and the brain→runner signal). */
|
|
110
|
+
export interface BrainStatus {
|
|
111
|
+
phase: BrainStatusPhase;
|
|
112
|
+
/** Optional neutral, human-readable hint. Stays free of provider/HTTP detail even now that
|
|
113
|
+
* {@link errorStatus} exists: the machine-readable status has its own seat, and interpolating it
|
|
114
|
+
* into the prose too would make one fact travel in two spellings a consumer has to reconcile.
|
|
115
|
+
* (CC parity: its own retry banner renders a TRANSLATED sentence — "No response from API",
|
|
116
|
+
* "Connection dropped" — while the raw status rides the structured key beside it.) */
|
|
117
|
+
detail?: string;
|
|
118
|
+
/** Seconds until the brain's next retry attempt (from the honored backoff / `Retry-After`), when known. */
|
|
119
|
+
retryInSec?: number;
|
|
120
|
+
/** RB-420-c — millisecond precision of {@link retryInSec}, for a client that renders a countdown.
|
|
121
|
+
* A wait longer than the re-announce slice is re-emitted with the REMAINING time, so a consumer
|
|
122
|
+
* that simply overwrites its row always shows a live number instead of a stale one. */
|
|
123
|
+
retryInMs?: number;
|
|
124
|
+
/**
|
|
125
|
+
* The WALL-CLOCK instant the announced wait is expected to end — `Date.now() + retryInMs`, read at
|
|
126
|
+
* the moment the frame is emitted, in the EMITTING core process's clock. Stamped centrally at the
|
|
127
|
+
* one emit point, so the invariant is total: present exactly when {@link retryInMs} is present, and
|
|
128
|
+
* absent on every frame that announces no wait (`recovered` / `gave_up`, and the immediate
|
|
129
|
+
* output-cap re-send, which takes no backoff).
|
|
130
|
+
*
|
|
131
|
+
* WHY IT IS PRODUCED HERE rather than derived by the consumer: `Date.now() + retryInMs` is only
|
|
132
|
+
* correct for a reader that received the frame instantly. A consumer several process hops away
|
|
133
|
+
* (serving layer → client) recomputes a deadline already shifted by the transit, so a countdown
|
|
134
|
+
* built that way restarts on every re-announced slice instead of converging. The producer is the
|
|
135
|
+
* only party that can state the instant, so it does.
|
|
136
|
+
*
|
|
137
|
+
* CLOCK DOMAIN, stated to remove the ambiguity: WALL CLOCK (`Date.now()`, epoch ms), NOT a
|
|
138
|
+
* monotonic reading. A consumer must not compare it against its own monotonic timers, and must
|
|
139
|
+
* treat it as approximate across a clock adjustment or a machine boundary — {@link retryInMs} stays
|
|
140
|
+
* the authoritative RELATIVE quantity, and this seat is the absolute convenience derived from it.
|
|
141
|
+
*/
|
|
142
|
+
retryAtMs?: number;
|
|
143
|
+
/** #530 — on a `waiting_first_token` frame: how long the first token has been awaited, ms, at the
|
|
144
|
+
* moment the frame is emitted (the emitting process's clock; approximate across a hop). Absent on
|
|
145
|
+
* every other phase. */
|
|
146
|
+
elapsedMs?: number;
|
|
147
|
+
/** #530 — on a `waiting_first_token` frame: the armed first-token watchdog, ms — the wait's ceiling,
|
|
148
|
+
* so a consumer can render "45s of 120s". Absent on every other phase. */
|
|
149
|
+
timeoutMs?: number;
|
|
150
|
+
/** RB-420-c — 1-based index of the attempt that just failed (the wait precedes attempt `attempt + 1`);
|
|
151
|
+
* same numbering as the `brain.retry` telemetry frame. Absent on frames that are not a retry wait. */
|
|
152
|
+
attempt?: number;
|
|
153
|
+
/** RB-420-c — the retry budget of THIS lane, so a consumer can render "attempt 3 of 10". Lane, not
|
|
154
|
+
* engine: a failure class served by a shorter ladder reports that ladder's budget, so the fraction a
|
|
155
|
+
* consumer renders is the one actually in force rather than the engine-wide ceiling. */
|
|
156
|
+
maxRetries?: number;
|
|
157
|
+
/** Why this wait is happening ({@link BrainRetryErrClass}). Present on retry-wait frames whose cause
|
|
158
|
+
* the engine classified; absent on frames that are not a retry wait (`recovered`/`gave_up`) and on a
|
|
159
|
+
* `circuit_open` fast-fail, which is a local verdict rather than an observed failure. */
|
|
160
|
+
errClass?: BrainRetryErrClass;
|
|
161
|
+
/**
|
|
162
|
+
* #506 ㋑ — the HTTP status of the attempt that just failed, on the frame announcing the RETRY of it.
|
|
163
|
+
* The one provider-stated number this channel carries; everything else about the failure still
|
|
164
|
+
* crosses as the engine's own graduated buckets ({@link phase} / {@link errClass}).
|
|
165
|
+
*
|
|
166
|
+
* WHY IT IS HERE AT ALL, given the channel spent its life refusing it: a UI that must tell an
|
|
167
|
+
* operator whether to wait or to go fix something needs the status, and the two neutral buckets
|
|
168
|
+
* cannot supply it — `rate_limit` covers a 429 the provider will clear on its own AND a 429 that
|
|
169
|
+
* means the account is out of quota, and `server` covers a 500 alongside a 503 behind a load
|
|
170
|
+
* balancer. CC states the same number on the same occasion (`system/api_retry.error_status`, read
|
|
171
|
+
* off `APIError.status`), so a serving layer forwarding this frame no longer has to choose between
|
|
172
|
+
* matching CC's payload and honoring this channel's contract.
|
|
173
|
+
*
|
|
174
|
+
* PRESENCE — an api_retry context whose attempt got a response that NAMES the failure, and nothing
|
|
175
|
+
* else. Present on the connect-ladder retry wait (`rate_limited` / `retrying` / `reconnecting` when
|
|
176
|
+
* a response came back) and on the output-cap re-send (the provider's own 400). ABSENT — never
|
|
177
|
+
* zeroed, never null — for:
|
|
178
|
+
* · a transport failure (`connect_refused` / `transport` at the connect leg): no response existed,
|
|
179
|
+
* so no status was ever stated. This is the arm CC spells as an explicit `null`; here it is
|
|
180
|
+
* absence, for the same reason every other seat on this interface uses absence, and the fact is
|
|
181
|
+
* still TOTAL rather than guessable — {@link errClass} names those two classes by construction.
|
|
182
|
+
* · a mid-stream tear (tiers B/C): its response's own status was a SUCCESS, and a status that
|
|
183
|
+
* failed nothing must not be published as the cause of a retry.
|
|
184
|
+
* · a `circuit_open` fast-fail: a local verdict, nothing was sent, nobody answered.
|
|
185
|
+
* · the terminal `recovered` / `gave_up` frames: they announce no attempt.
|
|
186
|
+
* Same "only a status that names the failure travels" predicate as the assistant frame's
|
|
187
|
+
* {@link AssistantMessage.apiErrorStatus} (one helper, both sites) — a 2xx never travels here either,
|
|
188
|
+
* which is reachable on this path via a provider's own `x-should-retry` verdict on a success status.
|
|
189
|
+
*
|
|
190
|
+
* OPTIONAL, not CC's nullable-REQUIRED, and the difference is CC's own: CC dedicates a whole message
|
|
191
|
+
* (`system/api_retry`) to the retry occasion and can therefore require the key on it, while this
|
|
192
|
+
* interface is ONE shape shared by every phase — the same geometry as CC's shared
|
|
193
|
+
* `control_request_progress` frame, where CC itself makes the key optional.
|
|
194
|
+
*/
|
|
195
|
+
errorStatus?: number;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* design/97 CORE-8 (③): one lightweight TOOL-ACTIVITY beat surfaced from a running task, for a per-agent live
|
|
199
|
+
* "last N tool calls" drill-down. A trusted run-scoped sink ({@link RunInternals.onActivity}) receives one at
|
|
200
|
+
* each tool start + end. Structural (name/phase/ids) + a SHORT redacted primary `arg` for display — NOT the full
|
|
201
|
+
* ARGS or OUTPUT (those carry untrusted/host data; the workflow layer surfaces redacted prompt/output separately).
|
|
202
|
+
*/
|
|
203
|
+
export interface ToolActivity {
|
|
204
|
+
phase: "start" | "end";
|
|
205
|
+
toolCallId: string;
|
|
206
|
+
toolName: string;
|
|
207
|
+
/** wall-clock stamp (epoch ms, `Date.now` read at the emitting Runner where the frame is
|
|
208
|
+
* minted). Carried on BOTH beats: the `start` beat stamps the moment the tool call began, the `end`
|
|
209
|
+
* beat the moment it settled — so a consumer derives per-call duration (`end.at − start.at`) and
|
|
210
|
+
* inter-call idle (an `end.at` → the NEXT `start.at`). Additive/optional: frames recorded by older
|
|
211
|
+
* producers simply lack it (a consumer must treat absence as "unknown", never as 0). */
|
|
212
|
+
at?: number;
|
|
213
|
+
/** design/99 MF-W: a SHORT primary-arg summary for the monitor's `Read(path)` / `Bash(grep …)` display — the
|
|
214
|
+
* single most salient input ABBREVIATED (command→name, path→basename, url→origin+path) + SECRET-SCRUBBED +
|
|
215
|
+
* truncated (~80 code points). Set on `phase: "start"`. NOT the full args object, and NOT `redactHostLeaks`
|
|
216
|
+
* (that over-redacts the path the monitor needs); the abbreviation + scrub is the leak boundary since the value
|
|
217
|
+
* is persisted + SSE-forwarded. See `primaryActivityArg`. */
|
|
218
|
+
arg?: string;
|
|
219
|
+
/** Set on `phase: "end"` — whether the tool call errored. */
|
|
220
|
+
isError?: boolean;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* design/171 §5.2/§6.2 — which carrier a piece of human input arrived on. `"external"` = the
|
|
224
|
+
* notify() event lane; `"system"` = NOT human input (machine-authored text riding a human-input
|
|
225
|
+
* code path) — the speaker projection is a guaranteed no-op for it.
|
|
226
|
+
*/
|
|
227
|
+
export type HumanInputSource = "objective" | "steer" | "next_turn" | "wake" | "external" | "system";
|
|
228
|
+
/** design/171 §6.2 — how a human input was disposed of when its event was emitted:
|
|
229
|
+
* `"applied"` = delivered into a model turn; `"queued"` = accepted, awaiting the next turn
|
|
230
|
+
* boundary; `"parked_for_wake"` = parked for the session's next run (torn-down lane);
|
|
231
|
+
* `"blocked"` (design/373 §4.3, additive — closed-set add, consumers named in the ship post) =
|
|
232
|
+
* a PARKED entry was withheld at resume redelivery — the screen runs on EVERY resume kind that
|
|
233
|
+
* drains parked steers (policy_ask / dry_run_review / plan_review / resource_limit, wake
|
|
234
|
+
* included) — by the deployment's `userPromptSubmit` screen (block verdict, or a fail-closed
|
|
235
|
+
* non-answer/crash): the row was consumed, the frame never reached the model, and the sibling
|
|
236
|
+
* `steering.parked_input_blocked` notice names the same inputId — "park 时收下、redeliver 时被筛"
|
|
237
|
+
* is auditable, never a silent disappearance. */
|
|
238
|
+
export type HumanInputDelivery = "applied" | "queued" | "parked_for_wake" | "blocked";
|
|
239
|
+
/**
|
|
240
|
+
* The fleet-task kinds a DELEGATED run can honestly claim, derived from (never a second spelling of)
|
|
241
|
+
* {@link TaskNotificationPayload}'s `task_type` vocabulary — the same axis a consumer already keys
|
|
242
|
+
* notifications on. The other members of that union name non-agent lanes (a background shell command,
|
|
243
|
+
* a monitor, an externally-produced notification), which no delegated run is; deriving rather than
|
|
244
|
+
* re-listing is what keeps the two faces from drifting into two vocabularies.
|
|
245
|
+
*/
|
|
246
|
+
export type DelegationTaskType = Extract<TaskNotificationPayload["task_type"], "background_agent" | "workflow">;
|
|
247
|
+
/** Streaming events surfaced from a running task (for live UIs). */
|
|
248
|
+
export type TaskEvent = ({
|
|
249
|
+
type: "text_delta";
|
|
250
|
+
delta: string;
|
|
251
|
+
} & TaskEventIdentity) | ({
|
|
252
|
+
/**
|
|
253
|
+
* #447 — the assistant's streaming PROSE SEGMENT is COMPLETE: the model closed the text content
|
|
254
|
+
* block whose bytes just streamed as `text_delta`s. This is the explicit segment boundary a
|
|
255
|
+
* REMOTE consumer needs so it never has to guess segment ends from wire silence (the idle-flush
|
|
256
|
+
* heuristic this retires cut one slow-model reply into N fragments). CC-aligned: CC's agent
|
|
257
|
+
* stream yields each finished content block as its own assistant-message unit at the provider's
|
|
258
|
+
* `content_block_stop` — block completion IS the segmentation signal there; a single-process
|
|
259
|
+
* consumer reads it off the provider stream natively, and this event is that same boundary
|
|
260
|
+
* surfaced on the TaskEvent wire (CC pays the byte cost of re-carrying the block at the stop;
|
|
261
|
+
* so does this event's `content`).
|
|
262
|
+
*
|
|
263
|
+
* `content` = the authoritative FULL text of the completed segment (byte-equal to that
|
|
264
|
+
* segment's accumulated deltas, from the brain's own accumulation) — a consumer commits the
|
|
265
|
+
* segment from it instead of trusting its own delta stitching. UNTRUSTED model output for
|
|
266
|
+
* display only, same contract as `text_delta`. Emitted only for a segment that holds bytes: an
|
|
267
|
+
* empty text block closes silently (a boundary with no segment would render phantom rows).
|
|
268
|
+
*
|
|
269
|
+
* Additive + ignorable. HONEST ABSENCE: the frame exists only when the serving Brain reports
|
|
270
|
+
* block ends (`text_end` on its event stream — all three first-party brains do; a custom brain
|
|
271
|
+
* that never emits them yields a wire without this frame). A consumer treats per-segment
|
|
272
|
+
* presence as the signal and falls back to its own heuristic only on streams that carry none —
|
|
273
|
+
* absence is "unreported", never "the segment did not end".
|
|
274
|
+
*/
|
|
275
|
+
type: "text_end";
|
|
276
|
+
content: string;
|
|
277
|
+
} & TaskEventIdentity) | ({
|
|
278
|
+
type: "reasoning_delta";
|
|
279
|
+
delta: string;
|
|
280
|
+
} & TaskEventIdentity) | ({
|
|
281
|
+
type: "tool_start";
|
|
282
|
+
toolCallId: string;
|
|
283
|
+
toolName: string; /** {@link ToolSpec.label} when distinct from `toolName` (design/71 liveness); omitted otherwise. */
|
|
284
|
+
label?: string;
|
|
285
|
+
args: unknown;
|
|
286
|
+
} & TaskEventIdentity) | ({
|
|
287
|
+
type: "tool_end";
|
|
288
|
+
toolCallId: string;
|
|
289
|
+
toolName: string;
|
|
290
|
+
/** {@link ToolSpec.label} when distinct from `toolName` (design/71 liveness); omitted otherwise. */
|
|
291
|
+
label?: string;
|
|
292
|
+
isError: boolean;
|
|
293
|
+
/**
|
|
294
|
+
* design/99 §E1 — the tool's MODEL-FACING result content, surfaced for a live UI
|
|
295
|
+
* that renders tool output. It is the harness `AgentToolResult.content` the mapper already produced
|
|
296
|
+
* (previously dropped here); the internal `details` payload is NOT exposed (e.g. a sub-agent tool's
|
|
297
|
+
* `details` is the entire child `TaskResult`).
|
|
298
|
+
*
|
|
299
|
+
* **Type is `unknown` and intentionally non-uniform** — within size it passes through AS-IS to keep
|
|
300
|
+
* structure a UI wants (a `string`, or `(TextContent|ImageContent)[]` blocks); when the serialized body
|
|
301
|
+
* exceeds the core size cap it degrades to a single TRUNCATED STRING (see {@link truncated}). A consumer
|
|
302
|
+
* must handle both shapes.
|
|
303
|
+
*
|
|
304
|
+
* **UNTRUSTED RAW + for OBSERVABILITY ONLY**: it is external tool output (file reads, shell stderr, API
|
|
305
|
+
* bodies) and may carry secrets/paths/PII — core bounds only its SIZE; a consumer MUST redact and MAY
|
|
306
|
+
* further bound before it persists or displays it (same contract as `tool_start.args`). It is for a live
|
|
307
|
+
* UI / trace, NOT for re-feeding to a model: a truncated body would mislead an LLM, and the model already
|
|
308
|
+
* has the full, untruncated result in its own context — build model prompts from the session, never by
|
|
309
|
+
* replaying this field. Absent when the tool produced no content body.
|
|
310
|
+
*/
|
|
311
|
+
output?: unknown;
|
|
312
|
+
/**
|
|
313
|
+
* design/116 (b) — the tool's CC-shaped `toolUseResult` facts (`details.type` ∈ the known card
|
|
314
|
+
* set: edit/create/update/bash/text/grep/glob/notebook-edit/notebook/file_unchanged/mcp/image/
|
|
315
|
+
* document/worktree/todo/task/task-list/task-output/task-stop/agent/fork/workflow-run/web-fetch/
|
|
316
|
+
* web-search/cron-create/cron-delete/cron-list/tool-search/repo-map/monitor-start/
|
|
317
|
+
* enter-plan-mode/exit-plan-mode/report-findings/schedule-wakeup/send-message/agent-transcript/
|
|
318
|
+
* a2a/path_not_in_root/readonly_out_of_root), surfaced on the live wire so a CC-faithful shell
|
|
319
|
+
* renders rich cards (red/green diff, stdout panes, file cards) without re-parsing the
|
|
320
|
+
* model-facing string. Absent when the tool carries no card shape or when the serialized card
|
|
321
|
+
* exceeds the size bound (omitted whole; a consumer falls back to `output`).
|
|
322
|
+
* UNTRUSTED RAW like {@link output}: a consumer MUST redact before persisting or forwarding.
|
|
323
|
+
*
|
|
324
|
+
* **The card set is a two-way contract** (structured-card audit, 2026-08-05): every entry has a tool
|
|
325
|
+
* that mints it and every minted card is registered, so a consumer switching on `details.type`
|
|
326
|
+
* gets neither a promise nothing keeps nor a card it was never told about. That pass ADDED
|
|
327
|
+
* report-findings / schedule-wakeup / send-message / agent-transcript / a2a / document /
|
|
328
|
+
* readonly_out_of_root and the completed **agent** shape (see below); it REMOVED multiedit /
|
|
329
|
+
* memory-saved / memory-recall, three words with no producer anywhere in core (a batch MultiEdit
|
|
330
|
+
* replay mints `type:"edit"` with an `edits[]` array; core mounts no memory tool at all).
|
|
331
|
+
*
|
|
332
|
+
* **Leak boundary**: the allowlist gates by `type`, so what rides is whatever the minting tool put
|
|
333
|
+
* in the object — which makes each mint site responsible for its own field set. The Agent family's
|
|
334
|
+
* completed card is therefore built from an EXPLICIT whitelist rather than a child-`TaskResult`
|
|
335
|
+
* spread: a `TaskResult` can carry `checkpointToken` (the resume CAPABILITY) on the
|
|
336
|
+
* `unexpected.suspended` terminal, and that field, with `checkpointGate`, never leaves the process.
|
|
337
|
+
*
|
|
338
|
+
* **双面契约(CC 2.1.207 @765495 成文,`tool_use_result` describe;CC207-CORE-SWEEP P2-3)** — a tool
|
|
339
|
+
* result has TWO faces that never substitute for each other:
|
|
340
|
+
* - **模型面** = {@link output} / the tool_result text: the string the MODEL reads. It may carry
|
|
341
|
+
* model-directed framing — e.g. a delegation tool's trailer (agentId / usage lines the model uses to
|
|
342
|
+
* address SendMessage or budget itself). That framing is FOR THE MODEL; it is not a host data format.
|
|
343
|
+
* - **宿主面** = this field: the tool's structured Output object, per-tool shaped, keyed by the matching
|
|
344
|
+
* tool call's name. For a delegation/Agent-family tool the completed shape is the subagent's final
|
|
345
|
+
* report WITHOUT the model-directed trailer, plus run totals (CC names it `AgentToolCompletedOutput`).
|
|
346
|
+
* Both Agent shapes ride `type:"agent"` and are told apart by `status`: `"async_launched"` is the
|
|
347
|
+
* background launch RECEIPT (task_id / description / prompt), any other value is the completed
|
|
348
|
+
* landing report (`result` / `stats` / `resolvedModel` / `toolStats` / kept `worktreePath` /
|
|
349
|
+
* failure attribution). Branch on `status`, never on which keys happen to be present. (Until
|
|
350
|
+
* 2026-08-05 only the receipt actually arrived — the completed delivery carried no `type` and was
|
|
351
|
+
* dropped by the allowlist, so this paragraph described something a host could not observe.)
|
|
352
|
+
* A host/shell MUST render from `structured` and MUST NOT parse the model-facing text back into data
|
|
353
|
+
* (trailer wording is model-audience prose and may change without notice); symmetrically, core never
|
|
354
|
+
* strips the trailer from the model face just because the structured face exists — the model still needs
|
|
355
|
+
* it in-context. When `structured` is absent, the honest fallback is to display `output` as opaque text,
|
|
356
|
+
* not to scrape it.
|
|
357
|
+
*/
|
|
358
|
+
structured?: unknown;
|
|
359
|
+
/**
|
|
360
|
+
* Present iff {@link isError} is true AND the harness result's `details` carried a string
|
|
361
|
+
* discriminator — read as `details.code` first, falling back to `details.errorKind` (each must be
|
|
362
|
+
* a string; `code` wins when both are present, as the deliberate tool-chosen spelling). The
|
|
363
|
+
* `errorKind` leg is what makes a LOOP-THROWN error's frame classifiable: the loop's thrown-error
|
|
364
|
+
* fold and the resume legs write the discriminator under that name. Lifted so a consumer never
|
|
365
|
+
* has to parse the (contract-stable) result text. Engine-minted vocabulary today includes
|
|
366
|
+
* `"tool.not_found"` (unknown tool name), `"gate.parked"` (an abort short-circuit contaminated
|
|
367
|
+
* this call because a durable gate parked the batch — the "Operation aborted" family), and, for a
|
|
368
|
+
* failed MCP tool call, the MCP failure class — see {@link import("./mcp-failure.js").McpFailure.kind}
|
|
369
|
+
* for the words (the same closed set the per-server status and the wiring manifest's `mcp[]` carry;
|
|
370
|
+
* this frame only transports it). Additive: absent on error frames minted before this field
|
|
371
|
+
* existed, and on error results whose details carry no string discriminator under either name.
|
|
372
|
+
*/
|
|
373
|
+
errorCode?: string;
|
|
374
|
+
/**
|
|
375
|
+
* @contract tool_end.delivered — WHETHER THE REQUEST REACHED ITS SERVER, when this frame closes a
|
|
376
|
+
* failed call whose result carried the MCP failure record: the closed
|
|
377
|
+
* {@link import("./mcp-failure.js").McpDelivered} (`"yes"` / `"no"` / `"unknown"`), lifted from
|
|
378
|
+
* `details.delivered` beside {@link errorCode} and read TOGETHER with it — the same `errorCode`
|
|
379
|
+
* (`connection_closed`) is safe to retry at `"no"` (nothing was sent) and must be verified first at
|
|
380
|
+
* `"unknown"` (the request may have executed). The meaning of each word is owned by
|
|
381
|
+
* {@link import("./mcp-failure.js").McpFailure.delivered}; this frame only transports it. Screened
|
|
382
|
+
* against the closed set (an out-of-vocabulary value is not lifted) and never derived from the
|
|
383
|
+
* code or the text. ABSENT on every frame whose result carried no such verdict — every executed
|
|
384
|
+
* call and every non-MCP failure — and a consumer must not read a semantic out of the absence.
|
|
385
|
+
*/
|
|
386
|
+
delivered?: import("./mcp-failure.js").McpDelivered;
|
|
387
|
+
/**
|
|
388
|
+
* WHICH call the gate was holding, when this frame is a park-contamination frame — the tool call
|
|
389
|
+
* id of the gated call. Three conditions, ALL required: `errorCode === "gate.parked"`, the run is
|
|
390
|
+
* tearing down behind a committed park, and that park binds a tool call at all.
|
|
391
|
+
*
|
|
392
|
+
* **WHERE IT COMES FROM — never lifted from the result**, and deliberately unlike its neighbour
|
|
393
|
+
* {@link errorCode}: that one IS the result's own `details.code`, i.e. tool-authored, because a
|
|
394
|
+
* tool classifying its own failure is bounded self-description. This field is an assertion about a
|
|
395
|
+
* DIFFERENT call, so a tool able to author it could point an approval UI at a call nobody is
|
|
396
|
+
* holding. It is therefore engine-minted — read from the run's committed park holder and handed to
|
|
397
|
+
* the frame projection as a parameter, the same posture (and the same reason) as {@link settledBy}.
|
|
398
|
+
* `errorCode` only participates in selecting WHETHER the id is placed; it never supplies the
|
|
399
|
+
* value, and on its own it is not enough — the run must also be aborting behind a real park, which
|
|
400
|
+
* is why a tool declaring `gate.parked` on its own failure cannot conjure this field.
|
|
401
|
+
*
|
|
402
|
+
* Why it is on the frame: a durable gate parks ONE call and the loop then short-circuits every
|
|
403
|
+
* sibling in the batch with an identical "Operation aborted" body, so `gate.parked` alone says
|
|
404
|
+
* "something parked this batch" without saying WHAT. A consumer wanting to render "waiting on
|
|
405
|
+
* <the gated call>" beside the collateral frames otherwise has to re-derive the pair from batch
|
|
406
|
+
* adjacency and timing — an inference that is wrong exactly when it matters (a batch with more
|
|
407
|
+
* than one ask candidate, a reordered stream).
|
|
408
|
+
*
|
|
409
|
+
* ONLY EVER STRUCTURALLY PROVEN, never guessed: the id is read off the committed checkpoint's
|
|
410
|
+
* pending tool call, so it is the same value the checkpoint parks on and the same value the
|
|
411
|
+
* decide/resume lane answers about. It is therefore ABSENT — not approximated — for the park
|
|
412
|
+
* kinds that hold no call (a resource-limit slice, a plan review), and absent for every abort
|
|
413
|
+
* that is not a gate park at all (user interrupt, timeout, walltime): those frames are
|
|
414
|
+
* byte-unchanged. Absence means "core cannot prove a causal call", never "there wasn't one".
|
|
415
|
+
*/
|
|
416
|
+
gatedCallId?: string;
|
|
417
|
+
/**
|
|
418
|
+
* The gate's record of the pass that admitted or refused this call, when the call went through the
|
|
419
|
+
* tool gate at all ({@link import("./gate-outcome.js").GateOutcome}): `disposition` (allowed, or
|
|
420
|
+
* denied by which LAYER), `settlement` (how the ask this pass consumed ENDED — a person's yes or
|
|
421
|
+
* no with the channel's attribution and, on a bare "no", no note; a window that elapsed and whose;
|
|
422
|
+
* nobody reachable; the wait aborted…) and `origin` (who ASKED). It is the SAME object the
|
|
423
|
+
* `permissionDenied` observer received and the engine's per-call sideband recorded — one mint, three
|
|
424
|
+
* faces — so a renderer never branches on prose to tell "a person refused" from "the window closed".
|
|
425
|
+
*
|
|
426
|
+
* WHERE IT COMES FROM — never inferred here: the gate mints it at its exit, the decide lane mints it
|
|
427
|
+
* from the host's decision facts joined with the row's persisted origin. Core does not derive it
|
|
428
|
+
* from `isError`, from the result, or from anything a tool or a post-tool hook can write.
|
|
429
|
+
*
|
|
430
|
+
* ABSENT on every frame of a call the gate never saw (a tool call on an unarmed gate, a deferred
|
|
431
|
+
* re-issue, a reconcile-recovered orphan). A gated call's frame ALWAYS carries it — an absence is
|
|
432
|
+
* not a fact about the call, and a consumer must not read one out of it.
|
|
433
|
+
*/
|
|
434
|
+
gate?: import("./gate-outcome.js").GateOutcome;
|
|
435
|
+
/**
|
|
436
|
+
* design/99 §E1 — `true` when {@link output} was SIZE-bounded by core (the full body exceeded the cap and
|
|
437
|
+
* was degraded to a truncated string). Lets a consumer detect truncation programmatically instead of
|
|
438
|
+
* parsing the `…[+N chars truncated]` marker out of the string. Absent/false = `output` is the complete
|
|
439
|
+
* content body. (The full untruncated result still lives in the model's context / `ToolResultStore`.)
|
|
440
|
+
*/
|
|
441
|
+
truncated?: boolean;
|
|
442
|
+
/**
|
|
443
|
+
* RB-210 — present iff {@link truncated}: the TRUE original size (characters) of
|
|
444
|
+
* the content this event's {@link output} was degraded from — for a string result, the string's own
|
|
445
|
+
* length; for a non-string result (a blocks array), the SUM of each block's own true size (a text
|
|
446
|
+
* block's `.text.length`; an image/document block's `.data.length`, i.e. its base64 payload) — never
|
|
447
|
+
* `JSON.stringify(content).length`, which is polluted by two different kinds of noise: structural
|
|
448
|
+
* overhead (field-name/punctuation bytes that aren't content at all) AND, for text containing quotes/
|
|
449
|
+
* backslashes/control characters, escape inflation (the same class of bug this field was created to
|
|
450
|
+
* fix in the first place — a real result: 17298 reported vs 13696 true content characters — reappearing
|
|
451
|
+
* in a subtler form if the true count were ever taken from the serialized form instead of the blocks).
|
|
452
|
+
*
|
|
453
|
+
* **Not always arithmetically tied to the marker's own `N`**: the marker counts how much MORE of the
|
|
454
|
+
* shown raw-text rendering exists beyond the cut point; `totalChars` counts the true size of the
|
|
455
|
+
* ORIGINAL value, which for mixed content includes non-text blocks (e.g. an inlined image) that
|
|
456
|
+
* `output` never renders as text at all. The two can legitimately diverge — `totalChars` may run far
|
|
457
|
+
* ahead of the cap plus the marker's `N` — and that gap is itself the honest signal that non-text bulk
|
|
458
|
+
* was dropped, not an inconsistency to paper over.
|
|
459
|
+
*/
|
|
460
|
+
totalChars?: number;
|
|
461
|
+
} & TaskEventIdentity) | ({
|
|
462
|
+
/**
|
|
463
|
+
* RB-126 — context occupancy at a compaction boundary, emitted on EVERY
|
|
464
|
+
* boundary (not only when a compaction fires). The three numbers are the engine's own trigger
|
|
465
|
+
* inputs, verbatim: `usedTokens > compactAtTokens` IS the predicate `shouldCompact` evaluates, so a
|
|
466
|
+
* consumer's "context is N% full" indicator and the moment compaction actually fires can never
|
|
467
|
+
* disagree. Re-deriving the numerator downstream is precisely what this exists to prevent.
|
|
468
|
+
*
|
|
469
|
+
* Deliberately NOT a field on `turn_end`: the boundary check runs AFTER that event, over the
|
|
470
|
+
* post-flush message set, so a value attached there would be a different (earlier) measurement
|
|
471
|
+
* wearing the same name. `windowTokens` is the AUTOCOMPACT window — a model declaring
|
|
472
|
+
* `autoCompactTokens` keeps a LARGER physical request window — so do not present it as "the
|
|
473
|
+
* model's context size".
|
|
474
|
+
*/
|
|
475
|
+
type: "context_usage";
|
|
476
|
+
usedTokens: number;
|
|
477
|
+
windowTokens: number;
|
|
478
|
+
compactAtTokens: number;
|
|
479
|
+
} & TaskEventIdentity) | ({
|
|
480
|
+
type: "turn_end";
|
|
481
|
+
/**
|
|
482
|
+
* Per-turn token usage + cost (the turn that just ended), for live per-turn accounting / a trace UI
|
|
483
|
+
* (service [41]). Re-priced in core from injected pricing (authoritative, like `TaskResult.stats`).
|
|
484
|
+
* Absent only if the turn produced no assistant usage.
|
|
485
|
+
*
|
|
486
|
+
* RB-457-a: `inputTokens` is the cache-MISS count (Anthropic-protocol shape, matches
|
|
487
|
+
* `stats.promptTokens` / the tracer's `brain.call.promptTokens`); `totalInputTokens` is the
|
|
488
|
+
* cache-INCLUSIVE total (`inputTokens + cacheReadTokens + cacheWriteTokens`) that cost is computed
|
|
489
|
+
* from. Sum `totalInputTokens` for context/window/spend views; read `inputTokens` only where the
|
|
490
|
+
* protocol's `input_tokens` is meant. Up to 2.13.x `inputTokens` carried the total.
|
|
491
|
+
*/
|
|
492
|
+
usage?: {
|
|
493
|
+
/** Cache-MISS input tokens for this turn (the Anthropic protocol's `input_tokens` meaning;
|
|
494
|
+
* matches `stats.promptTokens` / `brain.call.promptTokens`). **Up to 2.13.x this field
|
|
495
|
+
* carried the cache-INCLUSIVE total** (RB-457-a, BREAKING at 3.0.0) — that value now lives
|
|
496
|
+
* in `totalInputTokens`. Read this only where the protocol's `input_tokens` is meant. */
|
|
497
|
+
inputTokens: number;
|
|
498
|
+
/** Cache-INCLUSIVE input total for this turn (`inputTokens + cacheReadTokens +
|
|
499
|
+
* cacheWriteTokens`) — what cost is computed from. Sum THIS for context/window/spend
|
|
500
|
+
* views; it equals what `inputTokens` carried up to 2.13.x. */
|
|
501
|
+
totalInputTokens: number;
|
|
502
|
+
outputTokens: number;
|
|
503
|
+
cacheReadTokens: number;
|
|
504
|
+
cacheWriteTokens: number;
|
|
505
|
+
costMicroUsd: number;
|
|
506
|
+
};
|
|
507
|
+
/** TB 尸检 T1-5: set when the turn ended WITHOUT a provider usage frame (e.g. a degenerate-repetition
|
|
508
|
+
* cutoff cancelled the stream before usage arrived). Consumers must treat the missing usage as
|
|
509
|
+
* UNKNOWN — not zero (the provider still served the request; `stats` under-counts it honestly rather
|
|
510
|
+
* than polluting the single-source accounting with an estimate). */
|
|
511
|
+
usageMissing?: true;
|
|
512
|
+
/** TB telemetry B1: the NORMALIZED finish reason of the turn's LAST model call —
|
|
513
|
+
* the engine's `AssistantMessage.stopReason` (`StopReason`: `"stop"`/`"length"`/`"toolUse"`/
|
|
514
|
+
* `"error"`/`"aborted"`). This is post-normalization: e.g. Anthropic `end_turn`/`stop_sequence`
|
|
515
|
+
* both fold to `"stop"`, `max_tokens`→`"length"`; the raw provider reason is NOT preserved here.
|
|
516
|
+
* The machine signal for "was this turn cut by max_tokens" (`"length"`) — previously stream+trace
|
|
517
|
+
* double-blind, which made P1 cap-binding unjudgeable from artifacts. Absent when the turn produced
|
|
518
|
+
* no assistant message. Typed `string` (not the internal `StopReason`) to keep the engine's union
|
|
519
|
+
* private, but the value set is exactly those five tokens. */
|
|
520
|
+
stopReason?: string;
|
|
521
|
+
} & TaskEventIdentity) | ({
|
|
522
|
+
/** design/99 MF-18: a compaction boundary (or, since design/119 #1, mid-turn when the prompt-too-long recovery force-compacts between a failed request and its retry). `trigger` = what caused it: `"auto"` (the context-threshold
|
|
523
|
+
* auto-compaction), `"manual"` (the `TaskStream.compact` / `/compact` path), or `"forced"` (RB-398-b
|
|
524
|
+
* the prompt-too-long recovery and trim-pressure lanes — compaction was not optional; the
|
|
525
|
+
* hook layer's `PreCompactContext.trigger` always had this third state and the wire now carries the
|
|
526
|
+
* SAME value the hooks saw instead of flattening it to `"auto"`. Additive value space — a consumer
|
|
527
|
+
* switching on auto/manual keeps its exact old behavior). `tokensBefore` = the
|
|
528
|
+
* pre-compaction context size. CC parity = `SDKCompactBoundary{trigger, pre_tokens, preserved_segment}`. */
|
|
529
|
+
type: "compacted";
|
|
530
|
+
trigger: "auto" | "manual" | "forced";
|
|
531
|
+
tokensBefore: number;
|
|
532
|
+
/** TB telemetry B4: post-compaction context size in the TRIGGER-GATE coordinate
|
|
533
|
+
* (`postTriggerTokens` = estimate + anchor-less overhead). Pairs with {@link triggerTokensBefore}
|
|
534
|
+
* — NOT with `tokensBefore` (which is the CC `pre_tokens` value in prepareCompaction's coordinate,
|
|
535
|
+
* lacking the overhead term; subtracting across the two coordinates would mis-state the delta,
|
|
536
|
+
* the audit-B-1 / P-13 class). Absent when the post measurement failed (best-effort, never fails a
|
|
537
|
+
* successful compaction). */
|
|
538
|
+
tokensAfter?: number;
|
|
539
|
+
/** TB telemetry B4: pre-compaction size in the SAME trigger-gate coordinate as {@link tokensAfter}
|
|
540
|
+
* (`triggerTokens` — the exact quantity the compaction gate compared). `triggerTokensBefore −
|
|
541
|
+
* tokensAfter` is the self-consistent freed delta. Absent on the legacy emitter. */
|
|
542
|
+
triggerTokensBefore?: number;
|
|
543
|
+
/** TB telemetry B4: wall time of the whole compaction pass (summary call included) — what the
|
|
544
|
+
* boundary pause actually cost. Absent on legacy emitters. */
|
|
545
|
+
durationMs?: number;
|
|
546
|
+
/** CC `SDKCompactBoundary.preserved_segment` parity: `firstKeptEntryId` is the session-tree entry id where
|
|
547
|
+
* the surviving KEPT TAIL begins after this compaction. A consumer maps it to a wire message via the
|
|
548
|
+
* `entryId` carried on `message_committed` (message-identity Phase 1) — render the compaction divider just
|
|
549
|
+
* before that message + relink the preserved tail. (The eventId↔entryId side map service keeps for resumeAt
|
|
550
|
+
* also resolves it, but is no longer required.) Absent on a compaction that reported no kept-tail floor. */
|
|
551
|
+
preserved_segment?: {
|
|
552
|
+
firstKeptEntryId: string;
|
|
553
|
+
};
|
|
554
|
+
/** CC post-compact file-restore parity (2026-07-03): the working files the engine
|
|
555
|
+
* re-read into the summary (attachWorkingFiles), attachment order. A shell renders CC's
|
|
556
|
+
* `Read {path} (…)` cards under the compaction divider from this. Absent when the feature is
|
|
557
|
+
* off or nothing was attached. `truncated` = the content was clipped at the per-file cap. */
|
|
558
|
+
attachedFiles?: Array<{
|
|
559
|
+
path: string;
|
|
560
|
+
chars: number;
|
|
561
|
+
truncated: boolean;
|
|
562
|
+
}>;
|
|
563
|
+
/** design/145 §3 (additive): this compaction's summary call FELL BACK from the independent
|
|
564
|
+
* `compactionModel` to the main model — the small window would have truncated more than
|
|
565
|
+
* `compaction.clampTolerance` of the conversation. Mirror of `compaction.model_fallback`. */
|
|
566
|
+
modelFallback?: true;
|
|
567
|
+
/** design/145 §3 (additive): why the fallback happened ("window" is the only reason today). */
|
|
568
|
+
fallbackReason?: "window";
|
|
569
|
+
/** design/145 §3 (additive): the summary INPUT was clamp-truncated on the compaction model by
|
|
570
|
+
* this ratio (0..1) — set when the budget gate denied the fallback, or when the
|
|
571
|
+
* truncation stayed within tolerance (bounded loss, disclosed either way). */
|
|
572
|
+
clampedRatio?: number;
|
|
573
|
+
/** design/145 §3 (additive): "budget" = the gate that denied the fallback;
|
|
574
|
+
* "tolerance" = within-`clampTolerance` ordinary truncation. */
|
|
575
|
+
clampReason?: "budget" | "tolerance";
|
|
576
|
+
/** 压缩子阶段遥测 (1.283 批 G / design/145 §6 正交后续批, additive): sub-phase wall-time split
|
|
577
|
+
* of THIS pass, mirrored 1:1 from `maybeCompact().phaseDurations` (single source: the
|
|
578
|
+
* `compaction.phase_timings` trace frame minus its envelope). Invariant: prepareMs +
|
|
579
|
+
* (summaryMs ?? 0) + persistMs === {@link durationMs} (exact tiling; `summaryMs` absent on a
|
|
580
|
+
* provider-reuse pass — no summary LLM call happened). Absent on legacy emitters. */
|
|
581
|
+
phaseDurations?: import("./auto-compaction.js").CompactionPhaseDurations;
|
|
582
|
+
} & TaskEventIdentity) | ({
|
|
583
|
+
/**
|
|
584
|
+
* RB-398-b④: a compaction pass concluded WITHOUT landing — the non-landing outcomes
|
|
585
|
+
* used to exist only as `compaction.*` trace frames, so a stream-only consumer watched a
|
|
586
|
+
* context quietly stop being managed (breaker opens, rapid-refill disable, hook blocks —
|
|
587
|
+
* all invisible). One lightweight additive event, emitted at the same sites as the trace
|
|
588
|
+
* frames; the SUCCESS outcome stays on the `compacted` event (this event never carries it,
|
|
589
|
+
* so a consumer treating `compacted` as the only success signal is unchanged).
|
|
590
|
+
* `outcome` reuses the {@link CompactOutcome} vocabulary plus `"suppressed"` (over-threshold
|
|
591
|
+
* pass suppressed by the anti-thrash floor — a state manual compact can never reach, hence
|
|
592
|
+
* outside `CompactOutcome`):
|
|
593
|
+
* - `"failed"` — a burned summary attempt failed, or the open breaker drained a manual
|
|
594
|
+
* request without an attempt (`reason` says which).
|
|
595
|
+
* - `"blocked"` — a `preCompact` hook deliberately blocked the pass.
|
|
596
|
+
* - `"suppressed"` — wanted to fire, held under the §25.2 anti-thrash floor.
|
|
597
|
+
* - `"noop"` — the pass ran but found nothing to compact (no valid cut point/empty history).
|
|
598
|
+
* - `"disabled"` — an explicit (manual/forced) request was refused because compaction is off,
|
|
599
|
+
* or the rapid-refill breaker disabled compaction mid-task (`reason` says which). A spec
|
|
600
|
+
* that statically disables compaction does NOT produce one of these per boundary (that
|
|
601
|
+
* would be per-boundary noise about a static configuration, not a state change).
|
|
602
|
+
* - `"mooted"` — a manual request was processed but no boundary could serve it (dying loop /
|
|
603
|
+
* caller cancel).
|
|
604
|
+
*/
|
|
605
|
+
type: "compaction_outcome";
|
|
606
|
+
outcome: Exclude<CompactOutcome, "compacted"> | "suppressed";
|
|
607
|
+
/** Same three-state vocabulary (and the same value) the pass handed the `preCompact` hook. */
|
|
608
|
+
trigger: "auto" | "manual" | "forced";
|
|
609
|
+
/** Optional human-readable detail (mirrors the trace frame's reason where one exists). */
|
|
610
|
+
reason?: string;
|
|
611
|
+
} & TaskEventIdentity) | ({
|
|
612
|
+
/**
|
|
613
|
+
* TB telemetry B3: the engine injected a SYSTEM steering message (a deadline nudge
|
|
614
|
+
* or the design/130 P2 graceful-finalize instruction) into the model's next turn. Previously these
|
|
615
|
+
* went through `harness.steer()` with no stream echo — a consumer literally could not tell whether
|
|
616
|
+
* the finalize fired (the refute-130 attribution blind spot). Echo only; the model-facing message
|
|
617
|
+
* is unchanged. `preview` is the injected text clipped to a display bound.
|
|
618
|
+
*
|
|
619
|
+
* design/133 (LOW-11/F11): the turn-boundary attachment producers ride the same echo — sources
|
|
620
|
+
* `todo_reminder` / `task_reminder` / `changed_files` / `plan_mode` and the G1/续批 members
|
|
621
|
+
* `background_tasks` / `tools_delta` / `agent_listing` / `skills_listing` / `mcp_instructions`
|
|
622
|
+
* (one event per attachment; a multi-attachment boundary still coalesces into ONE steer message
|
|
623
|
+
* on the model lane). [c209-C]: the listing family's FIRST-FRAME deliveries (initial roster /
|
|
624
|
+
* `<skills>` block riding the first user message, not a steer) emit the same echo frames.
|
|
625
|
+
*
|
|
626
|
+
* `git_status` (env-tail migration, additive member): the git-status frame — the turn-dynamic
|
|
627
|
+
* git facts' carrier since they left the system prompt. Emitted at the frame's append RECEIPT
|
|
628
|
+
* (first-frame / resume-continuation / compaction re-assert deliveries alike). Its `preview`
|
|
629
|
+
* is a CONSTANT wording on purpose (never frame bytes): branch names and status text are
|
|
630
|
+
* repo-controlled and must not enter the event telemetry plane through this echo.
|
|
631
|
+
*/
|
|
632
|
+
type: "steering_injected";
|
|
633
|
+
source: "limit_approach" | "todo_reminder" | "task_reminder" | "tool_search_usage_reminder" | "changed_files" | "plan_mode" | "date_change" | "instructions_change" | "workflow_size_guideline_change" | "budget_usd" | "total_tokens_reminder" | "background_tasks" | "tools_delta" | "agent_listing" | "skills_listing" | "mcp_instructions" | "mcp_dropped_tools" | "final_verification" | "git_status";
|
|
634
|
+
preview: string;
|
|
635
|
+
} & TaskEventIdentity) | ({
|
|
636
|
+
/**
|
|
637
|
+
* design/121 (CC 198 `<new-diagnostics>` parity): NEW LSP diagnostics that appeared after the
|
|
638
|
+
* agent's edits, drained at a turn boundary. The SAME data was injected into the model as a
|
|
639
|
+
* `<new-diagnostics>` context message; this frame is the shell's render source (CC's collapsed
|
|
640
|
+
* `Found N new diagnostic issues in M files (ctrl+o to expand)` row). UNTRUSTED RAW (language-
|
|
641
|
+
* server output over workspace files) — a consumer redacts/bounds before persisting (§E1).
|
|
642
|
+
*/
|
|
643
|
+
type: "diagnostics";
|
|
644
|
+
files: Array<{
|
|
645
|
+
uri: string;
|
|
646
|
+
diagnostics: Array<{
|
|
647
|
+
message: string;
|
|
648
|
+
/** LSP severity: 1=Error 2=Warning 3=Information 4=Hint. */
|
|
649
|
+
severity?: number;
|
|
650
|
+
range?: {
|
|
651
|
+
start: {
|
|
652
|
+
line: number;
|
|
653
|
+
character: number;
|
|
654
|
+
};
|
|
655
|
+
end?: {
|
|
656
|
+
line: number;
|
|
657
|
+
character: number;
|
|
658
|
+
};
|
|
659
|
+
};
|
|
660
|
+
code?: string | number;
|
|
661
|
+
source?: string;
|
|
662
|
+
}>;
|
|
663
|
+
}>;
|
|
664
|
+
isNew: true;
|
|
665
|
+
} & TaskEventIdentity) | ({
|
|
666
|
+
/**
|
|
667
|
+
* message-identity Phase 1 (the CC `SDKCompactBoundary` parity enabler): a persisted session-tree entry was
|
|
668
|
+
* just COMMITTED for a RENDERABLE message the consumer has already streamed. `entryId` = `SessionTreeEntry.id`
|
|
669
|
+
* (uuidv7, persisted, replica-stable) — the SAME id space as `compacted.preserved_segment.firstKeptEntryId`,
|
|
670
|
+
* E18 `resumeAt`, and the design/381 `rewindFilesTo`. `role` + (for a tool result) `toolCallId` let a consumer bind it to
|
|
671
|
+
* the message it rendered: a tool result by its `toolCallId`; an assistant/user message by stream order (this
|
|
672
|
+
* is the terminal boundary for the content just streamed, after its deltas and before its tool results). A
|
|
673
|
+
* consumer builds its own `entryId → message` map from these to resolve `preserved_segment` WITHOUT the
|
|
674
|
+
* eventId↔entryId side map. Terminal per-message boundary; additive + ignorable. ABSENT for a message that did
|
|
675
|
+
* not persist (an empty-failure assistant) and for non-message entries (a compaction summary / model_change —
|
|
676
|
+
* never a `message_committed`). The engine still holds no eventId→entryId map (the deployment owns it); this
|
|
677
|
+
* only pushes the persisted id onto the wire, the constitution's primitive/profile split (cf. §E2 `eventId`).
|
|
678
|
+
*/
|
|
679
|
+
type: "message_committed";
|
|
680
|
+
entryId: string;
|
|
681
|
+
role: "user" | "assistant" | "toolResult";
|
|
682
|
+
/** Present iff `role === "toolResult"`: the exact join key to the `tool_start`/`tool_end` already rendered. */
|
|
683
|
+
toolCallId?: string;
|
|
684
|
+
} & TaskEventIdentity) | ({
|
|
685
|
+
/**
|
|
686
|
+
* design/171 §5.5/§6.2 — the human-input LIFECYCLE arm: one frame per human input this run
|
|
687
|
+
* accepts (WHO fed the run WHAT, per carrier). Additive + ignorable like its siblings.
|
|
688
|
+
* Deliberately NOT folded into `message_committed`: that event only fires for messages that
|
|
689
|
+
* PERSISTED, while a human input's lifecycle (queued / parked) exists before — and sometimes
|
|
690
|
+
* without — a committed message. Constructed via `buildHumanInputEvent` (mints the inputId,
|
|
691
|
+
* materializes no absent optional keys); `HumanInputEvent` is the Extract-derived alias.
|
|
692
|
+
*
|
|
693
|
+
* Attribution semantics are THREE-VALUED, never collapsed:
|
|
694
|
+
* - `actor` ABSENT ⇒ the caller supplied none (anonymous — every pre-171 caller);
|
|
695
|
+
* - `actor.hostAsserted === false` ⇒ an UNVERIFIED self-claim the host chose to carry;
|
|
696
|
+
* - an OLD event stream that predates this arm simply has no `human_input` frames at all —
|
|
697
|
+
* a consumer distinguishes "attribution unknowable (pre-arm producer)" by the emitting
|
|
698
|
+
* core's version, never by guessing from field absence on some other event.
|
|
699
|
+
*/
|
|
700
|
+
type: "human_input";
|
|
701
|
+
/** Correlation/idempotency key. Caller-supplied when the ingress already minted a message id
|
|
702
|
+
* (the parked-steer queue's `inputId` is THE same key); a uuidv7 otherwise. */
|
|
703
|
+
inputId: string;
|
|
704
|
+
/** Ordinal of this input among the run leg's accepted human inputs (1-based, drain order for
|
|
705
|
+
* parked entries). Leg-scoped: a resumed leg restarts at 1 — `inputId` is the global key and
|
|
706
|
+
* stream order the global order; a serving layer needing a session-global ordinal mints its
|
|
707
|
+
* own. */
|
|
708
|
+
sessionSeq: number;
|
|
709
|
+
/** Which concrete carrier accepted the input. OPEN set — a serving layer self-reports its own
|
|
710
|
+
* carriers (question note / elicitation / decide reason / …) through the same arm; the
|
|
711
|
+
* core's five report their {@link HumanInputSource} name. */
|
|
712
|
+
carrier: string;
|
|
713
|
+
source: HumanInputSource;
|
|
714
|
+
/** Who asserted the actor's identity (Slack app id, SSO IdP …) — mirrored from `actor.issuer`
|
|
715
|
+
* or supplied alone when the host can name its ingress but not the subject. */
|
|
716
|
+
issuer?: string;
|
|
717
|
+
actor?: ActorAssertion;
|
|
718
|
+
delivery: HumanInputDelivery;
|
|
719
|
+
/** Plaintext principal correlation key (deliberately not hashed — the audit consumer joins
|
|
720
|
+
* on it). */
|
|
721
|
+
principal?: string;
|
|
722
|
+
/** The persisted session-tree entry the input landed as, WHEN the emitter knows it (a
|
|
723
|
+
* serving layer replaying the ledger); the core's live emissions precede the commit and
|
|
724
|
+
* omit it — consumers there join via `message_committed` + stream order. */
|
|
725
|
+
entryId?: string;
|
|
726
|
+
} & TaskEventIdentity) | ({
|
|
727
|
+
/**
|
|
728
|
+
* design/99 §E3/§E10 — a live BRAIN-LAYER status the brain would otherwise absorb
|
|
729
|
+
* silently (a 429 rate-limit / a 5xx or network retry / a reconnect / an open circuit breaker). Emitted
|
|
730
|
+
* the moment the brain decides to wait/retry, carrying the graduated {@link BrainStatusPhase} + an
|
|
731
|
+
* optional `retryInSec` so a UI can show "rate limited, retrying in Ns". EPHEMERAL — a pure liveness
|
|
732
|
+
* signal, never persisted or replayed on resume. The phase and the hint are provider-neutral (no
|
|
733
|
+
* provider taxonomy, no `stop_reason`); the failing attempt's HTTP status rides its own seat in the
|
|
734
|
+
* retry context only — see {@link BrainStatus.errorStatus}.
|
|
735
|
+
* SCOPE: reflects THIS task's own (top-level) brain calls. A delegated sub-agent's brain liveness routes
|
|
736
|
+
* to the SUB-AGENT's own stream (isolated, like every child event — §E2); a background internal brain
|
|
737
|
+
* call (memory recall/consolidation) is intentionally not surfaced.
|
|
738
|
+
*/
|
|
739
|
+
type: "status";
|
|
740
|
+
} & BrainStatus & TaskEventIdentity) | ({
|
|
741
|
+
/**
|
|
742
|
+
* design/115 P2 core slice — SDK-side projection of a background task terminal notification. The model-facing
|
|
743
|
+
* projection is the sibling `<task-notification>` XML injected through the runner's system-injection lane when
|
|
744
|
+
* the originating harness is still running; durable idle wake/resume is a deployment transport concern.
|
|
745
|
+
*/
|
|
746
|
+
type: "task_notification";
|
|
747
|
+
notification: TaskNotificationPayload;
|
|
748
|
+
/** design/373 §3.6 — the frame's injection tier as DELIVERED (additive; consumers tolerate
|
|
749
|
+
* absence): on the live lane, the tier the routing used (`next`/`now` → steer lane, `later`
|
|
750
|
+
* → followUp lane); on the turn-open redelivery lane, the tier the park CARRIED. ABSENT is a
|
|
751
|
+
* fact, not a default: a pre-373 producer event, or a parked record whose tier was not
|
|
752
|
+
* carried (the terminal sweep's re-pend) — never read absence as `"later"`. */
|
|
753
|
+
priority?: import("./task-notification.js").SystemInjectionPriority;
|
|
754
|
+
} & TaskEventIdentity) | ({
|
|
755
|
+
/**
|
|
756
|
+
* design/99 MF-10 — a SUBAGENT PROGRESS TICK: the delegated sub-run's usage ACCRUING
|
|
757
|
+
* over time, emitted at each turn boundary WHILE it runs, so a parent UI animates the Task card's rollup
|
|
758
|
+
* (tokens/tool-uses climbing). Emitted ONLY for a subagent (a task with `parentToolCallId`); a top-level
|
|
759
|
+
* run's own usage is the consumer's own. `status:"running"` mid-run; the terminal usage is the `done` result.
|
|
760
|
+
* Live/ephemeral — NOT persisted (a resumable trace re-derives usage from the result), like `status`.
|
|
761
|
+
* Maps to CC `SDKTaskProgress` (camelCase here → snake_case `task_id`/`total_tokens`/`tool_uses`/`duration_ms`
|
|
762
|
+
* at the SDK/service projection, the same convention as `toolCallId`→`tool_use_id`).
|
|
763
|
+
*/
|
|
764
|
+
type: "task_progress";
|
|
765
|
+
taskId: string;
|
|
766
|
+
/**
|
|
767
|
+
* The FLEET-task kind of the run this tick reports on — the type discriminator a consumer needs
|
|
768
|
+
* when it folds progress ticks and task notifications into one ledger. Without it the only
|
|
769
|
+
* available test is "it appeared on the fleet stream, therefore it is an agent", and that
|
|
770
|
+
* inference has already minted an agent-shaped row for a task that was a background shell
|
|
771
|
+
* command. Drawn from {@link TaskNotificationPayload}'s `task_type` vocabulary (no second
|
|
772
|
+
* spelling of one axis) and narrowed to the members a progress tick can honestly claim.
|
|
773
|
+
*
|
|
774
|
+
* Present on every tick from a run that HAS a fleet row: `"background_agent"` for a background
|
|
775
|
+
* delegation child (its registry `a*` row) and `"workflow"` for a workflow-spawned agent (its
|
|
776
|
+
* `wa*` read-face row) — the latter alongside, not in place of, {@link workflowRunId}, which
|
|
777
|
+
* names the owning RUN while this names the KIND. ABSENT is a fact, not missing data: a
|
|
778
|
+
* SYNCHRONOUS delegated child has no fleet row at all. Additive — tolerate absence, and never
|
|
779
|
+
* read absence as "not an agent".
|
|
780
|
+
*/
|
|
781
|
+
taskType?: DelegationTaskType;
|
|
782
|
+
/**
|
|
783
|
+
* #258 — the registry row's STOP-CYCLE generation this tick reports from (fresh
|
|
784
|
+
* spawn = 1, every launched revival bumps it), the same counter `TaskNotificationPayload.seq`
|
|
785
|
+
* and `BackgroundChildEvent.seq` already speak — one axis, not a third spelling. It answers
|
|
786
|
+
* the one question a fleet consumer cannot otherwise decide when a frame arrives late: "late
|
|
787
|
+
* first frame of the cycle I know (same value), or a revived run I have not folded yet
|
|
788
|
+
* (higher value)?" Advisory observation, stamped at spawn/revive from the registry's own
|
|
789
|
+
* counter; the settle-time ledger stays the authority. ABSENT is a fact, not a gap: a run
|
|
790
|
+
* with no `a*` registry row (a SYNCHRONOUS delegated child, a workflow `wa*` agent, a
|
|
791
|
+
* top-level run) has no generation concept, and absence must never be read as "cycle 1".
|
|
792
|
+
*/
|
|
793
|
+
seq?: number;
|
|
794
|
+
/** Workflow-lane self-identification (field-proven: the SSE-forwarded tick of a
|
|
795
|
+
* WORKFLOW child previously carried a bare uuid with no workflow identity — indistinguishable
|
|
796
|
+
* from an unknown nested subagent; the fleet lane had `wa*`+workflowRunId but this lane had
|
|
797
|
+
* nothing). Present iff the tick bubbled out of a workflow-spawned agent. Additive. */
|
|
798
|
+
workflowRunId?: string;
|
|
799
|
+
/** the workflow agent's display label (opts.label / auto `agent-N`), riding with
|
|
800
|
+
* {@link workflowRunId} for the same self-identification. Additive. */
|
|
801
|
+
workflowAgentLabel?: string;
|
|
802
|
+
/** design/99 (nested-subagent live tree): the spawning run's taskId — present on a DEEPER-than-1 subagent tick
|
|
803
|
+
* (a subagent spawned by another subagent), forwarded via `RunInternals.onForwardEvent` so a UI nests the
|
|
804
|
+
* node directly (this.taskId under parentTaskId) without mapping `parentToolCallId` → owner. The top-level
|
|
805
|
+
* run is known to the consumer; its direct children carry the top's taskId here. Absent on a top-level tick. */
|
|
806
|
+
parentTaskId?: string;
|
|
807
|
+
/** design/99 BC-2 (Service AI [§I]): a short, single-line HUMAN display name for the subagent — its explicit
|
|
808
|
+
* `taskName` or the selected agent-type. So a Fleet child row shows a readable label instead of the raw
|
|
809
|
+
* `taskId` (the stable row identity stays the taskId). ABSENT when the subagent carries neither a taskName
|
|
810
|
+
* nor an agent-type — the child then keeps its taskId (NOT the raw objective, which could leak a delegated
|
|
811
|
+
* secret to a progress-scoped consumer — dual-review Q2). Sanitized (control-char-stripped, length-capped). */
|
|
812
|
+
name?: string;
|
|
813
|
+
/**
|
|
814
|
+
* The model this sub-run was PREPARED with — the resolved id, read off the leg's prepared model
|
|
815
|
+
* at the mint, so it is the same value `TaskResult.model` and the `task.start` trace frame carry.
|
|
816
|
+
* (`task.end` reports the run's totals and has never named a model; there is nothing to join to
|
|
817
|
+
* there.)
|
|
818
|
+
*
|
|
819
|
+
* Why it rides the live lane at all: a delegation may name a tier word (`"sonnet"`), an agent
|
|
820
|
+
* definition's model, or NOTHING (inherit the caller's current model, or fall to the `subagent`
|
|
821
|
+
* role) — every one of those resolves somewhere the consumer cannot see, so a shell badging a
|
|
822
|
+
* running child previously had only the REQUESTED word (or nothing) to render, which is a
|
|
823
|
+
* different claim from what the child runs on. This is the resolved answer, not the request.
|
|
824
|
+
*
|
|
825
|
+
* PREPARED, deliberately, and NOT "whatever is serving this turn" — stated in the first sentence
|
|
826
|
+
* because the difference is observable. A mid-run DEGRADE switch (`spec.limits.degrade`) or a
|
|
827
|
+
* gateway re-route moves the serving model without rewriting this field, exactly as
|
|
828
|
+
* `TaskResult.model` behaves; the switch is announced on its own channel (`TaskResult.degraded`),
|
|
829
|
+
* and the per-call served id lives on the brain-call telemetry. One name, one meaning, across the
|
|
830
|
+
* three faces that use it — at the cost of being the leg's declared model rather than a live one.
|
|
831
|
+
*
|
|
832
|
+
* Present on every tick this build mints (the frame family is subagent-only by construction, so
|
|
833
|
+
* there is no lane where a leg has no prepared model). Declared optional for the ordinary reason:
|
|
834
|
+
* a consumer folding frames from a pre-key producer must keep compiling, and absence there means
|
|
835
|
+
* "this producer did not state it", never "no model".
|
|
836
|
+
*/
|
|
837
|
+
model?: string;
|
|
838
|
+
/** The child's most recent tool intent as one human line ("Bash npm test",
|
|
839
|
+
* "Edit src/x.ts") — the SAME source/value as the registry sink tick's `currentAction` (residual
|
|
840
|
+
* observability, lane B), attached to the FORWARDED frame because every client wire projects this
|
|
841
|
+
* TaskEvent, not the sink (a deployment's projection legs otherwise see a permanently-absent field).
|
|
842
|
+
* UNTRUSTED display hint — redact at the projection boundary. Absent until the child runs a tool. */
|
|
843
|
+
currentAction?: string;
|
|
844
|
+
usage: {
|
|
845
|
+
totalTokens: number;
|
|
846
|
+
toolUses: number;
|
|
847
|
+
durationMs: number;
|
|
848
|
+
};
|
|
849
|
+
/** Per-turn ticks are `"running"`. ONE final tick fires at the sub-run's settle with the
|
|
850
|
+
* terminal face (`"completed"` | `"failed"` — timeout/blocked fold to failed; a suspended durable
|
|
851
|
+
* pause emits no terminal tick), so a uuid-lane consumer sees the row finish instead of a bare
|
|
852
|
+
* removal after the last running beat. Additive value space — a consumer switching on "running"
|
|
853
|
+
* keeps its exact old behavior. */
|
|
854
|
+
status: "running" | "completed" | "failed";
|
|
855
|
+
} & TaskEventIdentity) | ({
|
|
856
|
+
/**
|
|
857
|
+
* design/99 §E13 — the task's tracked working directory CHANGED mid-run (a `bash`
|
|
858
|
+
* `cd` moved it). Emitted live after the tool that moved it, carrying the new logical cwd, so a shell UI
|
|
859
|
+
* can update its prompt/path display. The tracked cwd is the RAW logical path (a `cd` through a symlink
|
|
860
|
+
* reports the raw path navigated, not the resolved target). Only fires when a real shell is mounted
|
|
861
|
+
* (absent under `handsReadOnly` / no-exec). The initial cwd is NOT announced (it is the known root); only
|
|
862
|
+
* subsequent changes are. A live observability signal — not persisted, and the tracked cwd RESETS to the
|
|
863
|
+
* root across a suspend/resume (the per-leg execution cwd is not durable today — a pre-existing limitation
|
|
864
|
+
* tracked with the E18/E19 execution-state durability work); within a leg, every `cd` move is reported.
|
|
865
|
+
*/
|
|
866
|
+
type: "workspace_changed";
|
|
867
|
+
cwd: string;
|
|
868
|
+
} & TaskEventIdentity) | ({
|
|
869
|
+
/**
|
|
870
|
+
* design/173 §2.4/§8.1 — this leg's EFFECTIVE wiring manifest: the machine-readable
|
|
871
|
+
* disclosure of how the run is assembled around the optional human-interaction seams
|
|
872
|
+
* (ask/question/elicit/park/durability/fleet/governance). Emitted ONCE PER PREPARED LEG
|
|
873
|
+
* (root/child/resume each get their own — per-leg assembly genuinely differs, e.g. the
|
|
874
|
+
* engine-stripped background lanes), after prepare and BEFORE the leg's first model/tool
|
|
875
|
+
* interaction; leg identity rides {@link TaskEventIdentity} plus `manifest.leg.kind`.
|
|
876
|
+
*
|
|
877
|
+
* DISCLOSURE, not verification (§0 guarantee boundary): seam presence/shape as facts —
|
|
878
|
+
* liveness (does the callback actually reach a human) is a round-trip probe's job.
|
|
879
|
+
* HOST/OPERATOR plane: the manifest never enters model context, and the `governance`
|
|
880
|
+
* section carries `audience: "operator"` — a serving layer forwarding this stream to a
|
|
881
|
+
* multi-tenant/shared consumer MUST project that section for operators only (its default,
|
|
882
|
+
* when it does no projection, is to not disclose the section). Additive: a consumer that
|
|
883
|
+
* ignores unknown event types is unchanged; consumers of the manifest itself must ignore
|
|
884
|
+
* unknown FIELDS (`schemaVersion` bumps only on a semantic break).
|
|
885
|
+
*/
|
|
886
|
+
type: "wiring_manifest";
|
|
887
|
+
manifest: import("./wiring-manifest.js").WiringManifest;
|
|
888
|
+
} & TaskEventIdentity) | {
|
|
889
|
+
type: "done";
|
|
890
|
+
result: TaskResult;
|
|
891
|
+
};
|
|
892
|
+
/** design/171 §6.2 — the `human_input` arm as a named type (Extract-derived: ONE source, the union
|
|
893
|
+
* above — never a hand-copied second shape). What `buildHumanInputEvent` returns. */
|
|
894
|
+
export type HumanInputEvent = Extract<TaskEvent, {
|
|
895
|
+
type: "human_input";
|
|
896
|
+
}>;
|
|
897
|
+
/**
|
|
898
|
+
* Outcome of a manual {@link TaskStream.compact} request (the enum names align with
|
|
899
|
+
* the diagnosis decision tree's drain exits, so a consumer can tell "202 processed" states apart):
|
|
900
|
+
* - `"compacted"` — a compaction landed at the boundary (the `compacted{trigger:"manual"}` event fired).
|
|
901
|
+
* - `"failed"` — the attempt ran and threw (summarizer failure / empty summary / oversized summary;
|
|
902
|
+
* `compaction.failed` trace frame + `Runner.onError(phase:"compaction")`), or the §17.4 breaker was
|
|
903
|
+
* already open so the request was drained without an attempt (`compaction.failed` frame, no attempt).
|
|
904
|
+
* - `"mooted"` — the request was processed but the run could no longer compact (dying loop: abort /
|
|
905
|
+
* durable suspend / plan review in flight), or the task settled before any boundary arrived.
|
|
906
|
+
* - `"noop"` — the attempt ran but there was nothing to compact (no valid cut point / empty history).
|
|
907
|
+
* - `"blocked"` — a `preCompact` hook deliberately blocked this compaction (not a failure).
|
|
908
|
+
* - `"disabled"` — compaction is disabled by the task spec (`compaction.enabled: false`).
|
|
909
|
+
*/
|
|
910
|
+
export type CompactOutcome = "compacted" | "failed" | "mooted" | "noop" | "blocked" | "disabled";
|