@sema-agent/core 5.37.0 → 5.38.0

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 (39) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/agents/subagent.js +6 -0
  3. package/dist/agents/teacher.js +3 -0
  4. package/dist/agents/team.d.ts +7 -1
  5. package/dist/agents/team.js +11 -9
  6. package/dist/agents/verify.js +3 -0
  7. package/dist/core/auto-mode-prompt-assets.d.ts +5 -3
  8. package/dist/core/auto-mode-prompt-assets.js +1 -1
  9. package/dist/core/checkpoint-store.d.ts +26 -1
  10. package/dist/core/hooks.d.ts +129 -2
  11. package/dist/core/hooks.js +20 -3
  12. package/dist/core/runner/prepare-config-doors.d.ts +17 -0
  13. package/dist/core/runner/prepare-config-doors.js +33 -2
  14. package/dist/core/runner/prepare-task.d.ts +17 -2
  15. package/dist/core/runner/prepare-task.js +124 -41
  16. package/dist/core/runner/runtask.js +46 -11
  17. package/dist/core/tool-model-gate.d.ts +125 -0
  18. package/dist/core/tool-model-gate.js +303 -0
  19. package/dist/core/tool-policy.d.ts +1 -1
  20. package/dist/core/types.d.ts +189 -1
  21. package/dist/core/types.js +21 -0
  22. package/dist/core/untrusted-text.d.ts +1 -1
  23. package/dist/index.d.ts +4 -3
  24. package/dist/index.js +2 -1
  25. package/dist/orchestration/builtin-workflows.d.ts +68 -6
  26. package/dist/orchestration/builtin-workflows.js +26 -9
  27. package/dist/orchestration/run-workflow-tool.d.ts +10 -1
  28. package/dist/orchestration/run-workflow-tool.js +70 -27
  29. package/dist/orchestration/workflow-script-store.d.ts +8 -3
  30. package/dist/prompts/coordinator.d.ts +4 -1
  31. package/dist/prompts/coordinator.js +8 -0
  32. package/dist/prompts/default.d.ts +14 -4
  33. package/dist/prompts/default.js +2 -1
  34. package/dist/scenarios/full-body.d.ts +5 -0
  35. package/dist/scenarios/full-body.js +8 -4
  36. package/dist/tools/fs/fs-shared.d.ts +3 -2
  37. package/dist/tools/fs/fs-shared.js +19 -9
  38. package/package.json +1 -1
  39. package/test/export-surface.snapshot.json +12 -1
@@ -0,0 +1,303 @@
1
+ export function modelIdTail(id) {
2
+ return id.toLowerCase().split("/").pop() ?? "";
3
+ }
4
+ export const TOOL_MODEL_GATE_CLASSES = Object.freeze({
5
+ "task-scaffold": Object.freeze({
6
+ floors: Object.freeze([
7
+ Object.freeze(["opus", Object.freeze([4, 8])]),
8
+ Object.freeze(["sonnet", Object.freeze([5])]),
9
+ Object.freeze(["fable", Object.freeze([5])]),
10
+ Object.freeze(["mythos", Object.freeze([5])]),
11
+ ]),
12
+ }),
13
+ });
14
+ const CLAUDE_ID_RE = /^claude-([a-z]+)-(\d+(?:-\d+)*)$/;
15
+ function versionTupleGte(version, floor) {
16
+ const n = Math.max(version.length, floor.length);
17
+ for (let i = 0; i < n; i++) {
18
+ const v = version[i] ?? 0;
19
+ const f = floor[i] ?? 0;
20
+ if (v > f)
21
+ return true;
22
+ if (v < f)
23
+ return false;
24
+ }
25
+ return true;
26
+ }
27
+ export function isModelGatedForClass(modelId, rule) {
28
+ const tail = modelIdTail(modelId);
29
+ if (rule.modelIds !== undefined) {
30
+ for (const row of rule.modelIds) {
31
+ if (typeof row === "string" && tail === row.toLowerCase())
32
+ return true;
33
+ }
34
+ }
35
+ if (rule.floors !== undefined) {
36
+ const m = CLAUDE_ID_RE.exec(tail);
37
+ if (m !== null) {
38
+ const family = m[1] ?? "";
39
+ const version = (m[2] ?? "").split("-").map(Number);
40
+ for (const row of rule.floors) {
41
+ if (!Array.isArray(row) || row.length !== 2)
42
+ continue;
43
+ const [fam, floor] = row;
44
+ if (typeof fam !== "string" || fam.toLowerCase() !== family)
45
+ continue;
46
+ if (Array.isArray(floor) && versionTupleGte(version, floor))
47
+ return true;
48
+ }
49
+ }
50
+ }
51
+ return false;
52
+ }
53
+ function gateConfigError(message) {
54
+ const e = new Error(message);
55
+ e.code = "config.tool_model_gate_invalid";
56
+ return e;
57
+ }
58
+ export function assertRestoreGatedToolsValue(value) {
59
+ if (value === undefined || value === true)
60
+ return;
61
+ if (Array.isArray(value)) {
62
+ const entries = value;
63
+ for (const entry of entries) {
64
+ if (typeof entry !== "string") {
65
+ throw gateConfigError(`TaskSpec.restoreGatedTools entries must be strings (got ${describeValue(entry)}) — an unevaluable selector must not silently restore nothing.`);
66
+ }
67
+ }
68
+ return;
69
+ }
70
+ throw gateConfigError(`TaskSpec.restoreGatedTools must be \`true\` or an array of tool names (got ${describeValue(value)}).`);
71
+ }
72
+ const isPlainObject = (v) => {
73
+ if (typeof v !== "object" || v === null || Array.isArray(v))
74
+ return false;
75
+ const proto = Object.getPrototypeOf(v);
76
+ return proto === Object.prototype || proto === null;
77
+ };
78
+ const describeValue = (v) => {
79
+ try {
80
+ const s = JSON.stringify(v);
81
+ return s === undefined ? String(v) : s;
82
+ }
83
+ catch {
84
+ return typeof v === "bigint" ? `${String(v)}n` : "[unserializable value]";
85
+ }
86
+ };
87
+ function mergeToolModelGateClasses(depsSeat) {
88
+ const merged = new Map();
89
+ for (const [cls, rule] of Object.entries(TOOL_MODEL_GATE_CLASSES))
90
+ merged.set(cls, rule);
91
+ if (depsSeat === undefined)
92
+ return merged;
93
+ if (!isPlainObject(depsSeat)) {
94
+ throw gateConfigError(`RunnerDeps.toolModelGate must be \`false\` or a plain object — prototype included (got ${Array.isArray(depsSeat) ? "an array" : describeValue(depsSeat)}).`);
95
+ }
96
+ for (const key of Object.keys(depsSeat)) {
97
+ if (key !== "classes") {
98
+ throw gateConfigError(`RunnerDeps.toolModelGate has unknown key ${JSON.stringify(key)} — the only legal key is "classes".`);
99
+ }
100
+ }
101
+ const classes = Object.hasOwn(depsSeat, "classes") ? depsSeat.classes : undefined;
102
+ if (classes === undefined)
103
+ return merged;
104
+ if (!isPlainObject(classes)) {
105
+ throw gateConfigError(`RunnerDeps.toolModelGate.classes must be a plain object (got ${Array.isArray(classes) ? "an array" : describeValue(classes)}).`);
106
+ }
107
+ for (const [cls, row] of Object.entries(classes)) {
108
+ if (cls === "")
109
+ throw gateConfigError("RunnerDeps.toolModelGate.classes has an empty-string class name.");
110
+ if (row === undefined)
111
+ continue;
112
+ if (!isPlainObject(row)) {
113
+ throw gateConfigError(`RunnerDeps.toolModelGate.classes[${JSON.stringify(cls)}] must be a plain object (got ${Array.isArray(row) ? "an array" : describeValue(row)}).`);
114
+ }
115
+ for (const key of Object.keys(row)) {
116
+ if (key !== "floors" && key !== "modelIds") {
117
+ throw gateConfigError(`RunnerDeps.toolModelGate.classes[${JSON.stringify(cls)}] has unknown key ${JSON.stringify(key)} — legal keys: "floors", "modelIds".`);
118
+ }
119
+ }
120
+ let floors;
121
+ if (row.floors !== undefined) {
122
+ if (!Array.isArray(row.floors)) {
123
+ throw gateConfigError(`RunnerDeps.toolModelGate.classes[${JSON.stringify(cls)}].floors must be an array (got ${describeValue(row.floors)}).`);
124
+ }
125
+ const seen = new Set();
126
+ const out = [];
127
+ const rows = row.floors;
128
+ for (const entry of rows) {
129
+ if (!Array.isArray(entry) || entry.length !== 2) {
130
+ throw gateConfigError(`RunnerDeps.toolModelGate.classes[${JSON.stringify(cls)}].floors rows must be [family, floor] pairs (got ${describeValue(entry)}).`);
131
+ }
132
+ const famRaw = entry[0];
133
+ const floorRaw = entry[1];
134
+ if (typeof famRaw !== "string" || famRaw.length === 0) {
135
+ throw gateConfigError(`RunnerDeps.toolModelGate.classes[${JSON.stringify(cls)}].floors family must be a non-empty string (got ${describeValue(famRaw)}).`);
136
+ }
137
+ const fam = famRaw.toLowerCase();
138
+ if (!/^[a-z]+$/.test(fam)) {
139
+ throw gateConfigError(`RunnerDeps.toolModelGate.classes[${JSON.stringify(cls)}].floors family ${JSON.stringify(famRaw)} cannot ever match — the canonical id shape only admits letters. Refused rather than kept as a dead row.`);
140
+ }
141
+ if (seen.has(fam)) {
142
+ throw gateConfigError(`RunnerDeps.toolModelGate.classes[${JSON.stringify(cls)}].floors lists family ${JSON.stringify(fam)} twice.`);
143
+ }
144
+ seen.add(fam);
145
+ const floorNums = [];
146
+ if (Array.isArray(floorRaw)) {
147
+ const floorEntries = floorRaw;
148
+ for (const n of floorEntries) {
149
+ if (typeof n === "number" && Number.isSafeInteger(n) && n >= 0)
150
+ floorNums.push(n);
151
+ }
152
+ }
153
+ if (!Array.isArray(floorRaw) || floorRaw.length === 0 || floorNums.length !== floorRaw.length) {
154
+ throw gateConfigError(`RunnerDeps.toolModelGate.classes[${JSON.stringify(cls)}].floors floor for ${JSON.stringify(fam)} must be a non-empty array of non-negative safe integers (got ${describeValue(floorRaw)}).`);
155
+ }
156
+ out.push([fam, floorNums]);
157
+ }
158
+ floors = out;
159
+ }
160
+ let modelIds;
161
+ if (row.modelIds !== undefined) {
162
+ if (!Array.isArray(row.modelIds)) {
163
+ throw gateConfigError(`RunnerDeps.toolModelGate.classes[${JSON.stringify(cls)}].modelIds must be an array (got ${describeValue(row.modelIds)}).`);
164
+ }
165
+ const seen = new Set();
166
+ const out = [];
167
+ const ids = row.modelIds;
168
+ for (const entry of ids) {
169
+ if (typeof entry !== "string" || entry.length === 0) {
170
+ throw gateConfigError(`RunnerDeps.toolModelGate.classes[${JSON.stringify(cls)}].modelIds entries must be non-empty strings (got ${describeValue(entry)}).`);
171
+ }
172
+ const folded = entry.toLowerCase();
173
+ if (folded.includes("/")) {
174
+ throw gateConfigError(`RunnerDeps.toolModelGate.classes[${JSON.stringify(cls)}].modelIds entry ${JSON.stringify(entry)} contains "/" — matching is against the id's TAIL segment, so a prefixed row can never fire. Write the bare id.`);
175
+ }
176
+ if (seen.has(folded)) {
177
+ throw gateConfigError(`RunnerDeps.toolModelGate.classes[${JSON.stringify(cls)}].modelIds lists ${JSON.stringify(folded)} twice (case-folded).`);
178
+ }
179
+ seen.add(folded);
180
+ out.push(folded);
181
+ }
182
+ modelIds = out;
183
+ }
184
+ const base = merged.get(cls);
185
+ merged.set(cls, {
186
+ ...(floors !== undefined ? { floors } : base?.floors !== undefined ? { floors: base.floors } : {}),
187
+ ...(modelIds !== undefined ? { modelIds } : base?.modelIds !== undefined ? { modelIds: base.modelIds } : {}),
188
+ });
189
+ }
190
+ return merged;
191
+ }
192
+ const ENV_OFF = new Set(["off", "0", "false"]);
193
+ const ENV_ON = new Set(["on", "1", "true"]);
194
+ export function applyToolModelGate(input) {
195
+ const noop = (discardedEnvRaw) => ({
196
+ survivors: undefined,
197
+ removedByClass: new Map(),
198
+ unknownClasses: [],
199
+ discardedEnvRaw,
200
+ });
201
+ let merged;
202
+ try {
203
+ if (input.depsSeat !== false && input.depsSeat !== undefined && !isPlainObject(input.depsSeat)) {
204
+ throw gateConfigError(`RunnerDeps.toolModelGate must be \`false\` or a plain object — prototype included (got ${describeValue(input.depsSeat)}).`);
205
+ }
206
+ merged = input.depsSeat === false ? undefined : mergeToolModelGateClasses(input.depsSeat);
207
+ }
208
+ catch (e) {
209
+ let code;
210
+ try {
211
+ code = e?.code;
212
+ }
213
+ catch {
214
+ code = undefined;
215
+ }
216
+ if (code !== undefined)
217
+ throw e;
218
+ let msg;
219
+ try {
220
+ msg = e instanceof Error ? e.message : String(e);
221
+ }
222
+ catch {
223
+ msg = "[unreportable throw]";
224
+ }
225
+ throw gateConfigError(`RunnerDeps.toolModelGate could not be read (${msg}) — a config seat whose reads throw is a bad value, refused rather than passed.`);
226
+ }
227
+ const raw = input.envRaw;
228
+ let envVerdict;
229
+ if (raw === undefined)
230
+ envVerdict = "absent";
231
+ else {
232
+ const tok = raw.toLowerCase();
233
+ envVerdict = ENV_OFF.has(tok) ? "off" : ENV_ON.has(tok) ? "on" : "invalid";
234
+ }
235
+ const invalidRaw = envVerdict === "invalid" ? raw : undefined;
236
+ if (merged === undefined)
237
+ return noop(invalidRaw);
238
+ if (envVerdict === "off")
239
+ return noop();
240
+ const tools = input.tools ?? [];
241
+ const stamped = [];
242
+ for (const entry of tools) {
243
+ if (entry.modelGate !== undefined)
244
+ stamped.push({ entry, cls: entry.modelGate });
245
+ }
246
+ if (stamped.length === 0)
247
+ return noop(invalidRaw);
248
+ const restoredClasses = new Set();
249
+ if (input.restoreGated === true) {
250
+ for (const s of stamped)
251
+ restoredClasses.add(s.cls);
252
+ }
253
+ else if (input.restoreGated !== undefined) {
254
+ const names = new Set(input.restoreGated);
255
+ for (const s of stamped)
256
+ if (names.has(s.entry.name))
257
+ restoredClasses.add(s.cls);
258
+ }
259
+ const unknownClasses = [];
260
+ const unknownSeen = new Set();
261
+ const removed = new Set();
262
+ const removedByClass = new Map();
263
+ for (const { entry, cls } of stamped) {
264
+ const rule = merged.get(cls);
265
+ if (rule === undefined) {
266
+ if (!unknownSeen.has(cls)) {
267
+ unknownSeen.add(cls);
268
+ unknownClasses.push(cls);
269
+ }
270
+ continue;
271
+ }
272
+ if (rule.floors === undefined && rule.modelIds === undefined)
273
+ continue;
274
+ if (restoredClasses.has(cls))
275
+ continue;
276
+ if (!isModelGatedForClass(input.modelId, rule))
277
+ continue;
278
+ removed.add(entry);
279
+ const list = removedByClass.get(cls) ?? [];
280
+ if (!list.includes(entry.name))
281
+ list.push(entry.name);
282
+ removedByClass.set(cls, list);
283
+ }
284
+ if (invalidRaw !== undefined) {
285
+ if (removed.size > 0) {
286
+ const e = new Error(`SEMA_TOOL_MODEL_GATE=${JSON.stringify(invalidRaw)} is not in the closed set (on|1|true|off|0|false, case-insensitive) and IS in force on this task (the model gate would remove default-mounted tool(s)). Refused rather than guessed — "off" mistyped must not silently keep the trim armed.`);
287
+ e.code = "config.tool_model_gate_env_invalid";
288
+ throw e;
289
+ }
290
+ return { survivors: undefined, removedByClass: new Map(), unknownClasses, discardedEnvRaw: invalidRaw };
291
+ }
292
+ if (removed.size === 0) {
293
+ return { survivors: undefined, removedByClass: new Map(), unknownClasses, discardedEnvRaw: undefined };
294
+ }
295
+ for (const list of removedByClass.values())
296
+ list.sort();
297
+ return {
298
+ survivors: tools.filter((t) => !removed.has(t)),
299
+ removedByClass,
300
+ unknownClasses,
301
+ discardedEnvRaw: undefined,
302
+ };
303
+ }
@@ -866,7 +866,7 @@ export interface AskRequest {
866
866
  * run's model: a parent-thread run is told to stop and wait for the user (its transcript has a
867
867
  * user turn coming), a delegated child is told to adapt or report the limitation. A fork is
868
868
  * deliberately IN: it inherits the parent's authority (design/110 — which is why the RB-330
869
- * `isDelegatedNonForkChild` derivation, serving the authority/context faces, excludes it), but
869
+ * non-fork facet of `effectiveDelegationFacts`, serving the authority/context faces, excludes it), but
870
870
  * its interaction contract is one-shot — "report once and stop … no waiting for the user"
871
871
  * (FORK_DIRECTIVE_FRAME) — so a stop-and-wait refusal would instruct it to do the impossible
872
872
  * (codex adversarial round, confirmed). NOT the same fact as {@link fromSubagent} either: that is
@@ -381,6 +381,25 @@ export interface ToolSpec<TParams extends TSchema = TSchema> {
381
381
  * Exclusion still wins ({@link TaskSpec.excludeTools} unmounts — nothing left to keep inline).
382
382
  */
383
383
  alwaysLoad?: boolean;
384
+ /**
385
+ * design/277 — model-gate CLASS tag (open vocabulary; the built-in table is
386
+ * {@link import("./tool-model-gate.js").TOOL_MODEL_GATE_CLASSES}, v1 vocabulary =
387
+ * `"task-scaffold"`). A tagged entry declares "I am a default-mounted scaffold of this class":
388
+ * at prepare, when the task's RESOLVED model id matches the class's rule, THIS entry is dropped
389
+ * from the roster (true unmount, entry-level — a same-name untagged entry is untouched; the
390
+ * removed entry stops occupying its name on every downstream surface). Untagged = never gated.
391
+ * The default-bundle assemblers stamp their default arms only; an EXPLICITLY composed tool is
392
+ * not tagged (user asked ⇒ user gets) — so don't stamp hand-mounted factories. FAIL-OPEN: an id
393
+ * the merged table knows nothing about is never gated (BYOM open set — the table encodes
394
+ * positive knowledge only), and a tag naming an unknown class is inert + announced
395
+ * (`config.tool_model_gate_unknown_class`). Restore channels: explicit composition (no tag),
396
+ * {@link TaskSpec.restoreGatedTools}, env `SEMA_TOOL_MODEL_GATE=off`,
397
+ * `RunnerDeps.toolModelGate: false`; {@link TaskSpec.excludeTools} always wins regardless.
398
+ * The materialized `AgentTool` face does NOT carry this field (`defineTool` is a whitelist
399
+ * constructor) — the decision completes on the ToolSpec face inside `prepareConfigDoors`,
400
+ * before any conversion.
401
+ */
402
+ modelGate?: string;
384
403
  /**
385
404
  * Tool contract identity (campaign S2, prompt-assembly protocol §7): declares the EXECUTION
386
405
  * CONTRACT this tool implements, independent of its presentation text. `defineTool` attaches it
@@ -751,6 +770,13 @@ export interface ToolExecuteContext {
751
770
  * X arrives deferred, the exemption having been dropped in transit — which is the one outcome the
752
771
  * parent explicitly ruled out. Children merge this with their own spec value. */
753
772
  alwaysLoadTools?: readonly string[];
773
+ /** design/277 — the model-gate restore selector ({@link TaskSpec.restoreGatedTools}), inherited
774
+ * down the delegation tree on the same trusted seat as the three tool-face controls above
775
+ * (Runner-filled, read-only, frozen snapshot). Restoration is a TASK-TREE intent ("this work
776
+ * wants the scaffold back") and each child re-judges the gate under its OWN resolved model —
777
+ * dropping the selector in transit would trim a same-model child's roster in a way nobody
778
+ * chose (the gate is a default, not a policy; there is no tighten-only axis to protect). */
779
+ restoreGatedTools?: readonly string[] | true;
754
780
  /** R2 双形轴 — the parent's resolved prompt profile, inherited down the delegation tree like the
755
781
  * tool-face controls (Runner-filled): a classic-profile parent's children speak classic too
756
782
  * unless the child spec says otherwise (child spec wins — profile is presentation, not policy). */
@@ -1867,6 +1893,24 @@ export interface TaskSpec {
1867
1893
  * wins — an unmounted tool has no schema to keep inline).
1868
1894
  */
1869
1895
  alwaysLoadTools?: string[];
1896
+ /**
1897
+ * design/277 — per-task restore valve for the tool-registration MODEL GATE. The gate trims
1898
+ * default-mounted scaffold entries (tagged via {@link ToolSpec.modelGate}) from the roster when
1899
+ * the task's resolved model id matches the gate table — this valve opts the task (and its whole
1900
+ * delegation tree — the selector inherits down like `excludeTools`) back in:
1901
+ * · an ARRAY of wire names — naming ANY tagged tool restores that tool's WHOLE class for this
1902
+ * task (CC parity: opting into any member of the family restores the family). Judged against
1903
+ * the full stamp set, so a name that `excludeTools` also lists still works as a class
1904
+ * SELECTOR — while the exclusion itself still wins for that name (exclusion is the final
1905
+ * valve; no restore channel resurrects an excluded name). Unknown names are inert (a
1906
+ * center-distributed list may be a superset), same posture as `excludeTools`.
1907
+ * · literal `true` — every gate class restored for this task (the shortest "this task wants
1908
+ * all its scaffolding" spelling for center-distributed specs).
1909
+ * A malformed value refuses at prepare (`config.tool_model_gate_invalid`, #123) — garbage must
1910
+ * not silently read as "restore nothing" in the trimming direction. Absent = the gate's verdict
1911
+ * stands. Explicit composition needs no valve: a hand-mounted (untagged) tool is never gated.
1912
+ */
1913
+ restoreGatedTools?: string[] | true;
1870
1914
  /**
1871
1915
  * RB-403 — whether a schema-VALID call on a still-deferred tool runs the real tool directly
1872
1916
  * (activating it as a side effect). Default `true`, matching the upstream posture where the
@@ -4440,6 +4484,93 @@ export interface BackgroundChildEvent {
4440
4484
  costMicroUsd?: number;
4441
4485
  };
4442
4486
  }
4487
+ /**
4488
+ * #281 件B — one lifecycle phase of a DELEGATED child leg, delivered to the process-level
4489
+ * {@link RunnerDeps.onDelegationLifecycle} observer: the public, deps-level home of the delegation
4490
+ * lifecycle that previously lived only on the trusted RunInternals third parameter
4491
+ * (`onSubagentSpawn` — sync lane, a steer handle whose `settled` is a void promise) and on the
4492
+ * background-lane-only {@link BackgroundChildEvent}. A deployment wired ONLY through `RunnerDeps`
4493
+ * now sees every delegation lane through one seat.
4494
+ *
4495
+ * EMISSION CHOKEPOINT (the reason this seat covers every lane at once): frames are minted by the
4496
+ * RUNNER at the child LEG itself — spawn right after the leg's `wiring_manifest` (prepared, nothing
4497
+ * run yet), terminal when the leg's `TaskResult` assembles — not by the individual spawn lanes. Every
4498
+ * delegation lane (sync/steer/background/fork/revive, workflow-spawned agents) runs its child through
4499
+ * this chokepoint, so none of them needs its own emission and none can drift.
4500
+ *
4501
+ * PER-LEG semantics, deliberately: a durable park + resume, or a retained child's revive, is a NEW
4502
+ * leg — each emits its own spawn/terminal pair, and `identity.legKind` (`"resume"`) says which cycle
4503
+ * a frame belongs to. Consumers correlate legs of one delegation by `identity.taskId` /
4504
+ * `identity.parentToolCallId` (stable across cycles).
4505
+ *
4506
+ * HONEST ABSENCES (recorded, not gaps to fix silently):
4507
+ * · a child whose PREPARE throws emits neither frame (there is no leg identity to report);
4508
+ * · the ROOT leg emits nothing here — the deployment called `runTask` itself and holds the result;
4509
+ * this seat is the delegation observer, not a run observer;
4510
+ * · a deps-only resume (`resume(token, outcome, config)` — no trusted internals re-supplied) of a
4511
+ * checkpoint minted BEFORE the delegation axis was persisted
4512
+ * ({@link import("./checkpoint-store.js").CheckpointState.isDelegatedChild}) emits neither frame:
4513
+ * the row carries no evidence the parked leg was a delegated child, and fabricating the axis
4514
+ * would stamp delegation frames onto host-resumed root tasks. Rows minted WITH the axis resume
4515
+ * with their spawn/terminal pair even deps-only; a resume that re-supplies trusted internals
4516
+ * (every in-engine lane does) was never affected.
4517
+ * PAIRING (codex r2-D2 closed the one hole): a spawn frame is always closed by a terminal frame —
4518
+ * the ordinary path emits it where the `TaskResult` assembles, and a POST-SPAWN throw that the
4519
+ * stream layer converts into a synthesized failed result (e.g. `resume.tool_unavailable` re-thrown
4520
+ * past the run tail) emits the failed terminal from that backstop, gated on a carrier that is set
4521
+ * only after the spawn emission and cleared by the ordinary terminal (so the two sites can never
4522
+ * both fire for one leg). A leg whose PROCESS dies mid-flight is the only unpaired spawn.
4523
+ *
4524
+ * OBSERVATION ONLY (matrix §5.2 Q3: frame-rate lifecycle facts ride an observer seat, never
4525
+ * onNotice): no return capability, and delivery can never alter the child run —
4526
+ * {@link deliverDelegationLifecycle} contains a throwing sink and an async sink's rejection alike.
4527
+ *
4528
+ * FRAME OBJECT SHAPE (consumer contract, not an implementation detail): every delivered frame is
4529
+ * FROZEN and carries a NULL PROTOTYPE — re-minted at the one delivery point so a forged member
4530
+ * cannot ride a frame through a writable `Object.prototype`, the same rule (and the same trade) as
4531
+ * the {@link import("./hooks.js").HookInvocationIdentity} envelope inside it. `Object.keys`, spread,
4532
+ * JSON serialization and direct member reads (`frame.status`) all behave normally, but
4533
+ * `frame instanceof Object` is `false` and inherited methods are ABSENT — probe optional members
4534
+ * with `Object.hasOwn(frame, "errorCode")` or `"errorCode" in frame`, never
4535
+ * `frame.hasOwnProperty(...)` (throws) or implicit string coercion (`` `${frame}` `` throws).
4536
+ */
4537
+ export type DelegationLifecycleEvent = {
4538
+ phase: "spawn";
4539
+ /** The child LEG's identity envelope (#281 件A — the same frozen object that leg's own hook
4540
+ * invocations carry). `isDelegatedChild` is `true` by construction on every frame here. */
4541
+ identity: import("./hooks.js").HookInvocationIdentity;
4542
+ } | {
4543
+ phase: "terminal";
4544
+ /** Same envelope as the leg's spawn frame (one mint per leg). */
4545
+ identity: import("./hooks.js").HookInvocationIdentity;
4546
+ /** The leg's settled status — the `TaskResult.status` the spawning lane receives, verbatim
4547
+ * (the sync lane's previously-void `settled` payload, made public). `"suspended"` means a
4548
+ * durable park: expect a later `"resume"`-leg spawn/terminal pair if it is redeemed. */
4549
+ status: TaskResult["status"];
4550
+ /** Turns the leg completed. */
4551
+ turns: number;
4552
+ /** The leg's `TaskResult.errorCode`, when one was stamped. */
4553
+ errorCode?: string;
4554
+ };
4555
+ /** Test seam (mirrors `__resetMalformedNoticeSeatAnnouncement`): never called by production code. */
4556
+ export declare function __resetMalformedDelegationSeatAnnouncement(): void;
4557
+ /**
4558
+ * The ONE delivery form behind every {@link RunnerDeps.onDelegationLifecycle} emission point (both
4559
+ * runner stations — spawn and terminal — call this; a second spelling of the swallow/announce rules
4560
+ * would be the #170 triplication reborn). Contract:
4561
+ * · a FUNCTION seat is invoked with the frame FROZEN (a mutating observer must not rewrite what a
4562
+ * later frame consumer — or the shared identity envelope's other readers — see), contained in the
4563
+ * caller's {@link SafeNotifier} against BOTH failure shapes the void-typed seat admits: a
4564
+ * synchronous throw ({@link SafeNotifier.notify}) and an async sink's rejected promise
4565
+ * ({@link observeThenableRejection} routes it back through the same notifier/site — the #253
4566
+ * three-station form). A broken observer never faults the child run.
4567
+ * · a PRESENT NON-function seat is a bad deployment value; #123 forbids folding it to silence. There
4568
+ * is no per-frame console fallback (this is a frame-rate observer stream, not an announcement
4569
+ * channel — an unwired seat means UNOBSERVED, and echoing every spawn to stderr would flood), so
4570
+ * the loud exit is the seat DEFECT itself: announced via `console.warn` once per process.
4571
+ * · an ABSENT seat is a plain no-op (the deployment chose not to observe).
4572
+ */
4573
+ export declare function deliverDelegationLifecycle(seat: RunnerDeps["onDelegationLifecycle"], event: DelegationLifecycleEvent, notifier: import("./safe-notify.js").SafeNotifier, site: string): void;
4443
4574
  /**
4444
4575
  * parity-204 — structured `RunnerDeps.loadProjectMemory` return (backward-compatible: a bare
4445
4576
  * `string | null` keeps its exact historical meaning). Adds the CC 2.1.204 `seededFromContext`
@@ -4519,6 +4650,24 @@ export interface EngineNotice {
4519
4650
  * a library-direct `createHandsToolkit` mount announces at toolkit creation (one per mount,
4520
4651
  * through the band-local `HandsToolkitOptions.onNotice` seat, absent ⇒ `console.warn`);
4521
4652
  * `detail: { seat, declared, inForce, cause }`.
4653
+ * - `"config.tool_model_gate_removed"` (design/277) — the model gate trimmed default-mounted
4654
+ * scaffold entries from a task's roster. One notice per (model, class), de-duplicated once per
4655
+ * `onNotice` SINK on the (modelId, class, canonical sorted removed-name set) line (same unit
4656
+ * as `config.read_face_deployment_clamped`: two deployments hosted in one process each hear
4657
+ * their own trim; unwired console arm once per process) — same class with a different removal
4658
+ * shape is a distinct fact and announces again;
4659
+ * `detail: { modelId, gateClass, removed, restore }` (`restore` names the three valves).
4660
+ * - `"config.tool_model_gate_unknown_class"` (design/277) — a `ToolSpec.modelGate` tag names a
4661
+ * class the merged gate table has no row for: the tag is inert (fail-open — the tool stays
4662
+ * mounted) and this is its loud half (a tag typo must not silently become "never gated" with
4663
+ * nobody told). Once per `onNotice` sink per class (console arm once per process);
4664
+ * `detail: { gateClass }`.
4665
+ * - `"config.tool_model_gate_env_invalid"` (design/277, the NOTICE dialect of the same fact the
4666
+ * refusal code carries) — `SEMA_TOOL_MODEL_GATE` holds a value outside `on|1|true|off|0|false`
4667
+ * in a seat where it is NOT in force (nothing this prepare would gate): announced once per
4668
+ * `onNotice` sink per value (console arm once per process) instead of lying in wait; where the
4669
+ * value IS in force the prepare refuses with the same code as `TaskResult.errorCode` (one
4670
+ * fact, one code, two loudness dialects); `detail: { raw }`.
4522
4671
  *
4523
4672
  * Deliberately NOT a notice family: brain retry/reconnect liveness (a rate limit, a 5xx, a
4524
4673
  * transient network failure being retried). Those are per-attempt liveness frames with their own
@@ -4874,6 +5023,23 @@ export interface RunnerDeps {
4874
5023
  * completion pushes from it. Observability only — a throwing observer is swallowed, never faults a run.
4875
5024
  */
4876
5025
  onBackgroundChildEvent?: (event: BackgroundChildEvent) => void;
5026
+ /**
5027
+ * #281 件B — PROCESS-level observer for EVERY delegated child leg's lifecycle (`spawn`/`terminal`),
5028
+ * all delegation lanes at once: synchronous and steer-handle delegations (whose spawn/settle
5029
+ * previously reached only the trusted `RunInternals.onSubagentSpawn` third parameter — and whose
5030
+ * `settled` is a void promise), background/fork/revive children (whose {@link BackgroundChildEvent}
5031
+ * family this seat complements, not replaces — BCE stays the fleet-row lane with registry `a*`
5032
+ * handles and ticks; this seat is the leg-identity lane), and workflow-spawned agents. Frames are
5033
+ * minted at the child leg's own runner chokepoint and carry the #281 件A identity envelope plus a
5034
+ * terminal status summary — see {@link DelegationLifecycleEvent} for the emission points, the
5035
+ * per-leg semantics and the recorded honest absences. Wire it once at Runner construction; a
5036
+ * deployment needs NO RunInternals access to observe delegation any more. Observation only — a
5037
+ * throwing or rejecting observer is contained ({@link deliverDelegationLifecycle}) and never
5038
+ * faults the child run; a PRESENT non-function value here is announced once per process and the
5039
+ * frames are simply not delivered (#123 — a bad seat must be loud, and this stream's loud exit is
5040
+ * the seat defect, not a per-frame console flood).
5041
+ */
5042
+ onDelegationLifecycle?: (event: DelegationLifecycleEvent) => void;
4877
5043
  /**
4878
5044
  * design/98 (S8) — the HARD sandbox seam for LLM-AUTHORED workflow scripts (`TaskSpec.selfOrchestration`).
4879
5045
  * A deployment supplies an isolated-vm / separate-process runner whose `safeForUntrustedScripts === true`;
@@ -4961,7 +5127,7 @@ export interface RunnerDeps {
4961
5127
  * resolves a saved workflow, and a registration's `defaultArgs` merge under the call-time args. Opt-in
4962
5128
  * (absent ⇒ inline `script` + built-in names only). */
4963
5129
  workflowScriptStore?: import("../orchestration/workflow-script-store.js").WorkflowScriptStore;
4964
- /** design/140 §6 1c — `false` removes the BUILT-IN named workflows (`team-discussion`, …) from the
5130
+ /** design/140 §6 1c — `false` removes the BUILT-IN named workflows (`discussion`, …) from the
4965
5131
  * auto-mounted run_workflow tool (the `builtinAgents:false` analog; default ON). A deployment
4966
5132
  * `workflowScriptStore` registration of the same name shadows a built-in regardless. */
4967
5133
  builtinWorkflows?: boolean;
@@ -5142,6 +5308,28 @@ export interface RunnerDeps {
5142
5308
  mcpImageResizer?: import("./mcp.js").McpImageResizer;
5143
5309
  /** Default tool-call gate for all tasks (a task's own `toolPolicy` overrides this). */
5144
5310
  toolPolicy?: import("./tool-policy.js").ToolPolicy;
5311
+ /**
5312
+ * design/277 — the deployment seat of the tool-registration MODEL GATE (default ON; the gate
5313
+ * itself only ever touches entries tagged via {@link ToolSpec.modelGate}, so an untagged roster
5314
+ * is byte-identical under any value here):
5315
+ * · `false` — deployment kill switch: the gate never trims anything.
5316
+ * · `{ classes }` — per-class rule rows merged into the built-in table
5317
+ * ({@link import("./tool-model-gate.js").TOOL_MODEL_GATE_CLASSES}) PER AXIS: a present
5318
+ * `floors`/`modelIds` axis REPLACES that axis, an absent axis INHERITS the built-in one — so
5319
+ * a row adding only `modelIds` (the BYOM channel for gating a deployment's own strong model)
5320
+ * keeps the built-in claude floors armed. `floors: []` / `modelIds: []` are the EXPLICIT
5321
+ * per-axis clears; a class whose merged rule has both axes empty gates nothing (legal
5322
+ * per-class off). New class names extend the open vocabulary for deployment-authored tags.
5323
+ * The legality set is CLOSED (#123): any other shape — array/null/true seat, unknown keys at
5324
+ * either level, malformed floors/modelIds rows, families that can never match the canonical id
5325
+ * grammar, duplicate families/ids, `/`-prefixed ids — refuses the prepare loudly
5326
+ * (`config.tool_model_gate_invalid`), never folds to a guess. Process-level counterpart: env
5327
+ * `SEMA_TOOL_MODEL_GATE=off`. Removals are announced per (model, class) through `onNotice`
5328
+ * (`config.tool_model_gate_removed`) — a silent default-face trim is forbidden (#237).
5329
+ */
5330
+ toolModelGate?: false | {
5331
+ classes?: Record<string, import("./tool-model-gate.js").ToolModelGateRule>;
5332
+ };
5145
5333
  /** Deployment default for {@link TaskSpec.basePolicyForResumeEdit} (#93 / F-012 L3): the resume-edit
5146
5334
  * re-adjudication override. Resolution: `spec.basePolicyForResumeEdit ?? THIS ?? (spec.toolPolicy ??
5147
5335
  * deps.toolPolicy)` — absence falls back to the caller policy, never to a silent skip. */
@@ -1,3 +1,24 @@
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
+ }
1
22
  let malformedNoticeSeatAnnounced = false;
2
23
  export function __resetMalformedNoticeSeatAnnouncement() {
3
24
  malformedNoticeSeatAnnounced = false;
@@ -21,7 +21,7 @@
21
21
  * model-facing instructions. Always neutralizes `<system-reminder>` / `</system-reminder>` (the codebase's
22
22
  * elevated-authority wrapper) by inserting a zero-width space after the leading `<`. Pass `extraTags` to also
23
23
  * neutralize a CALLER's own data-framing tags — e.g. `team.ts` embeds member output inside `<statement>` /
24
- * `<team-discussion>`, so an untrusted member could emit `</statement>` to break out (search [46] BUG2);
24
+ * `<discussion>`, so an untrusted member could emit `</statement>` to break out (search [46] BUG2);
25
25
  * the caller passes those tag names. Tag names must be literal (alphanumeric/hyphen) — they are code-supplied
26
26
  * wrapper names, never untrusted input. Idempotent for prompt assembly (a defused tag no longer matches).
27
27
  */
package/dist/index.d.ts CHANGED
@@ -21,6 +21,7 @@ export { createWebFetchTool, webFetchToolSpec, htmlToText, type WebFetchConfig,
21
21
  export { createTodoWriteTool } from "./tools/todo.js";
22
22
  export { createTaskListTools, createMemoryTaskListStore, assertJsonMetadata, type TaskListItem, type TaskListStore } from "./tools/task-list.js";
23
23
  export { assembleCodeTools, type CodeToolsConfig, CODE_ROLE } from "./scenarios/full-body.js";
24
+ export { TOOL_MODEL_GATE_CLASSES, isModelGatedForClass, type ToolModelGateRule } from "./core/tool-model-gate.js";
24
25
  export { capAggregateToolResults, AGGREGATE_TOOL_RESULT_BUDGET_CHARS, DEFAULT_BUDGET_EXEMPT_TOOLS, type AggregateBudgetOptions, } from "./core/tool-result-budget.js";
25
26
  export { capAggregateMediaBytes, AGGREGATE_MEDIA_BUDGET_BYTES, type MediaStripInfo } from "./core/media-byte-cap.js";
26
27
  export { createAskUserQuestionTool, createDurableQuestionPolicy, QUESTION_AWAITS_RESUME, isQuestionUnavailable, type OnQuestionOutcome, type QuestionUnavailable, type OnQuestion, type AskQuestion, type AskQuestionOption, type AskQuestionRequest, type QuestionAnswer, type QuestionAnswerItem, type AskQuestionCardDetails, type AskUserQuestionToolOptions, type AskAnswerContinuationSource, type SyntheticContinuationReason, } from "./core/ask-question.js";
@@ -162,7 +163,7 @@ export { FilePermissionRuleStoreProvider } from "./stores/file/permission-rule-s
162
163
  export { adoptFilePermissionRuleStore, type AdoptFileRuleStoreResult } from "./stores/file/permission-rule-adopt.js";
163
164
  export { AdoptionError, assertAdoptionBootGate, readRootAdoptionFile, writeRootAdoptionFile, ROOT_ADOPTION_FILE, type AdoptionErrorCode, type AdoptionSource, type AdoptionReport, type AdoptionReceipt, type AdoptionLegReport, type AffectedDeploymentConfig, type RootAdoptionFile, } from "./stores/file/adoption/marker.js";
164
165
  export { adoptLocalDataRoot, ackAdoptionConfig, witnessAdoptionConfig, listAdoptionQuarantine, readAdoptionStatus, type AdoptionStatus, type AdoptLocalDataRootOptions, type AdoptLocalDataRootResult, type AdoptionCarriageLeg, type AdoptionCarriageLegContext, type AdoptionConfigWitnessReceipt, } from "./stores/file/adoption/adopt.js";
165
- export { formatHookFeedback, runToolGate, createHookEnvCapabilities, createPreToolUseConstraintPolicy, type PersistedRuleHit, type PersistedRuleUnreadable, type PersistedRuleAnswer, normalizePersistedRuleHit, type Hooks, type HookToolContext, type HookEnvCapabilities, type HookToolOutput, type PreToolUseResult, type PostToolUseResult, type UserPromptSubmitResult, type HookToolFailure, type PostToolUseFailureResult, type PostToolBatchCall, type PostToolBatchResult, type PreCompactContext, type PreCompactResult, type PostCompactContext, type StopFailureContext, type PermissionDeniedPayload, type PermissionDeniedSource, } from "./core/hooks.js";
166
+ export { formatHookFeedback, runToolGate, createHookEnvCapabilities, createPreToolUseConstraintPolicy, type PersistedRuleHit, type PersistedRuleUnreadable, type PersistedRuleAnswer, normalizePersistedRuleHit, type Hooks, type HookToolContext, type HookInvocationIdentity, type UserPromptSubmitContext, type PostToolBatchContext, type HookEnvCapabilities, type HookToolOutput, type PreToolUseResult, type PostToolUseResult, type UserPromptSubmitResult, type HookToolFailure, type PostToolUseFailureResult, type PostToolBatchCall, type PostToolBatchResult, type PreCompactContext, type PreCompactResult, type PostCompactContext, type StopFailureContext, type PermissionDeniedPayload, type PermissionDeniedSource, } from "./core/hooks.js";
166
167
  export { InMemoryMemoryStore, composeMemoryBlock, composeLayeredMemoryBlock, normalizeMemorySpec, type NormalizedMemorySpec, type MemorySpecInput, supportsConsolidation, supportsPeriodicConsolidation, firstSentence, expandLexicalTerms, lexicalSearchMatch, type Embedder, classifyPromotable, enforcePromotableWriteGate, guardedMemoryStore, MemoryGateError, detectSecret, enforceSecretWriteGate, enforceStructuredNoteSecretGate, type UtilityGate, type MemoryStore, type MemoryVectorMode, type ScoredMemory, type MemoryNoteHeader, type MemoryNoteRecord, type MemoryNoteType, type StructuredNoteInput, } from "./core/memory.js";
167
168
  export { MemoryEngine, FileMemoryEngineBackend, memoryBackendContract, assertMemoryBackendSearchEquivalence, type MemoryBackendContractHooks, buildMemoryInstruction, truncateIndex, scanEntryFiles, deriveRepoKey, deriveRepoMemoryDir, deriveControlPlaneDir, deriveRepoControlPlaneDir, resolveMemoryEngineRoot, scopeDirName, ControlPlaneCorruptError, parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, readV2HeaderHints, type V2HeaderHints, LINEAGE_FILE, CHALLENGES_FILE, CHALLENGED_HISTORY_FILE, CHALLENGE_LEDGER_MAX_EVENTS, rebuildStrictControlPlaneLedger, type ControlPlaneRebuildReceipt, type StrictControlPlaneLedger, type ChallengeAssignment, type ChallengeEvent, type ChallengedHistoryRow, type LineagePendingTxn, type LineagePromotion, MEMORY_INDEX_FILENAME, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, type MemorySearchDetails, type MemorySearchHit, type MemoryGetDetails, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_MAX_ENTRY_DEPTH, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE, renderAnnouncements, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, type MemoryAnnouncement, type ScanFinding, type MemoryEngineOptions, type MemoryInjection, type EntryProvenanceAccount, type TransferEvidence, type CommittedBinding, type CommittedEntrySnapshot, type CommittedScopeSnapshots, type EntryCustodyReport, erasureSelectHash, type EraseMemoryEntriesInput, type ErasureSelect, type ErasedBinding, type MemoryErasureAttestation, type MemoryBackend, type MemoryEntry, type MemoryEntryFrontmatter, type MemoryEntryHeader, type ScoredMemoryEntry, type NotePatch, type PatchReport, type MaterializedFile, type MemorySessionHandle, type HarvestReport, type HarvestRejection, type HarvestRejectionCode, type ParsedEntryFile, type ScannedEntryFile, SCOPE_SEGMENT_MAX_ENCODED, PROJECT_MARKER_PATH, encodeScopeSegment, decodeScopeSegment, parseScopeKey, formatUserScope, formatOrgScope, formatProjScope, formatUserProjScope, isPersonalScope, assertScopeContractPlacement, formatProjectMarker, parseProjectMarker, resolveProjectId, PROJECT_ID_REGEX, type ParsedScopeKey, migrateScope, type MigrateScopeReport, materializeEntriesToFiles, harvestFilesToPatches, projectionsToWriteBack, screenInboundEntries, REMOTE_HARVEST_PER_FILE_BYTES, REMOTE_MASS_DELETION_FUSE_RATIO, type RemoteMemoryFile, type RemoteMemoryBaseline, type RemoteMaterialization, type RemoteHarvestResult, type InboundEntryFinding, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, reconcileMemoryEntries, nextSyncBaseline, mergeRecallHits, type MemorySyncCursor, type MemorySyncConflict, type MemorySyncPlan, type RecallSource, syncMemoryScope, type MemorySyncTransport, type MemorySyncRequestBody, type MemorySyncResponseBody, type MemorySyncClientConflict, type SyncMemoryScopeOptions, type MemorySyncClientResult, } from "./core/memory-engine/index.js";
168
169
  export { SHARED_MEMORY_READ_CAP_BYTES, SHARED_MEMORY_LIST_PAGE_SIZE, SharedMemoryStoreError, type SharedMemoryStoreProvider, type SharedMemoryStoreReader, type SharedMemoryPagedList, type SharedMemoryStoreInfo, type SharedMemoryDocumentEntry, type SharedMemorySnapshot, type SharedMemoryRequestContext, type MemoryListDetails, type MemoryReadDetails, } from "./core/shared-memory/types.js";
@@ -201,7 +202,7 @@ export { assertWorkflowSandboxConformance, assertWorkflowPrimitivesWiring, asser
201
202
  export { WorkflowModelNotAllowedError, type WorkflowAgentSpec } from "./orchestration/workflow-governance.js";
202
203
  export { WorkflowMaxAgentsError, WorkflowResultTooLargeError } from "./orchestration/workflow.js";
203
204
  export { createFileWorkflowScriptStore, mergeWorkflowArgs, type WorkflowScriptStore, type NamedWorkflowResolution, type NamedWorkflowListing, } from "./orchestration/workflow-script-store.js";
204
- export { TEAM_DISCUSSION_WORKFLOW_NAME, TEAM_DISCUSSION_SCRIPT, builtinWorkflowDefinitions, builtinWorkflowListings, resolveBuiltinWorkflow, type BuiltinWorkflowDefinition, } from "./orchestration/builtin-workflows.js";
205
+ export { DISCUSSION_WORKFLOW_NAME, DISCUSSION_SCRIPT, TEAM_DISCUSSION_WORKFLOW_NAME, TEAM_DISCUSSION_SCRIPT, canonicalWorkflowName, retiredWorkflowNameAliases, builtinWorkflowDefinitions, builtinWorkflowListings, resolveBuiltinWorkflow, type BuiltinWorkflowDefinition, } from "./orchestration/builtin-workflows.js";
205
206
  export { WORKFLOW_AGENT_STALL_MS, WORKFLOW_AGENT_MAX_RETRIES, WORKFLOW_AGENT_THROTTLE_BACKOFF_MS } from "./orchestration/workflow.js";
206
207
  export { createRunWorkflowTool, RUN_WORKFLOW_TOOL_NAME, workflowWhenToUseText, renderNamedWorkflowListing, type WorkflowCompletionNotifier, type WorkflowLimits, type RunWorkflowToolDeps, } from "./orchestration/run-workflow-tool.js";
207
208
  export { runSideQuery, type SideQuerySpec, type SideQueryResult, type SideQueryToolDef, type SideQueryDeps, type SideQueryMessage } from "./core/side-query.js";
@@ -254,7 +255,7 @@ export { retryBackoffMs, parseRetryAfter } from "./brain/retry.js";
254
255
  export { type BrainTimeoutConfig } from "./brain/timeout.js";
255
256
  export { createAssistantMessageEventStream } from "./internal/llm.js";
256
257
  export type { AssistantMessage, AssistantMessageEvent, CompleteSimpleFn, Context, DocumentContent, ImageContent, Message, StopReason, StreamFn, TextContent, ThinkingContent, ToolCall, ToolResultMessage, Usage, UserMessage, } from "./internal/llm.js";
257
- export type { AgentDefinition, BeforeWriteHook, BeforeWriteRequest, BeforeWriteResult, HandsBandOptions, Brain, BrainStatus, BrainStatusPhase, BrainRetryErrClass, ImageInput, McpElicitRequest, McpElicitResponse, McpServerSpec, A2aServerSpec, OnElicit, Model, ModelRef, ProjectMemoryLoad, RunnerDeps, EngineNotice, RuntimeCaps, BackgroundChildEvent, SkillManifest, SkillSpec, TaskEvent, TaskEventIdentity, ToolActivity, TaskLimits, StaleToolResultOffloadOptions, TaskResult, EffectiveMemoryScopes, RemoteEnvFailureNote, TaskSpec, TaskStatus, TaskStream, CompactOutcome, ThinkingLevel, ToolExecuteContext, ToolReturn, ToolSpec, ReversibilityVerdict, ToolEffect, ToolContentOrigin, WorkflowGovernanceBaseline, DelegationTaskType, } from "./core/types.js";
258
+ export type { AgentDefinition, BeforeWriteHook, BeforeWriteRequest, BeforeWriteResult, HandsBandOptions, Brain, BrainStatus, BrainStatusPhase, BrainRetryErrClass, ImageInput, McpElicitRequest, McpElicitResponse, McpServerSpec, A2aServerSpec, OnElicit, Model, ModelRef, ProjectMemoryLoad, RunnerDeps, EngineNotice, RuntimeCaps, BackgroundChildEvent, DelegationLifecycleEvent, SkillManifest, SkillSpec, TaskEvent, TaskEventIdentity, ToolActivity, TaskLimits, StaleToolResultOffloadOptions, TaskResult, EffectiveMemoryScopes, RemoteEnvFailureNote, TaskSpec, TaskStatus, TaskStream, CompactOutcome, ThinkingLevel, ToolExecuteContext, ToolReturn, ToolSpec, ReversibilityVerdict, ToolEffect, ToolContentOrigin, WorkflowGovernanceBaseline, DelegationTaskType, } from "./core/types.js";
258
259
  export { Type } from "typebox";
259
260
  export type { TSchema, Static } from "typebox";
260
261
  export { explainPromptAssembly, describeDefaultPack, type DefaultPackDescription, type ExplainInput } from "./prompt-assembly/explain.js";