@sema-agent/core 7.5.0 → 7.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/core/auto-mode.d.ts +9 -0
  3. package/dist/core/auto-mode.js +11 -0
  4. package/dist/core/checkpoint-store.js +5 -1
  5. package/dist/core/protocol-table.d.ts +5 -0
  6. package/dist/core/protocol-table.js +1 -0
  7. package/dist/core/runner/abort-race.d.ts +41 -0
  8. package/dist/core/runner/abort-race.js +38 -0
  9. package/dist/core/runner/checkpoint-scope.d.ts +44 -0
  10. package/dist/core/runner/checkpoint-scope.js +7 -0
  11. package/dist/core/runner/compaction-call-options.d.ts +1 -1
  12. package/dist/core/runner/content-ask-bindings.d.ts +27 -0
  13. package/dist/core/runner/content-ask-bindings.js +1 -0
  14. package/dist/core/runner/contracts.d.ts +1990 -0
  15. package/dist/core/runner/contracts.js +1 -0
  16. package/dist/core/runner/denial-limit-arms.d.ts +24 -1
  17. package/dist/core/runner/denial-limit-arms.js +21 -0
  18. package/dist/core/runner/derived-route-fallback.d.ts +34 -0
  19. package/dist/core/runner/derived-route-fallback.js +16 -0
  20. package/dist/core/runner/inherited-ask-grants.d.ts +46 -0
  21. package/dist/core/runner/inherited-ask-grants.js +29 -0
  22. package/dist/core/runner/park-commit.d.ts +108 -0
  23. package/dist/core/runner/park-commit.js +32 -0
  24. package/dist/core/runner/{prepare-permission-rules.d.ts → permission-rule-lanes.d.ts} +109 -3
  25. package/dist/core/runner/{prepare-permission-rules.js → permission-rule-lanes.js} +47 -1
  26. package/dist/core/runner/prepare-acquire-reconcile.d.ts +1 -1
  27. package/dist/core/runner/prepare-ask-lane.d.ts +110 -0
  28. package/dist/core/runner/prepare-ask-lane.js +133 -0
  29. package/dist/core/runner/prepare-boundary-parks.d.ts +105 -0
  30. package/dist/core/runner/prepare-boundary-parks.js +169 -0
  31. package/dist/core/runner/prepare-caps-and-workflow.d.ts +170 -0
  32. package/dist/core/runner/prepare-caps-and-workflow.js +255 -0
  33. package/dist/core/runner/prepare-config-doors.d.ts +2 -10
  34. package/dist/core/runner/prepare-context-lane.d.ts +119 -0
  35. package/dist/core/runner/prepare-context-lane.js +230 -0
  36. package/dist/core/runner/prepare-defer-classify.d.ts +86 -0
  37. package/dist/core/runner/prepare-defer-classify.js +107 -0
  38. package/dist/core/runner/prepare-delegation-surface.d.ts +104 -0
  39. package/dist/core/runner/prepare-delegation-surface.js +144 -0
  40. package/dist/core/runner/prepare-execution-env.d.ts +54 -0
  41. package/dist/core/runner/prepare-execution-env.js +86 -0
  42. package/dist/core/runner/prepare-file-history.d.ts +95 -0
  43. package/dist/core/runner/prepare-file-history.js +383 -0
  44. package/dist/core/runner/prepare-gate-stations.d.ts +177 -0
  45. package/dist/core/runner/prepare-gate-stations.js +290 -0
  46. package/dist/core/runner/prepare-hands-readface.d.ts +9 -11
  47. package/dist/core/runner/prepare-hands-readface.js +1 -1
  48. package/dist/core/runner/prepare-inherited-gate.d.ts +268 -0
  49. package/dist/core/runner/prepare-inherited-gate.js +266 -0
  50. package/dist/core/runner/prepare-listings.d.ts +77 -0
  51. package/dist/core/runner/prepare-listings.js +76 -0
  52. package/dist/core/runner/prepare-lsp.d.ts +55 -0
  53. package/dist/core/runner/prepare-lsp.js +27 -0
  54. package/dist/core/runner/prepare-memory-engine-session.d.ts +84 -0
  55. package/dist/core/runner/prepare-memory-engine-session.js +233 -0
  56. package/dist/core/runner/prepare-memory.d.ts +1 -1
  57. package/dist/core/runner/prepare-offload-wrappers.d.ts +62 -0
  58. package/dist/core/runner/prepare-offload-wrappers.js +45 -0
  59. package/dist/core/runner/prepare-park-ask.d.ts +164 -0
  60. package/dist/core/runner/prepare-park-ask.js +377 -0
  61. package/dist/core/runner/prepare-policy-chain.d.ts +208 -0
  62. package/dist/core/runner/prepare-policy-chain.js +584 -0
  63. package/dist/core/runner/prepare-project-context.d.ts +119 -0
  64. package/dist/core/runner/prepare-project-context.js +148 -0
  65. package/dist/core/runner/prepare-prompt-assembly.d.ts +95 -0
  66. package/dist/core/runner/prepare-prompt-assembly.js +162 -0
  67. package/dist/core/runner/prepare-prompt-inputs.d.ts +119 -0
  68. package/dist/core/runner/prepare-prompt-inputs.js +141 -0
  69. package/dist/core/runner/prepare-protocol-tools.d.ts +91 -0
  70. package/dist/core/runner/prepare-protocol-tools.js +179 -0
  71. package/dist/core/runner/prepare-question-face.d.ts +101 -0
  72. package/dist/core/runner/prepare-question-face.js +84 -0
  73. package/dist/core/runner/prepare-run-refs.d.ts +89 -0
  74. package/dist/core/runner/prepare-run-refs.js +39 -0
  75. package/dist/core/runner/prepare-safety-scan.d.ts +3 -7
  76. package/dist/core/runner/prepare-safety-scan.js +1 -2
  77. package/dist/core/runner/prepare-suspend-saga.d.ts +170 -0
  78. package/dist/core/runner/prepare-suspend-saga.js +308 -0
  79. package/dist/core/runner/prepare-task.d.ts +14 -1976
  80. package/dist/core/runner/prepare-task.js +182 -5162
  81. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +111 -0
  82. package/dist/core/runner/prepare-tool-disclosure-mount.js +219 -0
  83. package/dist/core/runner/prepare-turn-wiring.d.ts +154 -0
  84. package/dist/core/runner/prepare-turn-wiring.js +201 -0
  85. package/dist/core/runner/prepare-wiring-manifest.d.ts +192 -0
  86. package/dist/core/runner/prepare-wiring-manifest.js +247 -0
  87. package/dist/core/runner/prepare-workspace-restore.d.ts +3 -56
  88. package/dist/core/runner/prepare-workspace-restore.js +4 -38
  89. package/dist/core/runner/prompt-hash-salt.d.ts +1 -0
  90. package/dist/core/runner/prompt-hash-salt.js +2 -0
  91. package/dist/core/runner/remote-env-retry.d.ts +29 -0
  92. package/dist/core/runner/remote-env-retry.js +16 -0
  93. package/dist/core/runner/rollback-stack.d.ts +32 -0
  94. package/dist/core/runner/rollback-stack.js +30 -0
  95. package/dist/core/runner/runtask.js +1 -1
  96. package/dist/core/runner/workspace-path.d.ts +33 -0
  97. package/dist/core/runner/workspace-path.js +22 -0
  98. package/dist/core/session.d.ts +12 -0
  99. package/dist/core/session.js +3 -0
  100. package/dist/core/tool-policy.d.ts +16 -0
  101. package/dist/core/tool-policy.js +3 -0
  102. package/dist/core/types.d.ts +2 -2
  103. package/dist/core/write-protect.js +3 -2
  104. package/package.json +6 -2
  105. /package/dist/core/runner/{prepare-announce-once.d.ts → announce-once-ledger.d.ts} +0 -0
  106. /package/dist/core/runner/{prepare-announce-once.js → announce-once-ledger.js} +0 -0
@@ -0,0 +1,230 @@
1
+ import { DEFAULT_CHARS_PER_TOKEN, estimateContextTokens } from "../../internal/harness.js";
2
+ import { clearStaleToolResults, createClearedProjectionLedger, dropEmptyFailureAssistants, editBudget, replayClearedProjection, resolveTriggerWindow } from "../context-edit.js";
3
+ import { dropOrphanToolResults, guardBudget, insertTrimNotice, trimToBudget } from "../context-guard.js";
4
+ import { AGGREGATE_MEDIA_BUDGET_BYTES, capAggregateMediaBytes } from "../media-byte-cap.js";
5
+ import { capAggregateToolResults } from "../tool-result-budget.js";
6
+ import { createOffloadPersist } from "../tool-result-store.js";
7
+ import { emitTrace } from "../trace.js";
8
+ import { deliverEngineNotice } from "../types.js";
9
+ import { applyGitFrameGuard } from "./git-status-frame.js";
10
+ export function prepareContextLane(input) {
11
+ const { model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, requestLossyRef, trimPressureRef } = input;
12
+ const editAt = editBudget(model);
13
+ const guardAt = guardBudget(model);
14
+ const microCompact = buildMicroCompactState(deps, model, sessionId, runId, offloadStore, microCompactKnob);
15
+ const charsPerToken = model.charsPerToken ?? DEFAULT_CHARS_PER_TOKEN;
16
+ harness.on("context", async ({ messages, recheck, signal }) => {
17
+ batchHaltRef.current = undefined;
18
+ const pendingProjectionObservations = [];
19
+ try {
20
+ const healed = dropEmptyFailureAssistants(messages);
21
+ const replay = replayClearedProjection(healed, microCompact.ledger);
22
+ const replayed = replay.messages;
23
+ const capped = await capAggregateToolResults(replayed, {
24
+ store: offloadStore,
25
+ sessionId,
26
+ onCapped: (info) => pendingProjectionObservations.push(() => emitTrace(deps.tracer, () => ({
27
+ kind: "tool_result.capped",
28
+ version: 1,
29
+ taskId: hostTaskId,
30
+ ...(info.tool !== undefined ? { tool: info.tool } : {}),
31
+ sizeChars: info.sizeChars,
32
+ storeFallback: info.storeFallback,
33
+ ts: Date.now(),
34
+ }))),
35
+ });
36
+ const mediaCapped = capAggregateMediaBytes(capped, {
37
+ limitBytes: deps.mediaByteCapBytes ?? AGGREGATE_MEDIA_BUDGET_BYTES,
38
+ onStripped: (info) => {
39
+ const onMediaStripped = deps.onMediaStripped;
40
+ if (onMediaStripped !== undefined)
41
+ pendingProjectionObservations.push(() => onMediaStripped(info));
42
+ },
43
+ });
44
+ let ccPass;
45
+ const edited = microCompact.machine === "off" ? mediaCapped : clearStaleToolResults(mediaCapped, {
46
+ budgetTokens: editAt,
47
+ machine: microCompact.machine,
48
+ ...(microCompact.machine === "cc" || microCompact.clearOnRejection ? { recognizeCcMarkers: true } : {}),
49
+ ...(microCompact.machine === "cc"
50
+ ? {
51
+ clearSource: replayed,
52
+ onCleared: (pass) => { ccPass = pass; },
53
+ }
54
+ : {}),
55
+ anchoredTotalTokens: estimateContextTokens(mediaCapped, charsPerToken).tokens,
56
+ charsPerToken,
57
+ ...(offloadStore
58
+ ? {
59
+ offload: {
60
+ persist: createOffloadPersist(offloadStore, sessionId, deps.onNotice),
61
+ },
62
+ }
63
+ : {}),
64
+ });
65
+ if (ccPass !== undefined) {
66
+ recordFrontierClears({ pass: ccPass, index: replay.index, edited, ledger: microCompact.ledger, tracer: deps.tracer, taskId: hostTaskId, trigger: "frontier" });
67
+ }
68
+ const chain = await runGuardChain({
69
+ edited, replayed, index: replay.index, microCompact, guardAt, charsPerToken, offloadStore,
70
+ sessionId, taskId: hostTaskId, deps, trimPressureRef, recheck, signal,
71
+ pendingProjectionObservations,
72
+ });
73
+ if ("earlyReturn" in chain) {
74
+ return chain.earlyReturn;
75
+ }
76
+ const { working, trimDroppedMessages } = chain;
77
+ let trimmed = chain.trimmed;
78
+ trimmed = applyGitFrameGuard({
79
+ before: working,
80
+ trimmed,
81
+ budgetTokens: guardAt,
82
+ ref: gitStatusRef,
83
+ charsPerToken,
84
+ onDegrade: (message) => {
85
+ try {
86
+ deps.onError?.(new Error(message), { phase: "degraded", sessionId, classification: "env-git-snapshot" });
87
+ }
88
+ catch {
89
+ }
90
+ },
91
+ });
92
+ const swept = dropOrphanToolResults(trimmed);
93
+ if (swept.dropped.length > 0) {
94
+ try {
95
+ deps.onError?.(new Error(`context belt: dropped ${swept.dropped.length} orphan toolResult(s) whose tool-call assistant was not in the request view — ` +
96
+ swept.dropped
97
+ .map((d) => `toolCallId=${d.toolCallId} at index ${d.index} (prev=${d.prevRole}, next=${d.nextRole})`)
98
+ .join("; ")), { phase: "hook", sessionId });
99
+ }
100
+ catch {
101
+ }
102
+ }
103
+ requestLossyRef.current =
104
+ replayed !== healed ||
105
+ capped !== replayed ||
106
+ mediaCapped !== capped ||
107
+ edited !== mediaCapped ||
108
+ working !== edited ||
109
+ trimDroppedMessages ||
110
+ swept.dropped.length > 0 ||
111
+ gitStatusRef.overBudgetShrunk === true;
112
+ microCompact.projectionRef.current = { messages: swept.messages, keyOf: replay.index.keyOf };
113
+ return { messages: swept.messages };
114
+ }
115
+ finally {
116
+ flushProjectionObservations(pendingProjectionObservations);
117
+ }
118
+ });
119
+ return { microCompact, charsPerToken };
120
+ }
121
+ function buildMicroCompactState(deps, model, sessionId, runId, offloadStore, knob) {
122
+ const triggerWindow = resolveTriggerWindow(model);
123
+ if (triggerWindow.clamped) {
124
+ deliverEngineNotice(deps.onNotice, {
125
+ code: "config.autocompact_window_clamped",
126
+ message: `model "${model.id}" declares autoCompactTokens=${triggerWindow.declaredAutoCompactTokens} ABOVE its physical window ` +
127
+ `${triggerWindow.physicalWindow}; the autocompact window only ever LOWERS the trigger-side geometry, so the value was ` +
128
+ `clamped to the physical window — fix the model config (declare a value at or below the physical window, or omit it)`,
129
+ detail: {
130
+ modelId: model.id,
131
+ declaredAutoCompactTokens: triggerWindow.declaredAutoCompactTokens,
132
+ physicalWindow: triggerWindow.physicalWindow,
133
+ sessionId,
134
+ runId,
135
+ },
136
+ });
137
+ }
138
+ return {
139
+ machine: knob.machine,
140
+ clearOnRejection: knob.clearOnRejection,
141
+ ledger: createClearedProjectionLedger(),
142
+ projectionRef: {},
143
+ inTurnCompactionRef: {},
144
+ ...(offloadStore ? { offloadPersist: createOffloadPersist(offloadStore, sessionId, deps.onNotice) } : {}),
145
+ };
146
+ }
147
+ function recordFrontierClears(args) {
148
+ for (const { index } of args.pass.clears) {
149
+ const at = args.index.keyAt(index);
150
+ const clearedMsg = args.edited[index];
151
+ const markerText = clearedMsg.content?.[0]?.text;
152
+ if (at !== undefined && typeof markerText === "string") {
153
+ args.ledger.entries.set(at.key, { marker: markerText, groupCount: at.groupCount, fp: at.fp });
154
+ }
155
+ }
156
+ emitTrace(args.tracer, () => ({
157
+ kind: "context.mc_clear",
158
+ version: 1,
159
+ taskId: args.taskId,
160
+ clearedCount: args.pass.clears.length,
161
+ tokensSavedEstimate: args.pass.tokensSavedEstimate,
162
+ trigger: args.trigger,
163
+ ts: Date.now(),
164
+ }));
165
+ }
166
+ function flushProjectionObservations(pending) {
167
+ for (const fire of pending.splice(0)) {
168
+ try {
169
+ fire();
170
+ }
171
+ catch {
172
+ }
173
+ }
174
+ }
175
+ export async function runGuardChain(args) {
176
+ const { edited, microCompact, guardAt, charsPerToken, deps, taskId } = args;
177
+ const applyGuardTrim = (view, anchoredTotal) => {
178
+ let trimmed = trimToBudget(view, guardAt, anchoredTotal, charsPerToken);
179
+ const dropped = trimmed.length < view.length;
180
+ if (dropped) {
181
+ args.trimPressureRef.droppedMessages = true;
182
+ const droppedCount = view.length - trimmed.length;
183
+ trimmed = insertTrimNotice(trimmed);
184
+ emitTrace(deps.tracer, () => ({
185
+ kind: "context.trim",
186
+ version: 1,
187
+ taskId,
188
+ dropped: droppedCount,
189
+ ts: Date.now(),
190
+ }));
191
+ }
192
+ return { trimmed, dropped };
193
+ };
194
+ let working = edited;
195
+ let anchoredWorking = estimateContextTokens(working, charsPerToken).tokens;
196
+ if (microCompact.machine === "legacy") {
197
+ const t = applyGuardTrim(working, anchoredWorking);
198
+ return { working, trimmed: t.trimmed, trimDroppedMessages: t.dropped };
199
+ }
200
+ if (microCompact.machine === "off" && anchoredWorking > guardAt) {
201
+ let blockingPass;
202
+ const blockingEdited = clearStaleToolResults(working, {
203
+ budgetTokens: guardAt,
204
+ machine: "cc",
205
+ clearSource: args.replayed,
206
+ onCleared: (pass) => { blockingPass = pass; },
207
+ anchoredTotalTokens: anchoredWorking,
208
+ charsPerToken,
209
+ ...(args.offloadStore ? { offload: { persist: createOffloadPersist(args.offloadStore, args.sessionId, deps.onNotice) } } : {}),
210
+ });
211
+ if (blockingPass !== undefined) {
212
+ recordFrontierClears({ pass: blockingPass, index: args.index, edited: blockingEdited, ledger: microCompact.ledger, tracer: deps.tracer, taskId, trigger: "blocking" });
213
+ working = blockingEdited;
214
+ anchoredWorking = estimateContextTokens(working, charsPerToken).tokens;
215
+ }
216
+ }
217
+ if (anchoredWorking > guardAt && args.recheck !== true) {
218
+ const compacted = (await microCompact.inTurnCompactionRef.current?.(args.signal, anchoredWorking)) === true;
219
+ if (compacted) {
220
+ args.pendingProjectionObservations.length = 0;
221
+ return { earlyReturn: { messages: working, adoptSessionRebuild: true } };
222
+ }
223
+ if (args.signal?.aborted === true) {
224
+ args.pendingProjectionObservations.length = 0;
225
+ return { working, trimmed: working, trimDroppedMessages: false };
226
+ }
227
+ }
228
+ const t = applyGuardTrim(working, anchoredWorking);
229
+ return { working, trimmed: t.trimmed, trimDroppedMessages: t.dropped };
230
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * design/390 M11 — prepareTask's TOOL-FACE FOLD + DEFERRED CLASSIFICATION phase: the exclusion valve's
3
+ * true unmount (in place, on the shared mount array), the full-shell anti-drift invariant, the classic-
4
+ * profile description swap, `classifyDeferredOverFace` (the one classifier both counterfactual passes
5
+ * run), the built-in memory pairs' support-name pre-check (sole-cause counterfactual, whole-group
6
+ * retraction, the engine pair's recall paragraph stripped back out of the memory block) and the final
7
+ * `deferred` set. The body is the driver's stretch moved verbatim (one exception, named below); the
8
+ * interface is the dependency list it had implicitly (design/238 R-1).
9
+ *
10
+ * SYNCHRONOUS FUNCTION, SYNCHRONOUS CALL: the stretch has no await, so the phase adds no yield between
11
+ * the git-status probe before it and the content-origin wrap after it (design/390 §1.5 S1: a stretch
12
+ * with no await is extracted as a sync function — an `async` wrapper would open a microtask window).
13
+ *
14
+ * The one non-move: the stretch used to write `sharedMemoryPairMounted = false` /
15
+ * `memoryEnginePairMounted = false` on the retraction arm. Nothing reads either flag after this stretch
16
+ * (the driver's next reader was this stretch's own pair derivation, which runs first), so the two dead
17
+ * writes are not carried into the Result — the flags arrive as borrowed-readonly inputs.
18
+ *
19
+ * Throws pass through unchanged: `internal.full_shell_reachable_mismatch` (an engine invariant, never a
20
+ * configuration error). The reserved-name refusal for a standing ToolSearch collision is NOT here — it
21
+ * belongs to the disclosure mount downstream, which reads the `deferred` set this phase returns.
22
+ */
23
+ import type { AgentTool } from "../../internal/harness.js";
24
+ import type { Model } from "../../internal/llm.js";
25
+ import type { MaterializedA2a } from "../a2a.js";
26
+ import type { MaterializedMcp } from "../mcp.js";
27
+ import type { RunnerDeps, TaskSpec } from "../types.js";
28
+ import type { AnnounceOnceSink, ToolFaceSnapshot } from "./contracts.js";
29
+ export interface PrepareDeferClassifyInput {
30
+ /** borrowed-readonly — the REBOUND spec (`spec′`); only `tools` is read (the caller roster the
31
+ * classifier judges: names for the full-schema filter, specs for the `defer` declarations). */
32
+ spec: Pick<TaskSpec, "tools">;
33
+ /** borrowed-readonly — deployment deps; only `deferMode` is read (the classifier's mode knob). */
34
+ deps: Pick<RunnerDeps, "deferMode">;
35
+ /** borrowed-mutable — the shared mount array (later refs alias it; in-place contract, never
36
+ * re-created). Mutated here, in order: `splice` of every excluded name; element REPLACEMENT with a
37
+ * shallow copy for the classic-profile description swap (the caller's ToolSpec object is never
38
+ * mutated); `splice` of a retracted memory group. After this phase: the content-origin wrap
39
+ * (element replacement with the wrapped object), the disclosure mount, the MCP refresh re-splice. */
40
+ tools: AgentTool[];
41
+ /** borrowed-readonly — the frozen task-start tool face: `exclude` (the valve), `defer` (operator
42
+ * defers, exact wire names), `alwaysLoad` (the inline pins). */
43
+ toolFaceSnapshot: Pick<ToolFaceSnapshot, "exclude" | "defer" | "alwaysLoad">;
44
+ /** borrowed-readonly — whether a real execution env is mounted (the full-shell invariant runs only
45
+ * where a shell could be on the roster). */
46
+ handsEnabled: boolean;
47
+ /** borrowed-readonly — the spec-time precomputation the invariant checks the post-exclusion roster
48
+ * against (design/199 D-6): a mismatch is an engine bug, thrown. */
49
+ fullShellReachable: boolean;
50
+ /** borrowed-readonly — the resolved prompt profile (`classic` swaps in `descriptionClassic`). */
51
+ promptProfile: "simple" | "classic";
52
+ /** borrowed-readonly — the resolved model (the classifier's constant-defer arm reads its shape). */
53
+ model: Model;
54
+ /** borrowed-readonly — the materialized MCP roster; only `tools` is read (protocol names for the
55
+ * remote-roster arm; `mcpAlwaysLoad` self-declarations, losing to an explicit operator defer). */
56
+ mcp: Pick<MaterializedMcp, "tools">;
57
+ /** borrowed-readonly — the materialized A2A roster; only `tools` is read (protocol names). */
58
+ a2a: Pick<MaterializedA2a, "tools">;
59
+ /** borrowed-readonly — whether the shared-memory pair mounted (project-context phase). */
60
+ sharedMemoryPairMounted: boolean;
61
+ /** borrowed-readonly — whether the engine-memory pair mounted (project-context phase). */
62
+ memoryEnginePairMounted: boolean;
63
+ /** borrowed-readonly — the memory block as the project-context phase left it. The engine pair's
64
+ * retraction strips the recall paragraph from it and the stripped value returns as
65
+ * {@link PrepareDeferClassifyResult.memoryBlock}; the input value itself is never mutated. */
66
+ memoryBlock: string | undefined;
67
+ /** borrowed-readonly — the exact recall-discipline segment the memory phase composed (the strip
68
+ * removes THIS string, never a re-composition). */
69
+ memoryRecallSegment: string | undefined;
70
+ /** borrowed-readonly — the once-per-session ledger's deduplicated operator sink (a retraction is a
71
+ * session-level fact, announced once). */
72
+ onceLedger: AnnounceOnceSink;
73
+ /** borrowed-readonly — the acquired session's id (operator-line context). */
74
+ sessionId: string;
75
+ }
76
+ export interface PrepareDeferClassifyResult {
77
+ /** owned — the FINAL deferred set over the post-exclusion, post-retraction roster: the disclosure
78
+ * mount, the deferred registry and `Prepared.deferredToolNames` all read this one set (there is
79
+ * deliberately no second classification). Empty ⇒ nothing is deferred. */
80
+ deferred: Set<string>;
81
+ /** owned — the memory block after the engine pair's retraction strip (unchanged when no retraction
82
+ * ran). The driver binds it under the name the prompt assembly reads. */
83
+ memoryBlock: string | undefined;
84
+ }
85
+ /** The M11 phase body — prepareTask's tool-face fold + deferred classification, verbatim (module header). */
86
+ export declare function prepareDeferClassify(input: PrepareDeferClassifyInput): PrepareDeferClassifyResult;
@@ -0,0 +1,107 @@
1
+ import { getToolContract } from "../../prompt-assembly/tool-catalog.js";
2
+ import { FULL_SHELL_CONTRACT_ID } from "../../tools/fs/index.js";
3
+ import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
4
+ import { MEMORY_ENGINE_TOOL_NAMES } from "../memory-engine/tools.js";
5
+ import { SHARED_MEMORY_TOOL_NAMES } from "../shared-memory/types.js";
6
+ import { TOOL_SEARCH_NAME, classifyDeferred } from "./tool-disclosure.js";
7
+ function explicitlyDeferredMemoryTrio(mounted, roster, deferNames) {
8
+ return mounted ? MEMORY_ENGINE_TOOL_NAMES.filter((n) => roster.some((t) => t.name === n) && (deferNames ?? []).includes(n)) : [];
9
+ }
10
+ function memoryGroupRetractionSet(builtinDeferPairNames, engineTrioInPlay) {
11
+ return new Set([...builtinDeferPairNames, ...(engineTrioInPlay ? MEMORY_ENGINE_TOOL_NAMES : [])]);
12
+ }
13
+ export function prepareDeferClassify(input) {
14
+ const { spec, deps, tools, toolFaceSnapshot, handsEnabled, fullShellReachable, promptProfile, model, mcp, a2a, sharedMemoryPairMounted, memoryEnginePairMounted, memoryRecallSegment, onceLedger, sessionId } = input;
15
+ let memoryBlock = input.memoryBlock;
16
+ if (toolFaceSnapshot.exclude !== undefined && toolFaceSnapshot.exclude.length > 0) {
17
+ const excluded = new Set(toolFaceSnapshot.exclude);
18
+ for (let i = tools.length - 1; i >= 0; i--)
19
+ if (excluded.has(tools[i].name))
20
+ tools.splice(i, 1);
21
+ }
22
+ if (handsEnabled) {
23
+ const fullShellOnRoster = tools.some((t) => t.name === "Bash" && getToolContract(t, "core").contractId === FULL_SHELL_CONTRACT_ID);
24
+ if (fullShellOnRoster !== fullShellReachable) {
25
+ const e = new Error(`internal invariant: fullShellReachable=${String(fullShellReachable)} but the post-exclusion roster ` +
26
+ `${fullShellOnRoster ? "carries" : "does not carry"} the full shell (${FULL_SHELL_CONTRACT_ID}). ` +
27
+ `A new roster-affecting mechanism was added without updating the predicate.`);
28
+ e.code = "internal.full_shell_reachable_mismatch";
29
+ throw e;
30
+ }
31
+ }
32
+ if (promptProfile === "classic") {
33
+ for (let i = 0; i < tools.length; i++) {
34
+ const t = tools[i];
35
+ if (t.descriptionClassic !== undefined)
36
+ tools[i] = { ...t, description: t.descriptionClassic };
37
+ }
38
+ }
39
+ const userToolNames = (spec.tools ?? []).map((t) => t.name);
40
+ const protocolToolNames = [...mcp.tools.map((t) => t.name), ...a2a.tools.map((t) => t.name)];
41
+ const mcpAlwaysLoadNames = mcp.tools
42
+ .filter((t) => t.mcpAlwaysLoad === true && !(toolFaceSnapshot.defer ?? []).includes(t.name))
43
+ .map((t) => t.name);
44
+ const classifyDeferredOverFace = (face, builtinDeferNames) => {
45
+ const deferredSet = classifyDeferred({
46
+ specs: spec.tools ?? [],
47
+ protocolToolNames,
48
+ fullTools: face.filter((t) => userToolNames.includes(t.name) || protocolToolNames.includes(t.name)),
49
+ deferMode: deps.deferMode,
50
+ model,
51
+ deferNames: [...(toolFaceSnapshot.defer ?? []), ...builtinDeferNames].filter((n) => face.some((t) => t.name === n)),
52
+ alwaysLoadNames: [
53
+ ASK_USER_QUESTION_TOOL_NAME,
54
+ ...(toolFaceSnapshot.alwaysLoad ?? []),
55
+ ...mcpAlwaysLoadNames,
56
+ ],
57
+ });
58
+ for (const n of [...deferredSet]) {
59
+ if (!face.some((t) => t.name === n))
60
+ deferredSet.delete(n);
61
+ }
62
+ return deferredSet;
63
+ };
64
+ const sharedMemoryPair = sharedMemoryPairMounted ? SHARED_MEMORY_TOOL_NAMES.filter((n) => tools.some((t) => t.name === n)) : [];
65
+ const memoryEnginePair = explicitlyDeferredMemoryTrio(memoryEnginePairMounted, tools, toolFaceSnapshot.defer);
66
+ const builtinDeferPairNames = [...sharedMemoryPair, ...memoryEnginePair];
67
+ let deferred;
68
+ if (builtinDeferPairNames.length > 0) {
69
+ const withoutPairs = tools.filter((t) => !builtinDeferPairNames.some((n) => n === t.name));
70
+ const d1 = classifyDeferredOverFace(tools, builtinDeferPairNames);
71
+ const d0 = classifyDeferredOverFace(withoutPairs, []);
72
+ const soleCause = d0.size === 0 && d1.size > 0;
73
+ const supportNameTaken = tools.some((t) => t.name === TOOL_SEARCH_NAME || (t.aliases ?? []).includes(TOOL_SEARCH_NAME));
74
+ if (soleCause && supportNameTaken) {
75
+ const retractNames = memoryGroupRetractionSet(builtinDeferPairNames, memoryEnginePair.length > 0);
76
+ for (let i = tools.length - 1; i >= 0; i--) {
77
+ if (retractNames.has(tools[i].name))
78
+ tools.splice(i, 1);
79
+ }
80
+ if (sharedMemoryPair.length > 0) {
81
+ onceLedger.onError(new Error(`Shared memory tools ${SHARED_MEMORY_TOOL_NAMES.join("/")} were NOT mounted: mounting them would inject ` +
82
+ `the "${TOOL_SEARCH_NAME}" tool, whose name this task already declares — the pair mounts together or not at all.`), { phase: "config", sessionId, classification: "shared-memory-not-mounted" });
83
+ }
84
+ if (memoryEnginePair.length > 0) {
85
+ if (memoryBlock !== undefined && memoryRecallSegment !== undefined) {
86
+ const segment = memoryRecallSegment;
87
+ if (memoryBlock === segment)
88
+ memoryBlock = undefined;
89
+ else if (memoryBlock.includes(`\n\n${segment}`))
90
+ memoryBlock = memoryBlock.replace(`\n\n${segment}`, "");
91
+ else if (memoryBlock.startsWith(`${segment}\n\n`))
92
+ memoryBlock = memoryBlock.slice(segment.length + 2);
93
+ }
94
+ onceLedger.onError(new Error(`Memory tools ${MEMORY_ENGINE_TOOL_NAMES.join("/")} were NOT mounted: mounting them would inject ` +
95
+ `the "${TOOL_SEARCH_NAME}" tool, whose name this task already declares — these tools mount together or not at all.`), { phase: "config", sessionId, classification: "memory-tools-not-mounted" });
96
+ }
97
+ deferred = d0;
98
+ }
99
+ else {
100
+ deferred = d1;
101
+ }
102
+ }
103
+ else {
104
+ deferred = classifyDeferredOverFace(tools, []);
105
+ }
106
+ return { deferred, memoryBlock };
107
+ }
@@ -0,0 +1,104 @@
1
+ import type { AgentTool, ExecutionEnv, WorkspaceState } from "../../internal/harness.js";
2
+ import { createSendMessageTool } from "../../agents/send-message-tool.js";
3
+ import { type PeerLaneRefs } from "../../agents/peer-session-drain.js";
4
+ import type { SubagentRetainLedger } from "../../agents/retain-ledger.js";
5
+ import { type WorktreeSessionRef } from "../../tools/worktree.js";
6
+ import type { CwdRef } from "../../tools/fs/index.js";
7
+ import { type ToolResultStore } from "../tool-result-store.js";
8
+ import type { RunnerDeps, TaskSpec, ToolEffect, ToolExecuteContext } from "../types.js";
9
+ import type { PrepareResume, RunInternals, ToolFaceSnapshot } from "./contracts.js";
10
+ /** The runner the SendMessage / AgentTranscript mounts accept — spelled as the factory's own option type so this phase
11
+ * names no runner module of its own (the narrow `RunnerSelfSeat` contract is a design decision still open). */
12
+ type DelegationMountRunner = Parameters<typeof createSendMessageTool>[0]["runner"];
13
+ export interface PrepareDelegationSurfaceInput {
14
+ /** borrowed-readonly — the REBOUND spec. Read: `tools` (the shadow checks and the revival spawner's delegation-tool
15
+ * lookup), `oneShot` (the read/kill faces' completion promises), `retainBackgroundProcesses` (the Monitor receipt),
16
+ * `handsReadOnly` is NOT read here (the hands mount decided the surface). Never mutated. */
17
+ spec: Pick<TaskSpec, "tools" | "oneShot" | "retainBackgroundProcesses">;
18
+ /** borrowed-readonly — the deployment seats the mounts read: `workflowRunStore` / `backgroundAgentStore` / `mailboxStore` (the
19
+ * read/kill faces and the tier-3 revival seams), `rosterStore`, `peerAdmission`, `peerDirectory`, `onNotice`,
20
+ * `onBackgroundChildEvent`, `tracer`. */
21
+ deps: Pick<RunnerDeps, "workflowRunStore" | "backgroundAgentStore" | "mailboxStore" | "rosterStore" | "peerAdmission" | "peerDirectory" | "onNotice" | "onBackgroundChildEvent" | "tracer">;
22
+ /** borrowed-readonly — the trusted spawn-side channel: `parentNotify` (the delegated-child door), the uplink / sibling /
23
+ * identity seats the SendMessage mount forwards, `onTaskNotification`, `onSubagentSpawn`. Never mutated. */
24
+ internals: Pick<RunInternals, "parentNotify" | "parentPeerRef" | "parentRetainLedger" | "explicitAgentName" | "parentTaskId" | "parentSessionId" | "peerSelfRef" | "peerInboundChainRef" | "onTaskNotification" | "onSubagentSpawn"> | undefined;
25
+ /** borrowed-readonly — the resume leg, or undefined: `seed.activeWorktree` seeds the worktree session (checkpoint first). */
26
+ resume: Pick<PrepareResume, "seed"> | undefined;
27
+ /** borrowed-readonly — the acquired session id (the mounts' session axis). */
28
+ sessionId: string;
29
+ /** borrowed-readonly — the per-task identity (the mounts' owner axis). */
30
+ hostTaskId: string;
31
+ /** borrowed-readonly — the registry scope the mounts register in. */
32
+ taskScope: string;
33
+ /** borrowed-readonly — the SETTLED task root (the worktree tools' repoRoot). */
34
+ taskRootFinal: string;
35
+ /** borrowed-readonly — the run's resolved env the Monitor and worktree tools operate. */
36
+ executionEnv: ExecutionEnv;
37
+ /** borrowed-readonly — the trusted Runner self-reference the SendMessage / AgentTranscript mounts execute through; undefined
38
+ * when prepareTask runs standalone (then neither mounts). */
39
+ runnerSelf: DelegationMountRunner | undefined;
40
+ /** borrowed-readonly — the background/workflow door's first half: the hands mount decided a real shell with write hands. */
41
+ backgroundTaskToolsActive: boolean;
42
+ /** borrowed-readonly — the door's second half: the Workflow tool is on the roster. */
43
+ workflowToolsActive: boolean;
44
+ /** borrowed-readonly — the peer lane's mount verdict for this leg. */
45
+ peerLaneActive: boolean;
46
+ /** borrowed-readonly — whether the BUILT-IN SendMessage is on this face (the ListAgents mount's second term). */
47
+ peerSendMessageBuiltIn: boolean;
48
+ /** borrowed-mutable — the lane's late-bound facts; the SendMessage mount hands the SAME object to its peer seats (identity is
49
+ * the contract: the wiring station fills `askEffective` later). Not written here. */
50
+ peerLaneRefs: PeerLaneRefs;
51
+ /** borrowed-mutable — the run's shared roster (`Prepared.tools`, whose JSDoc carries the writer table). Writers here: the
52
+ * eight mounts (TaskOutput, TaskStop, SendMessage, AgentTranscript, ListAgents, Monitor, the worktree pair, the offload
53
+ * reader), every one a push. */
54
+ tools: AgentTool[];
55
+ /** borrowed-mutable — the effect collection (`Prepared.toolEffects`). Writers here: TaskOutput → read, TaskStop / SendMessage
56
+ * (built-in mount only) / Monitor / EnterWorktree / ExitWorktree → write. */
57
+ toolEffects: Map<string, ToolEffect>;
58
+ /** borrowed-mutable — the explicit-negative axis declarations. Writer here: the built-in SendMessage mount's `egress: false`
59
+ * (report-face only). */
60
+ axisExplicitNegatives: Map<string, {
61
+ irreversible?: false;
62
+ egress?: false;
63
+ }>;
64
+ /** borrowed-mutable — the env-hand tool-name set the read posture reads. Writers here: Monitor and the two worktree tools
65
+ * (membership minted at the mount). */
66
+ envHandToolNames: Set<string>;
67
+ /** borrowed-readonly — the frozen tool-face snapshot (`exclude` feeds the ListAgents mount predicate). */
68
+ toolFaceSnapshot: Pick<ToolFaceSnapshot, "exclude">;
69
+ /** borrowed-readonly — the large-result wrapper for first-party mounts. */
70
+ firstPartyOffload: (tool: AgentTool) => AgentTool;
71
+ /** borrowed-readonly — the run's offload store, or undefined when offload is disabled (then no reader mounts and the
72
+ * read/kill faces spill nowhere). */
73
+ offloadStore: ToolResultStore | undefined;
74
+ /** borrowed-readonly — the retain ledger the SendMessage mount continues children through (absent ⇒ honest not-retained). */
75
+ subagentRetain: SubagentRetainLedger | undefined;
76
+ /** borrowed-readonly — the caller-tool ctx enrichment: the revival spawner re-enters the delegation tool through it, and
77
+ * the SendMessage / AgentTranscript mounts activate their ctx-first arms with it. */
78
+ enrichSpecToolCtx: (ctx: ToolExecuteContext) => ToolExecuteContext;
79
+ /** borrowed-mutable — the hands mount's tracked-cwd seat: present ⇔ real shell + write hands; the worktree pair's mount
80
+ * predicate. Not written by this phase, but the tools it mounts over it ARE its writers (design/238 R-3): EnterWorktree and
81
+ * ExitWorktree assign `current` at run time (the hands band's shared cwd then points at the worktree, and back); the
82
+ * Monitor only reads it. The cell must stay the hands mount's own object — a copy would strand those writes. */
83
+ handsCwdRef: CwdRef | undefined;
84
+ /** borrowed-readonly — the workspace-state snapshot (the worktree seed's continuation rung), or undefined. */
85
+ wsSnapshot: Pick<WorkspaceState, "activeWorktree"> | undefined;
86
+ /** borrowed-readonly — the rebase of a snapshot path onto this leg's root. */
87
+ rebaseWsPath: (p: string) => string;
88
+ /** borrowed-readonly — the rebase of a checkpoint path onto this leg's root. */
89
+ rebaseRestoredPath: (p: string) => string;
90
+ /** borrowed-mutable — the workspace-state settle seat; the worktree mount stamps `restoredWorktreeDir` on it once. */
91
+ workspaceStateSettle: {
92
+ restoredWorktreeDir?: string;
93
+ } | undefined;
94
+ }
95
+ export interface PrepareDelegationSurfaceResult {
96
+ /** owned — whether the background/workflow door is open (TaskOutput / TaskStop / AgentTranscript ride it). */
97
+ delegationSurfaceActive: boolean;
98
+ /** borrowed-mutable — the EnterWorktree session's shared serializable state (`Prepared.worktreeSessionRef`), minted iff the
99
+ * worktree pair mounted. Writers: the worktree tools at run time (Enter/Exit); the suspend serializer reads it. */
100
+ worktreeSessionRef: WorktreeSessionRef | undefined;
101
+ }
102
+ /** The M6 phase body — prepareTask's delegation-surface stretch, verbatim (see the module header). */
103
+ export declare function prepareDelegationSurface(input: PrepareDelegationSurfaceInput): PrepareDelegationSurfaceResult;
104
+ export {};