@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.
Files changed (78) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/agents/agent-transcript-tool.d.ts +2 -2
  3. package/dist/agents/cascade.d.ts +2 -3
  4. package/dist/agents/repair-loop.d.ts +2 -2
  5. package/dist/agents/retain-ledger.d.ts +2 -3
  6. package/dist/agents/send-message-tool.d.ts +2 -2
  7. package/dist/agents/session-util.d.ts +2 -2
  8. package/dist/agents/subagent.d.ts +3 -4
  9. package/dist/agents/teacher.d.ts +2 -2
  10. package/dist/agents/team.d.ts +2 -2
  11. package/dist/agents/verify.d.ts +5 -6
  12. package/dist/core/agent-definition.d.ts +172 -0
  13. package/dist/core/agent-definition.js +1 -0
  14. package/dist/core/checkpoint-store.d.ts +8 -4
  15. package/dist/core/delegation-frames.d.ts +298 -0
  16. package/dist/core/delegation-frames.js +21 -0
  17. package/dist/core/engine-notice.d.ts +555 -0
  18. package/dist/core/engine-notice.js +55 -0
  19. package/dist/core/gate-fold.d.ts +12 -0
  20. package/dist/core/gate-fold.js +158 -0
  21. package/dist/core/gate-lanes.d.ts +93 -0
  22. package/dist/core/gate-lanes.js +626 -0
  23. package/dist/core/hands-band.d.ts +134 -0
  24. package/dist/core/hands-band.js +1 -0
  25. package/dist/core/hooks.d.ts +20 -101
  26. package/dist/core/hooks.js +53 -854
  27. package/dist/core/mcp-failure.d.ts +43 -5
  28. package/dist/core/mcp-failure.js +31 -14
  29. package/dist/core/mcp-server-spec.d.ts +217 -0
  30. package/dist/core/mcp-server-spec.js +1 -0
  31. package/dist/core/model-seat.d.ts +99 -0
  32. package/dist/core/model-seat.js +1 -0
  33. package/dist/core/reminder-mint.d.ts +10 -0
  34. package/dist/core/reminder-mint.js +3 -0
  35. package/dist/core/runner/contracts.d.ts +382 -6
  36. package/dist/core/runner/gate-exit.d.ts +177 -9
  37. package/dist/core/runner/gate-exit.js +70 -1
  38. package/dist/core/runner/prepare-caps-and-workflow.d.ts +2 -7
  39. package/dist/core/runner/prepare-delegation-surface.d.ts +2 -7
  40. package/dist/core/runner/prepare-run-refs.d.ts +12 -0
  41. package/dist/core/runner/prepare-run-refs.js +5 -0
  42. package/dist/core/runner/prepare-task.d.ts +2 -2
  43. package/dist/core/runner/runtask.d.ts +4 -71
  44. package/dist/core/runner/runtask.js +18 -6
  45. package/dist/core/runner-deps.d.ts +1416 -0
  46. package/dist/core/runner-deps.js +1 -0
  47. package/dist/core/runtime-caps.d.ts +164 -0
  48. package/dist/core/runtime-caps.js +1 -0
  49. package/dist/core/task-event.d.ts +910 -0
  50. package/dist/core/task-event.js +1 -0
  51. package/dist/core/task-limits.d.ts +110 -0
  52. package/dist/core/task-limits.js +1 -0
  53. package/dist/core/task-result.d.ts +809 -0
  54. package/dist/core/task-result.js +1 -0
  55. package/dist/core/task-spec.d.ts +1370 -0
  56. package/dist/core/task-spec.js +1 -0
  57. package/dist/core/task-stream.d.ts +382 -0
  58. package/dist/core/task-stream.js +1 -0
  59. package/dist/core/tool-spec.d.ts +1174 -0
  60. package/dist/core/tool-spec.js +1 -0
  61. package/dist/core/types.d.ts +26 -7691
  62. package/dist/core/types.js +2 -76
  63. package/dist/core/warm-resume.d.ts +2 -2
  64. package/dist/index.d.ts +2 -1
  65. package/dist/index.js +1 -1
  66. package/dist/orchestration/goal.d.ts +2 -2
  67. package/dist/orchestration/run-spec.d.ts +2 -2
  68. package/dist/orchestration/run-workflow-tool.d.ts +3 -3
  69. package/dist/orchestration/workflow.d.ts +4 -4
  70. package/dist/scenarios/scenario-registry.d.ts +3 -3
  71. package/dist/scenarios/teacher-quickstart.d.ts +2 -2
  72. package/dist/server/http.d.ts +2 -2
  73. package/dist/stores/file/fs-atomic.d.ts +88 -12
  74. package/dist/stores/file/fs-atomic.js +184 -55
  75. package/dist/stores/file/index.d.ts +1 -0
  76. package/dist/stores/file/index.js +1 -0
  77. package/package.json +1 -1
  78. package/test/export-surface.snapshot.json +9 -1
@@ -0,0 +1,298 @@
1
+ /**
2
+ * The DELEGATION frames a parent run observes about its children: `BackgroundChildEvent` (the
3
+ * background-agent beat), `DelegationLifecycleEvent` (the spawn/terminal pair) and the ONE delivery
4
+ * form behind that seat — with its once-per-process malformed-seat latch, which is module-private
5
+ * state and is the reason the type and the function cannot live in different files. Layer 0
6
+ * vocabulary; `types.ts` re-exports every name below, so no consumer's import changes.
7
+ */
8
+ import type { TaskStatus } from "./task-result.js";
9
+ import type { RunnerDeps } from "./runner-deps.js";
10
+ /**
11
+ * design/129-B — one lifecycle event of a BACKGROUND delegation child, delivered to the
12
+ * process-level {@link RunnerDeps.onBackgroundChildEvent} observer. Keys: `taskId` is the registry handle
13
+ * (`a*` domain — the TaskOutput/TaskStop key); `progressTaskId`/`sessionId` are the child RUN's id (uuid
14
+ * domain — the same key `task_progress` frames carry).
15
+ */
16
+ export interface BackgroundChildEvent {
17
+ kind: "spawn" | "tick" | "terminal";
18
+ /** Row key, stable across all three kinds. The prefix is the DOMAIN DISCRIMINATOR (load-bearing,
19
+ * not opaque — design/149): `a*` = registry handle (background subagent —
20
+ * TaskOutput/TaskStop addressable, fleet-footer display lane); `wa*` = synthetic workflow-agent
21
+ * row (NOT a registry row — per-agent read face is the workflow's `TaskOutput(w*)` agent_runs
22
+ * projection + journal API). Display contract (CC anchor): CC's footer renders ONLY the
23
+ * workflow AGGREGATE row — `wa*` rows are read-face/observability rows and SHOULD NOT be
24
+ * rendered in the footer fleet tree; shells filter on the prefix. */
25
+ taskId: string;
26
+ /** design/129: `true` when the child outlives its parent turn (session-scoped). */
27
+ sessionScoped: boolean;
28
+ /** Registry owner key (task-scoped: the host taskId; session-scoped: the sessionId). */
29
+ owner?: string;
30
+ /** Registry scope (tenant/principal isolation key). */
31
+ scope?: string;
32
+ /** spawn: the child's short description (bounded, display-safe). */
33
+ description?: string;
34
+ /** spawn + tick: the RESOLVED subagent type ("general-purpose" / "Explore" /
35
+ * a roster name / "fork") — the fleet row's TYPE column. Never the description. On ticks it is
36
+ * the ROW's type (this spawn), not the nested frame's origin — `progressTaskId` keys that. Rides
37
+ * every tick so a consumer that only forwards ticks (server K-7) still gets the type without
38
+ * waiting for spawn-frame forwarding; `name` stays the design/99 DISPLAY label (description-backed)
39
+ * and was never a type field. */
40
+ agentType?: string;
41
+ /**
42
+ * spawn + tick: the model the ROW runs on — the resolved `ModelRef` the spawner selected for this
43
+ * child (a per-call `model`, an agent definition's, or the caller's own current model, inherited).
44
+ * It answers with the SERVED catalog model id wherever that is knowable, because this value sits
45
+ * beside the child's own `task_progress.model` (the leg's prepared id) and a consumer joins the
46
+ * two: a row saying "sonnet" next to ticks naming the catalog id it routed to is indistinguishable
47
+ * from two different children. A `Model` object carries its id — and a per-call word that passed
48
+ * the spawn gate arrives as exactly that resolved object, so for it the row and the ticks are one
49
+ * value by construction. A definition/tool-level STRING ref resolves through the catalog in force
50
+ * at the spawn judgement; the child's own prepare re-resolves such a ref against the runner's live
51
+ * table, so for those refs alone a catalog hot-swapped between the two reads can lag the row one
52
+ * generation behind the ticks. The DISPLAY rule survives only as the FALLBACK, for a string the
53
+ * judgement-time catalog cannot resolve (a CC tier alias then shows its sema tier, never the alias
54
+ * verbatim). It rides ticks as well as spawn for the same reason
55
+ * {@link agentType} does: a consumer that only forwards ticks must not have to wait for spawn-frame
56
+ * forwarding to fill its row. Like `agentType` it is the ROW's own fact and is NEVER copied off a
57
+ * forwarded frame — a nested descendant's `task_progress` names ITS model, not this row's.
58
+ *
59
+ * ABSENT is a fact, not a gap, and there are exactly three ways to get there:
60
+ * · the delegation named no model anywhere in the chain, so the child runs on the `subagent` ROLE
61
+ * and its concrete id is only decided at the child's own prepare;
62
+ * · the RETAIN-LEDGER wake lane (a `SendMessage` resume of a completed child), whose frames are
63
+ * projected from an `AccessibleTaskRow` — and that row has no model column, so this lane has
64
+ * nothing to state and will not invent one. Stated precisely because the sibling wake lane does
65
+ * NOT share the limitation: a tier-3 DURABLE revive re-enters through the ordinary background
66
+ * spawn, re-derives the model like a first spawn, and its `(revived)` frames carry it whenever
67
+ * that derivation lands on a model (the durable record keeps a `model` of its own; a recorded
68
+ * key the current catalog no longer resolves degrades the revival to the inherited model, and a
69
+ * mount with nothing to inherit then leaves these frames honestly silent);
70
+ * · a pre-key producer.
71
+ * In every case the child's own `task_progress` frames still carry the resolved answer, which is the
72
+ * authority; this field is the row-level convenience beside it.
73
+ */
74
+ model?: string;
75
+ /** spawn: the HOST task's DECLARED task id (parent attribution). Omitted when the host
76
+ * run declared no task id (the `spec.taskId ?? sessionId` fallback would launder a session id into
77
+ * a task-id field — the orphan-pointer shape); {@link parentSessionId} is the always-on linkage. */
78
+ parentTaskId?: string;
79
+ /** spawn: the HOST run's session id (uuid domain) — the always-on parent
80
+ * linkage, present whether or not a task id was declared. */
81
+ parentSessionId?: string;
82
+ /** spawn: the ROOT host session of the delegation tree (uuid domain). For a depth-1
83
+ * child it equals {@link parentSessionId}; for a nested grandchild it is the TOP session while
84
+ * parentSessionId is the intermediate spawner — a recovery/enumeration consumer groups the whole
85
+ * tree under the root without walking intermediate (possibly dead) sessions. */
86
+ rootSessionId?: string;
87
+ /** spawn: epoch ms the child run was launched — the fleet row's time anchor
88
+ * (elapsed rendering without waiting for the first tick). */
89
+ startedAt?: number;
90
+ /** All kinds, `wa*` rows only (design/149 face 2): the owning workflow run's
91
+ * `w*` id. `parentTaskId` stays task-domain (the HOST task) so parent resolvers that only
92
+ * forward resolved task rows keep working; workflow grouping/nesting is THIS annotation's job. */
93
+ workflowRunId?: string;
94
+ /** All kinds: the delegating Agent tool call's OWN `toolCallId` —
95
+ * the same identity {@link TaskEvent}'s `parentToolCallId` already carries on every forwarded child
96
+ * content event (via `RunInternals.parentToolCallId`), extended to this SEPARATE observer/fleet
97
+ * event family so a consumer does not have to reassemble parent attribution from spawn-frame
98
+ * ordering alone (the exact gap a cross-repo consumer hit: FIFO-timing + shape-sniffing to guess
99
+ * which spawn frame a tick/terminal belongs to). Present from spawn — known at birth like
100
+ * {@link transcriptId} — not just on the terminal frame. */
101
+ parentToolCallId?: string;
102
+ /** tick: the forwarded `task_progress` frame's own taskId (uuid domain; nested children carry theirs). */
103
+ progressTaskId?: string;
104
+ /** tick: the forwarded frame's parentTaskId (nested attribution) · terminal: absent. */
105
+ progressParentTaskId?: string;
106
+ /** tick: the child's display name, when the frame carried one. */
107
+ name?: string;
108
+ /**
109
+ * tick (residual observability, lane B): the child's most recent tool intent as one human line ("Bash npm test",
110
+ * "Edit src/x.ts") — derived from the last `tool_start` on the child's stream, so a parent can show
111
+ * "what is it doing right now" with zero polling of the child. Absent until the child runs a tool.
112
+ */
113
+ currentAction?: string;
114
+ /**
115
+ * tick (structured Progress-section source): the SAME `{tool, target}` pair
116
+ * {@link currentAction} concatenates into prose, exposed separately so a consumer can look `toolName`
117
+ * up in its own tool registry (CC `renderToolActivity` parity) instead of parsing the human line.
118
+ * `target` is UNTRUSTED free text (a file path / command / pattern the child chose) — same trust
119
+ * posture as `currentAction` itself; bound + redact before rendering. Absent until the child runs a
120
+ * tool (mirrors {@link currentAction}'s own absence exactly — same source, same lifecycle).
121
+ */
122
+ currentTool?: {
123
+ toolName: string;
124
+ target?: string;
125
+ };
126
+ /** ALL kinds, when known: the child RUN's session id (uuid domain). Every delegation lane resolves it at
127
+ * row BIRTH (a pre-minted id threaded as the child's `spec.sessionId` — the engine's create-on-miss
128
+ * acquire), so a running row is addressable too; RB-427-b brought the workflow-agent (`wa*`) lane, which
129
+ * published it on the terminal frame alone, onto the same contract. ABSENT only where no id is minted:
130
+ * the workflow's two fail-loud spec shapes (`requireExistingSession` / `resumeAt` without a session). */
131
+ sessionId?: string;
132
+ /**
133
+ * ALL kinds (residual observability, lane C): a handle to the child's persisted transcript — the child RUN's session id
134
+ * (uuid domain). A parent reads the last N steps on demand via the `AgentTranscript` tool (pull model:
135
+ * it does NOT inflate the parent's context). Mirror of {@link sessionId} — same value, same presence
136
+ * rules; named distinctly so a consumer keys "how do I read this child's history" without conflating it
137
+ * with routing. Present from spawn precisely so a viewer can seed a LIVE child's transcript rather than
138
+ * waiting for the row to finish.
139
+ */
140
+ transcriptId?: string;
141
+ /**
142
+ * terminal (residual observability, lane A): the child's last ≤10 completed tool steps ({tool, target, outcome}) — the
143
+ * residual a parent needs to write a PRECISE resume prompt ("you stopped at file X, step Y") for a
144
+ * child that was stopped/killed mid-flight, WITHOUT reading the transcript. Bounded; absent if the
145
+ * child ran no tools.
146
+ */
147
+ recentSteps?: import("../agents/subagent-steps.js").SubagentStep[];
148
+ /**
149
+ * terminal (residual observability, lane D — core half): files the child mutated (Write/Edit/MultiEdit/NotebookEdit)
150
+ * with edit counts. The LSP-aggregated `<new-diagnostics>` projection is the shell's half; core
151
+ * supplies only the edited-file list. Bounded; absent if the child edited nothing.
152
+ */
153
+ editedFiles?: import("../agents/subagent-steps.js").SubagentEditedFile[];
154
+ /**
155
+ * terminal (residual observability, lane E): `true` when the child can be revived with `SendMessage` —
156
+ * either its session is retained live and the run was not killed, or it has a NAMED durable row that no
157
+ * USER stop closed (a parent teardown, a reap or a host death is precisely what the durable revival
158
+ * lane recovers from). `false` for a user-stopped child, an anonymous or store-less one, and for the
159
+ * session-teardown reap frame, whose lane cannot know what survives the teardown.
160
+ * Lets a parent's orchestration logic decide "continue it" vs "start fresh" without trial-and-error.
161
+ */
162
+ resumable?: boolean;
163
+ /** terminal: the settled status. */
164
+ status?: "completed" | "killed" | "failed";
165
+ /** terminal + spawn/tick (#258 widened the carriers): the stop-cycle number — on a TERMINAL frame
166
+ * a MIRROR of the sibling task_notification's `TaskNotificationPayload.seq` (same settle, same X3
167
+ * snapshot); on SPAWN and TICK frames the registry row's generation at emit (fresh spawn = 1, a
168
+ * revived cycle's bumped counter), so a fleet consumer can tell a LATE first frame from a revived
169
+ * cycle's frame without waiting for the terminal. One axis with `task_progress`'s `seq` — the tick
170
+ * mirrors the frame's own stamp. Same honest downgrade as ever: present whenever a cycle number is
171
+ * knowable (the registry handle / retain ledger's `cycleSeq`, the durable row's `seq` on a tier-3
172
+ * revival / parked resume), absent when no carrier exists (forging a period would lie). */
173
+ seq?: number;
174
+ /** terminal: the cross-channel completion correlation id —
175
+ * MIRROR of the sibling task_notification's `TaskNotificationPayload.completionId` (same settle,
176
+ * same minted value; the fleet lane was explicitly one of the four channels the original ask named
177
+ * — "同一完成今天从四条通道扇出" — so it gets the same mirror treatment `seq`/`stoppedBy` already
178
+ * have here, not a fifth divergent copy). Absent until the underlying task reaches a terminal
179
+ * settle, or on a pre-P1-3 core build. */
180
+ completionId?: string;
181
+ /** terminal, killed only (design/134 §3.3): who stopped it. MIRROR of task-registry `StopSource`
182
+ * (the single source; inlined to avoid an import cycle — task-registry imports types.ts; third
183
+ * mirror: task-notification.ts `TaskNotificationPayload.stoppedBy` — keep all three in sync).
184
+ * OPEN ENUM: consumers must tolerate unknown values (a future "timeout" is not breaking). */
185
+ stoppedBy?: "user" | "parent" | "system" | (string & {});
186
+ /** terminal: bounded human summary (same text the task_notification carries). */
187
+ summary?: string;
188
+ /** tick: live rollup (`task_progress.usage`) · terminal: the settled
189
+ * `{tokens, turns, costMicroUsd?, toolUses?, durationMs}`. `costMicroUsd` is own+nested and present
190
+ * only when that total is KNOWN (RB-368); `toolUses` is the run's own `stats.toolCalls` and is
191
+ * absent — never zeroed — when the gateway reported no usage. The last two joined the terminal face
192
+ * to close a reporting asymmetry: the workflow lane's completion notification and the Agent tool's
193
+ * own sync `<usage>` footer had published both all along, so one child answered "how much work, how
194
+ * long" on one lane and refused on the other.
195
+ *
196
+ * `durationMs` MEASURES DIFFERENT SPANS on the two kinds, which is worth knowing before plotting it:
197
+ * on a TICK it is the child RUN's own elapsed (the forwarded frame's value, from the run's start);
198
+ * on the TERMINAL it is the delegation LEG's, from the lane's launch instant — which is earlier, so
199
+ * a consumer graphing one series sees a step up at settle. Both are true of what they name; neither
200
+ * can be computed from the other without the spawn/prepare interval, which is why they are not
201
+ * reconciled into one. The intra-turn activity beat carries a third, narrower shape (`{toolUses}`
202
+ * alone — there is no honest live token figure on that lane). */
203
+ usage?: {
204
+ totalTokens?: number;
205
+ toolUses?: number;
206
+ durationMs?: number;
207
+ tokens?: number;
208
+ turns?: number;
209
+ costMicroUsd?: number;
210
+ };
211
+ }
212
+ /**
213
+ * #281 件B — one lifecycle phase of a DELEGATED child leg, delivered to the process-level
214
+ * {@link RunnerDeps.onDelegationLifecycle} observer: the public, deps-level home of the delegation
215
+ * lifecycle that previously lived only on the trusted RunInternals third parameter
216
+ * (`onSubagentSpawn` — sync lane, a steer handle whose `settled` is a void promise) and on the
217
+ * background-lane-only {@link BackgroundChildEvent}. A deployment wired ONLY through `RunnerDeps`
218
+ * now sees every delegation lane through one seat.
219
+ *
220
+ * EMISSION CHOKEPOINT (the reason this seat covers every lane at once): frames are minted by the
221
+ * RUNNER at the child LEG itself — spawn right after the leg's `wiring_manifest` (prepared, nothing
222
+ * run yet), terminal when the leg's `TaskResult` assembles — not by the individual spawn lanes. Every
223
+ * delegation lane (sync/steer/background/fork/revive, workflow-spawned agents) runs its child through
224
+ * this chokepoint, so none of them needs its own emission and none can drift.
225
+ *
226
+ * PER-LEG semantics, deliberately: a durable park + resume, or a retained child's revive, is a NEW
227
+ * leg — each emits its own spawn/terminal pair, and `identity.legKind` (`"resume"`) says which cycle
228
+ * a frame belongs to. Consumers correlate legs of one delegation by `identity.taskId` /
229
+ * `identity.parentToolCallId` (stable across cycles).
230
+ *
231
+ * HONEST ABSENCES (recorded, not gaps to fix silently):
232
+ * · a child whose PREPARE throws emits neither frame (there is no leg identity to report);
233
+ * · the ROOT leg emits nothing here — the deployment called `runTask` itself and holds the result;
234
+ * this seat is the delegation observer, not a run observer;
235
+ * · a deps-only resume (`resume(token, outcome, config)` — no trusted internals re-supplied) of a
236
+ * checkpoint minted BEFORE the delegation axis was persisted
237
+ * ({@link import("./checkpoint-store.js").CheckpointState.isDelegatedChild}) emits neither frame:
238
+ * the row carries no evidence the parked leg was a delegated child, and fabricating the axis
239
+ * would stamp delegation frames onto host-resumed root tasks. Rows minted WITH the axis resume
240
+ * with their spawn/terminal pair even deps-only; a resume that re-supplies trusted internals
241
+ * (every in-engine lane does) was never affected.
242
+ * PAIRING (closed the one hole): a spawn frame is always closed by a terminal frame —
243
+ * the ordinary path emits it where the `TaskResult` assembles, and a POST-SPAWN throw that the
244
+ * stream layer converts into a synthesized failed result (e.g. `resume.tool_unavailable` re-thrown
245
+ * past the run tail) emits the failed terminal from that backstop, gated on a carrier that is set
246
+ * only after the spawn emission and cleared by the ordinary terminal (so the two sites can never
247
+ * both fire for one leg). A leg whose PROCESS dies mid-flight is the only unpaired spawn.
248
+ *
249
+ * OBSERVATION ONLY (matrix §5.2 Q3: frame-rate lifecycle facts ride an observer seat, never
250
+ * onNotice): no return capability, and delivery can never alter the child run —
251
+ * {@link deliverDelegationLifecycle} contains a throwing sink and an async sink's rejection alike.
252
+ *
253
+ * FRAME OBJECT SHAPE (consumer contract, not an implementation detail): every delivered frame is
254
+ * FROZEN and carries a NULL PROTOTYPE — re-minted at the one delivery point so a forged member
255
+ * cannot ride a frame through a writable `Object.prototype`, the same rule (and the same trade) as
256
+ * the {@link import("./hooks.js").HookInvocationIdentity} envelope inside it. `Object.keys`, spread,
257
+ * JSON serialization and direct member reads (`frame.status`) all behave normally, but
258
+ * `frame instanceof Object` is `false` and inherited methods are ABSENT — probe optional members
259
+ * with `Object.hasOwn(frame, "errorCode")` or `"errorCode" in frame`, never
260
+ * `frame.hasOwnProperty(...)` (throws) or implicit string coercion (`` `${frame}` `` throws).
261
+ */
262
+ export type DelegationLifecycleEvent = {
263
+ phase: "spawn";
264
+ /** The child LEG's identity envelope (#281 件A — the same frozen object that leg's own hook
265
+ * invocations carry). `isDelegatedChild` is `true` by construction on every frame here. */
266
+ identity: import("./hooks.js").HookInvocationIdentity;
267
+ } | {
268
+ phase: "terminal";
269
+ /** Same envelope as the leg's spawn frame (one mint per leg). */
270
+ identity: import("./hooks.js").HookInvocationIdentity;
271
+ /** The leg's settled status word — the projection of the `TaskResult.terminal` the spawning lane
272
+ * receives (the sync lane's previously-void `settled` payload, made public). `"suspended"` means a
273
+ * durable park: expect a later `"resume"`-leg spawn/terminal pair if it is redeemed. */
274
+ status: TaskStatus;
275
+ /** Turns the leg completed. */
276
+ turns: number;
277
+ /** The leg's terminal code (the failed cause's `code`), when one was stamped. */
278
+ errorCode?: string;
279
+ };
280
+ /** Test seam (mirrors `__resetMalformedNoticeSeatAnnouncement`): never called by production code. */
281
+ export declare function __resetMalformedDelegationSeatAnnouncement(): void;
282
+ /**
283
+ * The ONE delivery form behind every {@link RunnerDeps.onDelegationLifecycle} emission point (both
284
+ * runner stations — spawn and terminal — call this; a second spelling of the swallow/announce rules
285
+ * would be the #170 triplication reborn). Contract:
286
+ * · a FUNCTION seat is invoked with the frame FROZEN (a mutating observer must not rewrite what a
287
+ * later frame consumer — or the shared identity envelope's other readers — see), contained in the
288
+ * caller's {@link SafeNotifier} against BOTH failure shapes the void-typed seat admits: a
289
+ * synchronous throw ({@link SafeNotifier.notify}) and an async sink's rejected promise
290
+ * ({@link observeThenableRejection} routes it back through the same notifier/site — the #253
291
+ * three-station form). A broken observer never faults the child run.
292
+ * · a PRESENT NON-function seat is a bad deployment value; #123 forbids folding it to silence. There
293
+ * is no per-frame console fallback (this is a frame-rate observer stream, not an announcement
294
+ * channel — an unwired seat means UNOBSERVED, and echoing every spawn to stderr would flood), so
295
+ * the loud exit is the seat DEFECT itself: announced via `console.warn` once per process.
296
+ * · an ABSENT seat is a plain no-op (the deployment chose not to observe).
297
+ */
298
+ export declare function deliverDelegationLifecycle(seat: RunnerDeps["onDelegationLifecycle"], event: DelegationLifecycleEvent, notifier: import("./safe-notify.js").SafeNotifier, site: string): void;
@@ -0,0 +1,21 @@
1
+ import { observeThenableRejection } from "./safe-notify.js";
2
+ let malformedDelegationSeatAnnounced = false;
3
+ export function __resetMalformedDelegationSeatAnnouncement() {
4
+ malformedDelegationSeatAnnounced = false;
5
+ }
6
+ export function deliverDelegationLifecycle(seat, event, notifier, site) {
7
+ if (typeof seat === "function") {
8
+ const frame = Object.freeze(Object.assign(Object.create(null), event));
9
+ notifier.notify(() => observeThenableRejection(seat(frame), notifier, site), site);
10
+ return;
11
+ }
12
+ if (seat !== undefined && !malformedDelegationSeatAnnounced) {
13
+ malformedDelegationSeatAnnounced = true;
14
+ try {
15
+ console.warn(`The delegation-lifecycle sink (RunnerDeps.onDelegationLifecycle) holds ${seat === null ? "null" : typeof seat} — not a ` +
16
+ `function. Delegation spawn/terminal frames are NOT delivered until the wiring is fixed (omit the key, or wire a function).`);
17
+ }
18
+ catch {
19
+ }
20
+ }
21
+ }