@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.
- package/CHANGELOG.md +44 -0
- package/dist/core/auto-mode.d.ts +9 -0
- package/dist/core/auto-mode.js +11 -0
- package/dist/core/checkpoint-store.js +5 -1
- package/dist/core/protocol-table.d.ts +5 -0
- package/dist/core/protocol-table.js +1 -0
- package/dist/core/runner/abort-race.d.ts +41 -0
- package/dist/core/runner/abort-race.js +38 -0
- package/dist/core/runner/checkpoint-scope.d.ts +44 -0
- package/dist/core/runner/checkpoint-scope.js +7 -0
- package/dist/core/runner/compaction-call-options.d.ts +1 -1
- package/dist/core/runner/content-ask-bindings.d.ts +27 -0
- package/dist/core/runner/content-ask-bindings.js +1 -0
- package/dist/core/runner/contracts.d.ts +1990 -0
- package/dist/core/runner/contracts.js +1 -0
- package/dist/core/runner/denial-limit-arms.d.ts +24 -1
- package/dist/core/runner/denial-limit-arms.js +21 -0
- package/dist/core/runner/derived-route-fallback.d.ts +34 -0
- package/dist/core/runner/derived-route-fallback.js +16 -0
- package/dist/core/runner/inherited-ask-grants.d.ts +46 -0
- package/dist/core/runner/inherited-ask-grants.js +29 -0
- package/dist/core/runner/park-commit.d.ts +108 -0
- package/dist/core/runner/park-commit.js +32 -0
- package/dist/core/runner/{prepare-permission-rules.d.ts → permission-rule-lanes.d.ts} +109 -3
- package/dist/core/runner/{prepare-permission-rules.js → permission-rule-lanes.js} +47 -1
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +1 -1
- package/dist/core/runner/prepare-ask-lane.d.ts +110 -0
- package/dist/core/runner/prepare-ask-lane.js +133 -0
- package/dist/core/runner/prepare-boundary-parks.d.ts +105 -0
- package/dist/core/runner/prepare-boundary-parks.js +169 -0
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +170 -0
- package/dist/core/runner/prepare-caps-and-workflow.js +255 -0
- package/dist/core/runner/prepare-config-doors.d.ts +2 -10
- package/dist/core/runner/prepare-context-lane.d.ts +119 -0
- package/dist/core/runner/prepare-context-lane.js +230 -0
- package/dist/core/runner/prepare-defer-classify.d.ts +86 -0
- package/dist/core/runner/prepare-defer-classify.js +107 -0
- package/dist/core/runner/prepare-delegation-surface.d.ts +104 -0
- package/dist/core/runner/prepare-delegation-surface.js +144 -0
- package/dist/core/runner/prepare-execution-env.d.ts +54 -0
- package/dist/core/runner/prepare-execution-env.js +86 -0
- package/dist/core/runner/prepare-file-history.d.ts +95 -0
- package/dist/core/runner/prepare-file-history.js +383 -0
- package/dist/core/runner/prepare-gate-stations.d.ts +177 -0
- package/dist/core/runner/prepare-gate-stations.js +290 -0
- package/dist/core/runner/prepare-hands-readface.d.ts +9 -11
- package/dist/core/runner/prepare-hands-readface.js +1 -1
- package/dist/core/runner/prepare-inherited-gate.d.ts +268 -0
- package/dist/core/runner/prepare-inherited-gate.js +266 -0
- package/dist/core/runner/prepare-listings.d.ts +77 -0
- package/dist/core/runner/prepare-listings.js +76 -0
- package/dist/core/runner/prepare-lsp.d.ts +55 -0
- package/dist/core/runner/prepare-lsp.js +27 -0
- package/dist/core/runner/prepare-memory-engine-session.d.ts +84 -0
- package/dist/core/runner/prepare-memory-engine-session.js +233 -0
- package/dist/core/runner/prepare-memory.d.ts +1 -1
- package/dist/core/runner/prepare-offload-wrappers.d.ts +62 -0
- package/dist/core/runner/prepare-offload-wrappers.js +45 -0
- package/dist/core/runner/prepare-park-ask.d.ts +164 -0
- package/dist/core/runner/prepare-park-ask.js +377 -0
- package/dist/core/runner/prepare-policy-chain.d.ts +208 -0
- package/dist/core/runner/prepare-policy-chain.js +584 -0
- package/dist/core/runner/prepare-project-context.d.ts +119 -0
- package/dist/core/runner/prepare-project-context.js +148 -0
- package/dist/core/runner/prepare-prompt-assembly.d.ts +95 -0
- package/dist/core/runner/prepare-prompt-assembly.js +162 -0
- package/dist/core/runner/prepare-prompt-inputs.d.ts +119 -0
- package/dist/core/runner/prepare-prompt-inputs.js +141 -0
- package/dist/core/runner/prepare-protocol-tools.d.ts +91 -0
- package/dist/core/runner/prepare-protocol-tools.js +179 -0
- package/dist/core/runner/prepare-question-face.d.ts +101 -0
- package/dist/core/runner/prepare-question-face.js +84 -0
- package/dist/core/runner/prepare-run-refs.d.ts +89 -0
- package/dist/core/runner/prepare-run-refs.js +39 -0
- package/dist/core/runner/prepare-safety-scan.d.ts +3 -7
- package/dist/core/runner/prepare-safety-scan.js +1 -2
- package/dist/core/runner/prepare-suspend-saga.d.ts +170 -0
- package/dist/core/runner/prepare-suspend-saga.js +308 -0
- package/dist/core/runner/prepare-task.d.ts +14 -1976
- package/dist/core/runner/prepare-task.js +182 -5162
- package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +111 -0
- package/dist/core/runner/prepare-tool-disclosure-mount.js +219 -0
- package/dist/core/runner/prepare-turn-wiring.d.ts +154 -0
- package/dist/core/runner/prepare-turn-wiring.js +201 -0
- package/dist/core/runner/prepare-wiring-manifest.d.ts +192 -0
- package/dist/core/runner/prepare-wiring-manifest.js +247 -0
- package/dist/core/runner/prepare-workspace-restore.d.ts +3 -56
- package/dist/core/runner/prepare-workspace-restore.js +4 -38
- package/dist/core/runner/prompt-hash-salt.d.ts +1 -0
- package/dist/core/runner/prompt-hash-salt.js +2 -0
- package/dist/core/runner/remote-env-retry.d.ts +29 -0
- package/dist/core/runner/remote-env-retry.js +16 -0
- package/dist/core/runner/rollback-stack.d.ts +32 -0
- package/dist/core/runner/rollback-stack.js +30 -0
- package/dist/core/runner/runtask.js +1 -1
- package/dist/core/runner/workspace-path.d.ts +33 -0
- package/dist/core/runner/workspace-path.js +22 -0
- package/dist/core/session.d.ts +12 -0
- package/dist/core/session.js +3 -0
- package/dist/core/tool-policy.d.ts +16 -0
- package/dist/core/tool-policy.js +3 -0
- package/dist/core/types.d.ts +2 -2
- package/dist/core/write-protect.js +3 -2
- package/package.json +6 -2
- /package/dist/core/runner/{prepare-announce-once.d.ts → announce-once-ledger.d.ts} +0 -0
- /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 {};
|