@sema-agent/core 7.4.0 → 7.5.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.
Files changed (115) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/dist/agents/cascade.d.ts +2 -1
  3. package/dist/agents/peer-notices.d.ts +11 -1
  4. package/dist/agents/peer-session-drain.js +2 -0
  5. package/dist/agents/verify.d.ts +2 -1
  6. package/dist/core/ask-origin.d.ts +130 -0
  7. package/dist/core/ask-origin.js +35 -0
  8. package/dist/core/auto-mode-arming.d.ts +40 -1
  9. package/dist/core/auto-mode-arming.js +51 -3
  10. package/dist/core/auto-mode.d.ts +87 -10
  11. package/dist/core/auto-mode.js +34 -6
  12. package/dist/core/checkpoint-store.d.ts +41 -6
  13. package/dist/core/checkpoint-store.js +8 -0
  14. package/dist/core/hooks.d.ts +63 -19
  15. package/dist/core/hooks.js +37 -16
  16. package/dist/core/mcp.d.ts +47 -55
  17. package/dist/core/mcp.js +89 -31
  18. package/dist/core/park-selfcheck.js +3 -0
  19. package/dist/core/permission-rule-consent.d.ts +2 -11
  20. package/dist/core/permission-rule-consent.js +13 -62
  21. package/dist/core/permission-rule-org.d.ts +70 -54
  22. package/dist/core/permission-rule-org.js +47 -61
  23. package/dist/core/permission-rule-provider.d.ts +162 -0
  24. package/dist/core/permission-rule-provider.js +148 -0
  25. package/dist/core/permission-rule-session.d.ts +23 -19
  26. package/dist/core/permission-rule-session.js +5 -5
  27. package/dist/core/permission-rule-store.d.ts +46 -21
  28. package/dist/core/permission-rule-store.js +13 -6
  29. package/dist/core/permission-rule-sync.d.ts +2 -1
  30. package/dist/core/permission-rule-sync.js +11 -0
  31. package/dist/core/runner/assemble-result.d.ts +3 -2
  32. package/dist/core/runner/checkpoint-scope.d.ts +32 -0
  33. package/dist/core/runner/checkpoint-scope.js +4 -0
  34. package/dist/core/runner/contracts.d.ts +1878 -0
  35. package/dist/core/runner/contracts.js +1 -0
  36. package/dist/core/runner/denial-limit-arms.d.ts +57 -31
  37. package/dist/core/runner/denial-limit-arms.js +42 -17
  38. package/dist/core/runner/derived-route-fallback.d.ts +34 -0
  39. package/dist/core/runner/derived-route-fallback.js +16 -0
  40. package/dist/core/runner/prepare-acquire-reconcile.d.ts +1 -1
  41. package/dist/core/runner/prepare-announce-once.d.ts +83 -0
  42. package/dist/core/runner/prepare-announce-once.js +105 -0
  43. package/dist/core/runner/prepare-caps-and-workflow.d.ts +170 -0
  44. package/dist/core/runner/prepare-caps-and-workflow.js +255 -0
  45. package/dist/core/runner/prepare-config-doors.d.ts +2 -10
  46. package/dist/core/runner/prepare-defer-classify.d.ts +86 -0
  47. package/dist/core/runner/prepare-defer-classify.js +107 -0
  48. package/dist/core/runner/prepare-delegation-surface.d.ts +104 -0
  49. package/dist/core/runner/prepare-delegation-surface.js +144 -0
  50. package/dist/core/runner/prepare-execution-env.d.ts +54 -0
  51. package/dist/core/runner/prepare-execution-env.js +86 -0
  52. package/dist/core/runner/prepare-file-history.d.ts +95 -0
  53. package/dist/core/runner/prepare-file-history.js +383 -0
  54. package/dist/core/runner/prepare-hands-readface.d.ts +6 -8
  55. package/dist/core/runner/prepare-hands-readface.js +3 -3
  56. package/dist/core/runner/prepare-inherited-gate.d.ts +268 -0
  57. package/dist/core/runner/prepare-inherited-gate.js +266 -0
  58. package/dist/core/runner/prepare-listings.d.ts +77 -0
  59. package/dist/core/runner/prepare-listings.js +76 -0
  60. package/dist/core/runner/prepare-lsp.d.ts +55 -0
  61. package/dist/core/runner/prepare-lsp.js +27 -0
  62. package/dist/core/runner/prepare-memory.d.ts +1 -1
  63. package/dist/core/runner/prepare-offload-wrappers.d.ts +62 -0
  64. package/dist/core/runner/prepare-offload-wrappers.js +45 -0
  65. package/dist/core/runner/prepare-permission-rules.d.ts +132 -0
  66. package/dist/core/runner/prepare-permission-rules.js +140 -0
  67. package/dist/core/runner/prepare-project-context.d.ts +131 -0
  68. package/dist/core/runner/prepare-project-context.js +150 -0
  69. package/dist/core/runner/prepare-prompt-inputs.d.ts +138 -0
  70. package/dist/core/runner/prepare-prompt-inputs.js +141 -0
  71. package/dist/core/runner/prepare-protocol-tools.d.ts +91 -0
  72. package/dist/core/runner/prepare-protocol-tools.js +182 -0
  73. package/dist/core/runner/prepare-question-face.d.ts +119 -0
  74. package/dist/core/runner/prepare-question-face.js +83 -0
  75. package/dist/core/runner/prepare-run-refs.d.ts +89 -0
  76. package/dist/core/runner/prepare-run-refs.js +39 -0
  77. package/dist/core/runner/prepare-safety-scan.d.ts +3 -2
  78. package/dist/core/runner/prepare-task.d.ts +11 -1815
  79. package/dist/core/runner/prepare-task.js +138 -2542
  80. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +111 -0
  81. package/dist/core/runner/prepare-tool-disclosure-mount.js +219 -0
  82. package/dist/core/runner/prepare-wiring-manifest.d.ts +184 -0
  83. package/dist/core/runner/prepare-wiring-manifest.js +240 -0
  84. package/dist/core/runner/prepare-workspace-restore.d.ts +1 -27
  85. package/dist/core/runner/prepare-workspace-restore.js +1 -22
  86. package/dist/core/runner/rollback-stack.d.ts +32 -0
  87. package/dist/core/runner/rollback-stack.js +30 -0
  88. package/dist/core/runner/runtask.d.ts +11 -2
  89. package/dist/core/runner/runtask.js +27 -9
  90. package/dist/core/runner/workspace-path.d.ts +33 -0
  91. package/dist/core/runner/workspace-path.js +22 -0
  92. package/dist/core/sensitive-path-policy.d.ts +16 -0
  93. package/dist/core/sensitive-path-policy.js +1 -1
  94. package/dist/core/tool-policy.d.ts +57 -9
  95. package/dist/core/tool-policy.js +11 -0
  96. package/dist/core/types.d.ts +63 -51
  97. package/dist/core/wiring-manifest.d.ts +40 -3
  98. package/dist/core/wiring-manifest.js +4 -3
  99. package/dist/core/write-protect.d.ts +13 -2
  100. package/dist/core/write-protect.js +58 -29
  101. package/dist/engine/harness/types.d.ts +38 -16
  102. package/dist/engine/harness/types.js +25 -1
  103. package/dist/engine/session/session.d.ts +3 -11
  104. package/dist/index.d.ts +10 -6
  105. package/dist/index.js +9 -5
  106. package/dist/internal/harness.d.ts +1 -0
  107. package/dist/stores/file/adoption/adopt.d.ts +1 -1
  108. package/dist/stores/file/adoption/marker.d.ts +1 -1
  109. package/dist/stores/file/permission-rule-adopt.js +4 -3
  110. package/dist/stores/file/permission-rule-store.d.ts +65 -25
  111. package/dist/stores/file/permission-rule-store.js +215 -37
  112. package/dist/stores/file/task-list-store.d.ts +1 -1
  113. package/dist/tools/fs/read-face.d.ts +1 -1
  114. package/package.json +8 -2
  115. package/test/export-surface.snapshot.json +76 -28
@@ -0,0 +1,111 @@
1
+ /**
2
+ * design/390 §1.2 M14 — prepareTask's TOOL-DISCLOSURE-MOUNT phase, verbatim from the driver: the deferred registry
3
+ * and its placeholders, the ToolSearch mount with its serialized activation section, the materialize strategy's
4
+ * two-layer resolution (spec over the env channel, the safety interlock, the loud arms for a discarded env value), the
5
+ * per-tool static exemption with its monotone ledger, the activation seed from the transcript / compaction snapshots /
6
+ * checkpoint, the callable-names accessor the offload preview and ToolSearch share, the delta ref with its failed-server
7
+ * arm, and the RefreshMcpTools rebuild seam's two arms. The env-channel announce ledger (per sink, per line) moved with
8
+ * the segment — only this phase announces through it. The interface is the dependency list the segment had implicitly
9
+ * (design/238 R-1).
10
+ *
11
+ * ASYNC PHASE: with deferred names present the segment awaits the session's context and branch (the activation seed),
12
+ * exactly where the driver awaited them; with nothing deferred the body has no await of its own. The phase adds no yield
13
+ * BEFORE the first of those awaits (the reserved-name refusal, the env screen and the registry build run synchronously
14
+ * as they did). Read-stability of the host handles for the call: {@link RunInternals} (`@contract prepare.deps-read-stable`).
15
+ *
16
+ * The roster is READ here, never written (the writer table is on {@link Prepared.tools}); `harnessTools` is the derived
17
+ * list the harness is built from, and the placeholders live only in it.
18
+ */
19
+ import type { AgentHarness, AgentTool } from "../../internal/harness.js";
20
+ import { type ToolFingerprintInput } from "../cache-break-detector.js";
21
+ import type { MaterializedMcp, McpServerStatus } from "../mcp.js";
22
+ import type { StoredSession } from "../session.js";
23
+ import type { RunnerDeps, TaskSpec } from "../types.js";
24
+ import type { Prepared, PrepareResume } from "./contracts.js";
25
+ export interface PrepareToolDisclosureMountInput {
26
+ /** borrowed-readonly — the REBOUND spec. Read: `toolMaterializeStrategy` (the requested strategy, spec wins) and
27
+ * `deferSelfResolve` (the direct-call lane, the interlock). Never mutated. */
28
+ spec: Pick<TaskSpec, "toolMaterializeStrategy" | "deferSelfResolve">;
29
+ /** borrowed-readonly — the deployment seats: only `onNotice` (the env-channel discard announcements). */
30
+ deps: Pick<RunnerDeps, "onNotice">;
31
+ /** borrowed-readonly — the acquired session: `buildContext` and `getBranch` seed the activation set (deferred names only). */
32
+ session: Pick<StoredSession, "buildContext" | "getBranch">;
33
+ /** borrowed-readonly — the resume leg, or undefined: `seed.activeTools` is the authoritative activation reseed. */
34
+ resume: Pick<PrepareResume, "seed"> | undefined;
35
+ /** borrowed-readonly — the deferred classification (post-exclusion names): read for size, membership and iteration. */
36
+ deferred: ReadonlySet<string>;
37
+ /** borrowed-readonly — the run's shared roster (`Prepared.tools`): READ here — the registry build, the reserved-name
38
+ * check, the live resolution the placeholders and the static face perform at call time. Never written by this phase. */
39
+ tools: AgentTool[];
40
+ /** borrowed-readonly — the once-per-session ledger's failed-server projection (current failures minus the pairs this
41
+ * session already announced). */
42
+ onceLedger: {
43
+ mcpFailuresToAnnounce(statuses: ReadonlyArray<McpServerStatus>): Array<{
44
+ name: string;
45
+ error?: string;
46
+ }>;
47
+ };
48
+ /** borrowed-readonly — the materialized MCP handle; only `statuses` (the failed-server arm's source). */
49
+ mcp: Pick<MaterializedMcp, "statuses">;
50
+ /** borrowed-mutable — the assembled-prompt manifest (`Prepared.promptManifest`). Writer here: ONE assignment of
51
+ * `toolDisclosure` (deferred names only). */
52
+ promptManifest: Prepared["promptManifest"];
53
+ /** borrowed-mutable — the offload preview's reachability accessor seat. Writer here: `current` ← the callable-names
54
+ * accessor (deferred names only); the offload wrappers read it at each offload. */
55
+ offloadReachableToolsRef: {
56
+ current?: () => ReadonlySet<string>;
57
+ };
58
+ /** borrowed-readonly — the content-origin wrap seat; both rebuild arms CALL `current` at run time, never write it. */
59
+ contentOriginWrapRef: {
60
+ current?: () => void;
61
+ };
62
+ /** borrowed-mutable — the RefreshMcpTools rebuild seam. Writer here: `current` ← the deferred arm's rematerialize or the
63
+ * default arm's bare setTools (exactly one of the two, decided by `deferred.size`). */
64
+ rebuildHarnessToolsRef: {
65
+ current?: () => Promise<void>;
66
+ };
67
+ /** borrowed-readonly — the harness seat; the rebuild arms and the direct-call lane read `current` at run time. */
68
+ harnessRef: {
69
+ current?: AgentHarness;
70
+ };
71
+ }
72
+ export interface PrepareToolDisclosureMountResult {
73
+ /** owned — the activation set (`Prepared.activeTools`): seeded here; ToolSearch and the direct lane add at run time. */
74
+ activeTools: Set<string>;
75
+ /** borrowed-mutable — the cache-fingerprint seat. Writer: the driver, once, when the fingerprint is built; the rebuild
76
+ * arms refresh its `tools` at run time. */
77
+ fpRef: {
78
+ current?: {
79
+ systemPrompt: string;
80
+ tools: ToolFingerprintInput[];
81
+ };
82
+ };
83
+ /** borrowed-mutable — the turn-snapshot identity seat. Writer: the driver's turn wiring, once; the rebuild arms call
84
+ * `refreshTools` on it at run time. */
85
+ turnSnapshotRef: {
86
+ current?: import("../../prompt-assembly/turn-snapshot.js").TurnPromptSnapshot;
87
+ };
88
+ /** owned — the list the harness is built from: the roster with placeholders in the deferred slots plus ToolSearch (or the
89
+ * roster itself when nothing is deferred). */
90
+ harnessTools: AgentTool[];
91
+ /** owned — the boundary delta ref (`Prepared.toolsDeltaRef`), minted when anything is deferred OR a server failed. */
92
+ toolsDeltaRef: Prepared["toolsDeltaRef"];
93
+ /** owned — the effective strategy (`Prepared.toolMaterializeStatic`). */
94
+ toolMaterializeStatic: boolean;
95
+ /** owned — the direct-call lane's presence (`Prepared.deferDirectCall`). */
96
+ deferDirectCall: boolean;
97
+ /** owned — the per-tool static exemption seat (`Prepared.staticFaceFor` reads its `current`). */
98
+ staticFaceForRef: {
99
+ current?: (name: string) => boolean;
100
+ };
101
+ /** borrowed-mutable — the listing-ride seam: the ToolSearch tool and the direct lane call `current` at run time. Writer:
102
+ * the listings phase, once, after the roster face is classified. */
103
+ listingRideRef: {
104
+ current?: (newly: readonly string[]) => string | undefined;
105
+ };
106
+ }
107
+ /** Test seam (mirrors `__resetToolModelGateAnnouncements`): never called by production code.
108
+ * Re-arms BOTH arms (a WeakMap has no clear — it is re-minted). */
109
+ export declare function __resetMaterializeEnvAnnouncements(): void;
110
+ /** The M14 phase body — prepareTask's tool-disclosure-mount stretch, verbatim (see the module header). */
111
+ export declare function prepareToolDisclosureMount(input: PrepareToolDisclosureMountInput): Promise<PrepareToolDisclosureMountResult>;
@@ -0,0 +1,219 @@
1
+ import { readCompactionActiveTools } from "../../internal/harness.js";
2
+ import { toolsToFingerprintInputs } from "../cache-break-detector.js";
3
+ import { deliverEngineNotice } from "../types.js";
4
+ import { TOOL_SEARCH_NAME, buildDeferredRegistry, extractDiscoveredToolNames, createPlaceholderTool, createToolSearchTool, staticSchemaRenderable } from "./tool-disclosure.js";
5
+ let announcedMaterializeEnvBySink = new WeakMap();
6
+ const announcedMaterializeEnvConsole = new Set();
7
+ function materializeEnvLedger(onNotice) {
8
+ if (typeof onNotice !== "function")
9
+ return announcedMaterializeEnvConsole;
10
+ let lines = announcedMaterializeEnvBySink.get(onNotice);
11
+ if (lines === undefined) {
12
+ lines = new Set();
13
+ announcedMaterializeEnvBySink.set(onNotice, lines);
14
+ }
15
+ return lines;
16
+ }
17
+ export function __resetMaterializeEnvAnnouncements() {
18
+ announcedMaterializeEnvBySink = new WeakMap();
19
+ announcedMaterializeEnvConsole.clear();
20
+ }
21
+ function emitMaterializeEnvNotice(onNotice, message, detail) {
22
+ const ledger = materializeEnvLedger(onNotice);
23
+ if (ledger.has(message))
24
+ return;
25
+ ledger.add(message);
26
+ deliverEngineNotice(onNotice, { code: "config.materialize_env_discarded", message, detail });
27
+ }
28
+ export async function prepareToolDisclosureMount(input) {
29
+ const { spec, deps, session, resume, deferred, tools, onceLedger, mcp, promptManifest, offloadReachableToolsRef, contentOriginWrapRef, rebuildHarnessToolsRef, harnessRef } = input;
30
+ const activeTools = new Set();
31
+ const fpRef = {};
32
+ const turnSnapshotRef = {};
33
+ let harnessTools = tools;
34
+ const failedMcpServers = onceLedger.mcpFailuresToAnnounce(mcp.statuses);
35
+ let toolsDeltaRef;
36
+ let toolMaterializeStatic = false;
37
+ let deferDirectCall = false;
38
+ const staticFaceForRef = {};
39
+ if (deferred.size > 0 || failedMcpServers.length > 0) {
40
+ toolsDeltaRef = { pending: [], pendingRemoved: [], pendingReadded: [], pendingFailed: failedMcpServers };
41
+ }
42
+ const listingRideRef = {};
43
+ {
44
+ const raw = process.env.SEMA_TOOL_MATERIALIZE_STRATEGY;
45
+ if (raw !== undefined && raw !== "swap" && raw !== "static" && deferred.size === 0) {
46
+ const line = `SEMA_TOOL_MATERIALIZE_STRATEGY=${JSON.stringify(raw)} is not "swap" or "static" — inert on this task (no deferred tools), but a deferring task WITHOUT an explicit spec strategy will refuse to prepare under it (an explicit legal spec outranks and discards it, loudly). Fix or unset the flag.`;
47
+ emitMaterializeEnvNotice(deps.onNotice, line, { raw });
48
+ }
49
+ }
50
+ if (deferred.size > 0) {
51
+ if (deferred.has(TOOL_SEARCH_NAME) || tools.some((t) => t.name === TOOL_SEARCH_NAME)) {
52
+ const e = new Error(`Tool name "${TOOL_SEARCH_NAME}" is reserved when deferred tools are present.`);
53
+ e.code = "config.reserved_tool_name";
54
+ throw e;
55
+ }
56
+ const registry = buildDeferredRegistry(deferred, tools);
57
+ const rawEnvStrategy = process.env.SEMA_TOOL_MATERIALIZE_STRATEGY;
58
+ const envStrategyInvalid = rawEnvStrategy !== undefined && rawEnvStrategy !== "swap" && rawEnvStrategy !== "static";
59
+ if (envStrategyInvalid && spec.toolMaterializeStrategy === undefined) {
60
+ const e = new Error(`SEMA_TOOL_MATERIALIZE_STRATEGY must be "swap" or "static" (got ${JSON.stringify(rawEnvStrategy)}).`);
61
+ e.code = "config.tool_materialize_invalid";
62
+ throw e;
63
+ }
64
+ if (envStrategyInvalid) {
65
+ const line = `SEMA_TOOL_MATERIALIZE_STRATEGY=${JSON.stringify(rawEnvStrategy)} was ignored — not "swap" or "static", and the task spec pins toolMaterializeStrategy=${JSON.stringify(spec.toolMaterializeStrategy)} which outranks it. Fix or unset the env flag.`;
66
+ emitMaterializeEnvNotice(deps.onNotice, line, { raw: rawEnvStrategy, specStrategy: spec.toolMaterializeStrategy });
67
+ }
68
+ const envStrategy = envStrategyInvalid ? undefined : rawEnvStrategy;
69
+ const requestedStrategy = spec.toolMaterializeStrategy ?? envStrategy ?? "swap";
70
+ const laneDegrade = requestedStrategy === "static" && spec.deferSelfResolve === false;
71
+ const materializeStatic = requestedStrategy === "static" && !laneDegrade;
72
+ toolMaterializeStatic = materializeStatic;
73
+ deferDirectCall = spec.deferSelfResolve !== false;
74
+ promptManifest.toolDisclosure = {
75
+ deferredTools: deferred.size,
76
+ strategy: materializeStatic ? "static" : "swap",
77
+ source: laneDegrade ? "degraded_no_direct_lane" : spec.toolMaterializeStrategy !== undefined ? "spec" : envStrategy !== undefined ? "env" : "default",
78
+ };
79
+ const everExempt = new Set();
80
+ const staticFaceFor = (name) => {
81
+ if (!materializeStatic)
82
+ return false;
83
+ if (everExempt.has(name))
84
+ return false;
85
+ if (staticSchemaRenderable(tools.find((t) => t.name === name)?.parameters))
86
+ return true;
87
+ everExempt.add(name);
88
+ return false;
89
+ };
90
+ staticFaceForRef.current = staticFaceFor;
91
+ let activationChain = Promise.resolve();
92
+ const serializeActivation = (section) => {
93
+ const p = activationChain.then(section);
94
+ activationChain = p.then(() => undefined, () => undefined);
95
+ return p;
96
+ };
97
+ const directCallFor = (name) => {
98
+ if (spec.deferSelfResolve === false)
99
+ return undefined;
100
+ const executionMode = tools.find((t) => t.name === name)?.executionMode;
101
+ return {
102
+ resolveReal: () => {
103
+ const real = tools.find((t) => t.name === name);
104
+ if (real === undefined)
105
+ return undefined;
106
+ return {
107
+ parameters: real.parameters,
108
+ invoke: (toolCallId, params, signal, onUpdate) => real.execute(toolCallId, params, signal, onUpdate),
109
+ };
110
+ },
111
+ ...(executionMode !== undefined ? { executionMode } : {}),
112
+ staticFace: () => staticFaceFor(name),
113
+ activate: async () => serializeActivation(async () => {
114
+ if (activeTools.has(name))
115
+ return undefined;
116
+ activeTools.add(name);
117
+ try {
118
+ await rematerialize(activeTools);
119
+ }
120
+ catch (e) {
121
+ activeTools.delete(name);
122
+ throw e;
123
+ }
124
+ return listingRideRef.current?.([name]);
125
+ }),
126
+ };
127
+ };
128
+ const placeholders = new Map([...registry.values()].map((i) => [i.name, createPlaceholderTool(i, directCallFor(i.name))]));
129
+ const { messages } = await session.buildContext();
130
+ for (const n of extractDiscoveredToolNames(messages, registry)) {
131
+ if (deferred.has(n))
132
+ activeTools.add(n);
133
+ }
134
+ for (const entry of await session.getBranch()) {
135
+ if (entry.type !== "compaction" || entry.fromHook === true)
136
+ continue;
137
+ for (const n of readCompactionActiveTools(entry.details)) {
138
+ if (deferred.has(n))
139
+ activeTools.add(n);
140
+ }
141
+ }
142
+ if (resume) {
143
+ for (const n of resume.seed.activeTools)
144
+ if (deferred.has(n))
145
+ activeTools.add(n);
146
+ }
147
+ const callableToolNames = () => {
148
+ const s = new Set(tools.map((t) => t.name));
149
+ for (const n of deferred)
150
+ if (!activeTools.has(n))
151
+ s.delete(n);
152
+ s.add(TOOL_SEARCH_NAME);
153
+ return s;
154
+ };
155
+ offloadReachableToolsRef.current = callableToolNames;
156
+ let toolSearch;
157
+ const buildToolList = (active) => {
158
+ const list = tools.map((t) => (deferred.has(t.name) && (staticFaceFor(t.name) || !active.has(t.name)) ? placeholders.get(t.name) : t));
159
+ list.push(toolSearch);
160
+ return list;
161
+ };
162
+ const announcedTools = new Set(activeTools);
163
+ const withdrawnTools = new Set();
164
+ const deltaRef = toolsDeltaRef;
165
+ const rematerialize = async (active) => {
166
+ contentOriginWrapRef.current?.();
167
+ const list = buildToolList(active);
168
+ await harnessRef.current.setTools(list, list.map((t) => t.name));
169
+ if (fpRef.current)
170
+ fpRef.current.tools = toolsToFingerprintInputs(list);
171
+ turnSnapshotRef.current?.refreshTools(toolsToFingerprintInputs(list));
172
+ const live = new Set(list.map((t) => t.name));
173
+ for (const n of [...announcedTools]) {
174
+ if (!live.has(n)) {
175
+ announcedTools.delete(n);
176
+ withdrawnTools.add(n);
177
+ deltaRef.pendingRemoved.push(n);
178
+ }
179
+ }
180
+ for (const n of active) {
181
+ if (!live.has(n))
182
+ continue;
183
+ if (!announcedTools.has(n)) {
184
+ announcedTools.add(n);
185
+ if (withdrawnTools.delete(n))
186
+ deltaRef.pendingReadded.push(n);
187
+ else
188
+ deltaRef.pending.push(n);
189
+ }
190
+ }
191
+ };
192
+ rebuildHarnessToolsRef.current = () => rematerialize(activeTools);
193
+ toolSearch = createToolSearchTool({
194
+ registry,
195
+ active: activeTools,
196
+ rematerialize,
197
+ listingRide: (newly) => listingRideRef.current?.(newly),
198
+ mountedNames: callableToolNames,
199
+ directCallEnabled: spec.deferSelfResolve !== false,
200
+ isMounted: (name) => tools.some((t) => t.name === name),
201
+ ...(materializeStatic
202
+ ? { staticSchemaFor: (name) => (staticFaceFor(name) ? tools.find((t) => t.name === name)?.parameters : undefined) }
203
+ : {}),
204
+ serializeActivation,
205
+ });
206
+ harnessTools = buildToolList(activeTools);
207
+ }
208
+ else {
209
+ rebuildHarnessToolsRef.current = async () => {
210
+ contentOriginWrapRef.current?.();
211
+ const list = [...tools];
212
+ await harnessRef.current.setTools(list, list.map((t) => t.name));
213
+ if (fpRef.current)
214
+ fpRef.current.tools = toolsToFingerprintInputs(list);
215
+ turnSnapshotRef.current?.refreshTools(toolsToFingerprintInputs(list));
216
+ };
217
+ }
218
+ return { activeTools, fpRef, turnSnapshotRef, harnessTools, toolsDeltaRef, toolMaterializeStatic, deferDirectCall, staticFaceForRef, listingRideRef };
219
+ }
@@ -0,0 +1,184 @@
1
+ /**
2
+ * design/390 §1.2 M18 — prepareTask's WIRING-MANIFEST phase, verbatim from the driver: the run's own frozen approver
3
+ * seat, the loud ungated-write warning (hand band / destructive MCP / first-party write tools with no effect-aware gate,
4
+ * deduped per deployment and tool-set shape), the PreToolUse context stash and the blocked-call set, the settlement and
5
+ * bare-rejection sidebands, the turn halt fact, the suspend infrastructure predicates (restore surface gap, durable
6
+ * infra, the task-axis lane with its INACTIVE line), usage governance, the platform-suspend arming, the synthesized
7
+ * durableApproval, the effective wiring manifest, the peer lane's ask fold and drain binding, the park predicate as ONE
8
+ * named const, the durable-gate-unavailable notice, the hook identity mint and the hook env face. The four helpers only
9
+ * this segment called moved with it (usage governance, the per-deployment warn ledger, the MCP manifest projection, the
10
+ * durable-gate notice). The interface is the dependency list the segment had implicitly (design/238 R-1).
11
+ *
12
+ * SYNCHRONOUS FUNCTION, SYNCHRONOUS CALL: the stretch has no await, so the phase adds no yield between the policy-chain
13
+ * station before it and the gate registration after it (design/390 §1.5: a stretch with no await is extracted as a sync
14
+ * function). Read-stability of the host handles for the call: {@link RunInternals} (`@contract prepare.deps-read-stable`).
15
+ *
16
+ * One read changed spelling and nothing else: the manifest's MCP face read `mcp?.statuses ?? []` while `mcp` had long
17
+ * been the protocol-tools phase's non-null handle; the optional chain was dead and is now the plain member read.
18
+ */
19
+ import type { AgentHarness, ExecutionEnv } from "../../internal/harness.js";
20
+ import { type PeerLaneRefs } from "../../agents/peer-session-drain.js";
21
+ import type { OnQuestion } from "../ask-question.js";
22
+ import type { CheckpointStore } from "../checkpoint-store.js";
23
+ import { type HookEnvCapabilities, type HookInvocationIdentity, type Hooks } from "../hooks.js";
24
+ import type { LockedPreflight } from "../locked-config.js";
25
+ import type { MaterializedMcp } from "../mcp.js";
26
+ import type { SessionStore } from "../session.js";
27
+ import { type ToolResultStore } from "../tool-result-store.js";
28
+ import { type ApprovalSettledBy, type OnAsk } from "../tool-policy.js";
29
+ import type { RunnerDeps, RuntimeCaps, TaskSpec, ToolEffect, ToolExecuteContext } from "../types.js";
30
+ import { type UsageWindow } from "../usage-window-store.js";
31
+ import { type AutoModeArmReason, type WiringManifest, type WiringManifestMcpEntry } from "../wiring-manifest.js";
32
+ import type { AnnounceOnceSink, Prepared, PrepareResume, RunInternals, UsageGovernance } from "./contracts.js";
33
+ export interface PrepareWiringManifestInput {
34
+ /** borrowed-readonly — the REBOUND spec. Read: `onAsk` (the own seat), `onQuestion` (the question seam), `handsReadOnly` / `tools` (the ungated scan),
35
+ * `resourceSuspend` (the task-axis lane), `durableApproval` / `principal` (the synthesis and the notice), `interactiveTools` /
36
+ * `mcp` / `checkpointStore` (manifest cells and the notice's cause). Never mutated. */
37
+ spec: Pick<TaskSpec, "onAsk" | "onQuestion" | "handsReadOnly" | "tools" | "resourceSuspend" | "durableApproval" | "principal" | "interactiveTools" | "mcp" | "checkpointStore">;
38
+ /** borrowed-readonly — the deployment seats, whole: `onAsk` (the seat's deps half), `onError` (the warning), `executionEnv`
39
+ * (the hook-env predicate), `checkpointStore` (durable infra), the manifest's dozen wiring facts (`backgroundAgentStore`,
40
+ * `permissionRuleStore`, `permissionRuleSyncWired`, `onBackgroundChildEvent`, `lockedConfig`, `compliancePostureResolver`,
41
+ * `memoryScopeAdmission`, `retentionPolicy`, `onElicit`), `onNotice`, and the usage-governance seats (`usageWindowStore`,
42
+ * `now`, `onUsageGovernance`…) the helper reads through the whole record. */
43
+ deps: RunnerDeps;
44
+ /** borrowed-readonly — the trusted spawn-side channel: `questionFaceStripped`, `isDelegatedChild`, `onTaskNotification`,
45
+ * `peerSelfRef`, `explicitAgentName` (the drain), `insideFork` / `agentName` / `parentToolCallId` (the identity). */
46
+ internals: Pick<RunInternals, "questionFaceStripped" | "isDelegatedChild" | "onTaskNotification" | "peerSelfRef" | "explicitAgentName" | "insideFork" | "agentName" | "parentToolCallId"> | undefined;
47
+ /** borrowed-readonly — the resume leg, or undefined (the manifest's leg derivation reads presence only). */
48
+ resume: Pick<PrepareResume, "seed"> | undefined;
49
+ /** borrowed-readonly — the acquired session id. */
50
+ sessionId: string;
51
+ /** borrowed-readonly — the run's invocation id. */
52
+ runId: string;
53
+ /** borrowed-readonly — the per-task identity (the hook identity's taskId). */
54
+ hostTaskId: string;
55
+ /** borrowed-readonly — the registry scope the peer drain binds under. */
56
+ taskScope: string;
57
+ /** borrowed-readonly — the resolved hook record (`spec.hooks ?? deps.hooks`): the effect-aware-gate term, the tracked flag
58
+ * and the hook-env face's consumer test. */
59
+ hooks: Hooks | undefined;
60
+ /** borrowed-readonly — whether the deployment declared its screening face observational (then it is no gate). */
61
+ preToolUseObservational: boolean;
62
+ /** borrowed-readonly — the caller-facing policy layers; only their COUNT is read (an effect-aware gate exists). */
63
+ policyLayers: ReadonlyArray<unknown>;
64
+ /** borrowed-readonly — whether an execution env is wired (the hand band exists at all). */
65
+ handsEnabled: boolean;
66
+ /** borrowed-readonly — the materialized MCP handle: `tools` (the destructive-ungated scan) and `statuses` (the manifest face). */
67
+ mcp: Pick<MaterializedMcp, "tools" | "statuses">;
68
+ /** borrowed-readonly — the assembled safety vocabulary (the ungated scans and the manifest's armed cell). Read only. */
69
+ irreversibleTools: ReadonlySet<string>;
70
+ /** borrowed-readonly — see `irreversibleTools`. */
71
+ egressTools: ReadonlySet<string>;
72
+ /** borrowed-readonly — the effect collection; read only here (the ungated scans). */
73
+ toolEffects: ReadonlyMap<string, ToolEffect>;
74
+ /** borrowed-readonly — the per-task owned env when a factory minted one (durable infra, the hook-env predicate). */
75
+ ownedEnv: ExecutionEnv | undefined;
76
+ /** borrowed-readonly — the run's resolved env (the hook env face is built over it). */
77
+ executionEnv: ExecutionEnv;
78
+ /** borrowed-readonly — the run's offload store, or undefined (a volatile store disqualifies durable suspend). */
79
+ offloadStore: ToolResultStore | undefined;
80
+ /** borrowed-readonly — the once-per-session ledger's operator sink (the INACTIVE line, once per session). */
81
+ onceLedger: Pick<AnnounceOnceSink, "onError">;
82
+ /** borrowed-readonly — the resolved usage windows, or undefined. */
83
+ usageWindows: readonly UsageWindow[] | undefined;
84
+ /** borrowed-readonly — the env-lifetime suspend instant, or undefined (platform-suspend arming). */
85
+ envLifetimeSuspendAt: number | undefined;
86
+ /** borrowed-readonly — the resolved per-principal entitlements (forceDurableGate). */
87
+ runtimeCaps: RuntimeCaps | undefined;
88
+ /** borrowed-readonly — the resolved interaction posture (a manifest cell). */
89
+ resolvedInteractionPosture: ToolExecuteContext["interactionPosture"];
90
+ /** borrowed-readonly — the question-face phase's durable-face decision (the composition-lie detector reuses it). */
91
+ durableQuestionFace: boolean;
92
+ /** borrowed-readonly — the live question face, or undefined (the notice's effective fact). */
93
+ liveQuestionFace: OnQuestion | undefined;
94
+ /** borrowed-readonly — the leg's checkpoint store, resolved once by the question-face phase. */
95
+ checkpointStore: CheckpointStore | undefined;
96
+ /** borrowed-readonly — the session store (durability declaration and transcript tier). */
97
+ sessions: SessionStore;
98
+ /** borrowed-readonly — the model gate's per-leg read face, or undefined. */
99
+ modelGateManifest: WiringManifest["modelGate"];
100
+ /** borrowed-readonly — the arming outcome the caps phase minted (the manifest's auto-mode face). */
101
+ autoModeArmReason: AutoModeArmReason;
102
+ /** borrowed-readonly — the frozen preflight; only `mcp` (the declared servers the manifest face joins). */
103
+ lockedPreflight: Pick<LockedPreflight, "mcp">;
104
+ /** borrowed-mutable — the peer lane's late-bound facts. Writer here: `askEffective` ← the manifest's ask derivation (the
105
+ * one fold of this session's permission-mode class). */
106
+ peerLaneRefs: PeerLaneRefs;
107
+ /** borrowed-readonly — the peer lane's mount verdict (the drain binds only while mounted). */
108
+ peerLaneActive: boolean;
109
+ /** borrowed-readonly — the built harness the peer drain binds to. */
110
+ harness: AgentHarness;
111
+ /** borrowed-readonly — the durable-suspend holder; the drain's parked predicate reads `token` at run time. Not written here. */
112
+ suspendRef: Prepared["suspendRef"];
113
+ /** borrowed-readonly — the review holder; same read. Not written here. */
114
+ reviewRef: Prepared["reviewRef"];
115
+ /** borrowed-readonly — the FROZEN approver seat (the notice's liveApprover fact). */
116
+ frozenOnAsk: OnAsk | undefined;
117
+ /** borrowed-readonly — the LIVE half of the inherited chain (the notice reads its content mandates). */
118
+ liveInheritedGate: RunInternals["inheritedGate"];
119
+ /** borrowed-readonly — the effective delegation facts (the identity's isDelegatedChild). */
120
+ delegation: {
121
+ isDelegatedChild: boolean;
122
+ };
123
+ }
124
+ export interface PrepareWiringManifestResult {
125
+ /** owned — the run's own approver seat (spec over deps), read by the gate-fold station. */
126
+ onAsk: OnAsk | undefined;
127
+ /** borrowed-mutable — PreToolUse `additionalContext` per call id. Writers: the gate (phase 1 stash), the post-tool handler
128
+ * (one-shot drain). */
129
+ preToolContexts: Map<string, string[]>;
130
+ /** borrowed-mutable — the call ids the gate blocked or suspended (`Prepared.blockedToolCalls`). Writers: the gate; readers
131
+ * delete one-shot. */
132
+ blockedToolCalls: Set<string>;
133
+ /** borrowed-mutable — the settlement sideband (`Prepared.approvalSettlement`). Writer: the gate; the frame mint reads and
134
+ * deletes. Nothing else may write it. */
135
+ approvalSettlement: Map<string, {
136
+ settledBy?: ApprovalSettledBy;
137
+ approver?: string;
138
+ resolution?: import("../tool-policy.js").AskDenyResolution;
139
+ autoDenied?: true;
140
+ }>;
141
+ /** borrowed-mutable — the bare-human-rejection sideband. Writer: resolveAskBound; the gate handler consumes one-shot. */
142
+ humanBareRejections: Set<string>;
143
+ /** borrowed-mutable — the turn's halt fact (`Prepared.batchHaltRef`). Writer: the judgment site; cleared at the next request. */
144
+ batchHaltRef: Prepared["batchHaltRef"];
145
+ /** owned — whether any consumer of the blocked set is wired (populate only then). */
146
+ blockedTracked: boolean;
147
+ /** owned — the restore-surface gap of a declared-suspendable adapter, or undefined for a complete / non-suspendable one. */
148
+ incompleteSuspendAdapter: readonly ("resumeVM" | "postResumeInit")[] | undefined;
149
+ /** owned — the infrastructure half of suspend eligibility (no task opt-in in it). */
150
+ durableSuspendInfraReady: boolean;
151
+ /** owned — the task-axis lane: the infrastructure plus the caller's opt-in. */
152
+ resourceSuspendEligible: boolean;
153
+ /** owned — the run's usage-governance handle, or undefined (`Prepared.usageGovernance`). */
154
+ usageGovernance: UsageGovernance | undefined;
155
+ /** owned — whether the suspend saga must be armed for a PLATFORM cause. */
156
+ platformSuspendArmed: boolean;
157
+ /** owned — the effective durable-approval opt-in: the caller's, or the scope synthesized for a forced durable gate. */
158
+ durableApproval: TaskSpec["durableApproval"];
159
+ /** owned — this leg's EFFECTIVE wiring manifest (`Prepared.wiringManifest`). */
160
+ wiringManifest: WiringManifest;
161
+ /** owned — THE park predicate as one named const (`parkLane.effective === true`). */
162
+ parkLaneArmed: boolean;
163
+ /** owned — THE identity mint of this leg (`Prepared.hookIdentity`), one frozen envelope every hook station reuses. */
164
+ hookIdentity: HookInvocationIdentity;
165
+ /** owned — the read-only env capability face every hook context carries, or undefined (no hook consumer, or no env wired). */
166
+ hookEnvFace: HookEnvCapabilities | undefined;
167
+ }
168
+ /**
169
+ * The wiring manifest's per-server MCP read face for one leg ({@link WiringManifest.mcp}): every
170
+ * server the leg's FROZEN entry snapshot declared, in declaration order, joined by name to this leg's
171
+ * materialization statuses. Pure projection — no connection logic, no second look at any transport:
172
+ * · a status `connected` ⇒ `connected` with the mounted roster size (the intake's post-filter count);
173
+ * · a status `failed` ⇒ `failed` with the class the materialization stamped ({@link McpFailureCode})
174
+ * and the remote text SECRET-REDACTED (an HTTP endpoint's error body rides the SDK's message
175
+ * verbatim, and a body that echoes the bearer it rejected must not reach a stream consumer),
176
+ * then neutralized + bounded to the reminder's own 240 (the same bound the once-per-session
177
+ * ledger applies to that text — one face, one width);
178
+ * · no status at all ⇒ `skipped` (the declaration stands, this leg produced no attempt record for it).
179
+ * `source` is echoed from the declaration (single-line, bounded; core reads nothing off it). The
180
+ * entries are fresh objects so the manifest never aliases the status records the refresh face mutates.
181
+ */
182
+ export declare function mcpManifestEntries(entries: ReadonlyArray<import("../types.js").McpServerSpec> | undefined, statuses: ReadonlyArray<import("../mcp.js").McpServerStatus>): WiringManifestMcpEntry[];
183
+ /** The M18 phase body — prepareTask's wiring-manifest stretch, verbatim (see the module header). */
184
+ export declare function prepareWiringManifest(input: PrepareWiringManifestInput): PrepareWiringManifestResult;