@sema-agent/core 7.5.2 → 7.6.1
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 +67 -0
- package/dist/agents/agent-transcript-tool.d.ts +2 -2
- package/dist/agents/cascade.d.ts +4 -5
- package/dist/agents/cascade.js +12 -10
- package/dist/agents/repair-loop.d.ts +7 -5
- package/dist/agents/repair-loop.js +13 -15
- 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 +27 -46
- package/dist/agents/subagent.js +119 -105
- package/dist/agents/suspend-guard.d.ts +31 -19
- package/dist/agents/suspend-guard.js +14 -8
- package/dist/agents/teacher.d.ts +2 -2
- package/dist/agents/teacher.js +9 -9
- package/dist/agents/team.d.ts +6 -5
- package/dist/agents/team.js +10 -8
- package/dist/agents/verify.d.ts +8 -9
- package/dist/agents/verify.js +17 -17
- package/dist/core/a2a.js +2 -1
- package/dist/core/agent-definition.d.ts +172 -0
- package/dist/core/agent-definition.js +1 -0
- package/dist/core/ask-origin.d.ts +60 -7
- package/dist/core/ask-origin.js +26 -1
- package/dist/core/checkpoint-store.d.ts +78 -76
- package/dist/core/checkpoint-store.js +17 -1
- 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/gate-outcome.d.ts +189 -0
- package/dist/core/gate-outcome.js +70 -0
- package/dist/core/hands-band.d.ts +134 -0
- package/dist/core/hands-band.js +1 -0
- package/dist/core/hooks.d.ts +22 -177
- package/dist/core/hooks.js +53 -851
- package/dist/core/mcp-failure.d.ts +142 -0
- package/dist/core/mcp-failure.js +145 -0
- package/dist/core/mcp-server-spec.d.ts +217 -0
- package/dist/core/mcp-server-spec.js +1 -0
- package/dist/core/mcp.d.ts +21 -77
- package/dist/core/mcp.js +76 -150
- package/dist/core/model-seat.d.ts +99 -0
- package/dist/core/model-seat.js +1 -0
- package/dist/core/pause-registry.d.ts +131 -0
- package/dist/core/pause-registry.js +27 -0
- package/dist/core/reminder-mint.d.ts +10 -0
- package/dist/core/reminder-mint.js +3 -0
- package/dist/core/runner/assemble-result.d.ts +32 -41
- package/dist/core/runner/assemble-result.js +55 -74
- package/dist/core/runner/contracts.d.ts +427 -69
- package/dist/core/runner/denial-limit-arms.d.ts +1 -1
- package/dist/core/runner/denial-limit-arms.js +3 -3
- package/dist/core/runner/gate-exit.d.ts +242 -0
- package/dist/core/runner/gate-exit.js +124 -0
- package/dist/core/runner/park-commit.d.ts +17 -23
- package/dist/core/runner/park-commit.js +14 -15
- package/dist/core/runner/prepare-ask-lane.d.ts +0 -3
- package/dist/core/runner/prepare-ask-lane.js +3 -5
- package/dist/core/runner/prepare-boundary-parks.d.ts +3 -6
- package/dist/core/runner/prepare-boundary-parks.js +3 -3
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +2 -7
- package/dist/core/runner/prepare-caps-and-workflow.js +1 -1
- package/dist/core/runner/prepare-delegation-surface.d.ts +2 -7
- package/dist/core/runner/prepare-gate-stations.d.ts +4 -7
- package/dist/core/runner/prepare-gate-stations.js +29 -54
- package/dist/core/runner/prepare-inherited-gate.js +1 -1
- package/dist/core/runner/prepare-memory.d.ts +44 -26
- package/dist/core/runner/prepare-park-ask.d.ts +2 -4
- package/dist/core/runner/prepare-park-ask.js +5 -5
- package/dist/core/runner/prepare-task.d.ts +2 -2
- package/dist/core/runner/prepare-task.js +8 -9
- package/dist/core/runner/prepare-wiring-manifest.d.ts +7 -15
- package/dist/core/runner/prepare-wiring-manifest.js +9 -10
- package/dist/core/runner/runtask.d.ts +20 -102
- package/dist/core/runner/runtask.js +119 -121
- package/dist/core/runner/terminal-projection.d.ts +22 -0
- package/dist/core/runner/terminal-projection.js +28 -0
- 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/store-contracts/checkpoint-store-contract.d.ts +4 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +8 -2
- 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/terminal-cause.d.ts +137 -0
- package/dist/core/terminal-cause.js +9 -0
- package/dist/core/tool-policy.d.ts +43 -139
- package/dist/core/tool-policy.js +79 -112
- package/dist/core/tool-spec.d.ts +1174 -0
- package/dist/core/tool-spec.js +1 -0
- package/dist/core/types.d.ts +27 -7789
- package/dist/core/types.js +2 -76
- package/dist/core/warm-resume.d.ts +2 -2
- package/dist/core/wiring-manifest.d.ts +6 -3
- package/dist/core/workflow-journal-store.js +3 -4
- package/dist/engine/harness/agent-harness.d.ts +1 -1
- package/dist/index.d.ts +12 -8
- package/dist/index.js +9 -6
- package/dist/orchestration/builtin-workflows.d.ts +2 -2
- package/dist/orchestration/builtin-workflows.js +1 -1
- package/dist/orchestration/goal.d.ts +2 -2
- package/dist/orchestration/goal.js +8 -7
- package/dist/orchestration/run-spec.d.ts +2 -2
- package/dist/orchestration/run-spec.js +5 -3
- package/dist/orchestration/run-workflow-tool.d.ts +4 -4
- package/dist/orchestration/run-workflow-tool.js +4 -4
- package/dist/orchestration/workflow-governance.d.ts +4 -4
- package/dist/orchestration/workflow-governance.js +4 -2
- package/dist/orchestration/workflow-primitives.d.ts +1 -1
- package/dist/orchestration/workflow-primitives.js +1 -1
- package/dist/orchestration/workflow.d.ts +15 -4
- package/dist/orchestration/workflow.js +64 -39
- package/dist/prompts/supervisor.d.ts +1 -1
- package/dist/prompts/supervisor.js +3 -3
- package/dist/scenarios/scenario-registry.d.ts +3 -3
- package/dist/scenarios/scenario-registry.js +1 -1
- 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 +3 -1
- package/test/export-surface.snapshot.json +82 -22
|
@@ -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
|
+
}
|