@sema-agent/core 7.18.0 → 7.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +276 -2
- package/dist/agents/subagent.js +12 -20
- package/dist/agents/teacher.js +2 -5
- package/dist/agents/verify.js +21 -10
- package/dist/brain/anthropic.js +8 -3
- package/dist/brain/open-responses.js +12 -4
- package/dist/brain/openai.js +7 -2
- package/dist/brain/reasoning.d.ts +13 -13
- package/dist/brain/reasoning.js +2 -2
- package/dist/brain/stream-engine.d.ts +4 -0
- package/dist/brain/stream-engine.js +6 -1
- package/dist/brain/stream-shared.d.ts +3 -0
- package/dist/brain/stream-shared.js +1 -0
- package/dist/core/ask-origin.d.ts +16 -0
- package/dist/core/ask-origin.js +8 -0
- package/dist/core/auto-mode-arming.d.ts +6 -0
- package/dist/core/auto-mode-arming.js +5 -5
- package/dist/core/auto-mode-defaults.d.ts +43 -15
- package/dist/core/auto-mode-defaults.js +4 -1
- package/dist/core/auto-mode.d.ts +74 -26
- package/dist/core/auto-mode.js +80 -11
- package/dist/core/checkpoint-store.d.ts +7 -0
- package/dist/core/checkpoint-store.js +2 -2
- package/dist/core/child-clamp-seats.d.ts +79 -0
- package/dist/core/child-clamp-seats.js +29 -0
- package/dist/core/engine-notice.d.ts +89 -41
- package/dist/core/fs-write-gate-policy.d.ts +17 -9
- package/dist/core/fs-write-gate-policy.js +51 -4
- package/dist/core/gate-bounded-await.d.ts +38 -0
- package/dist/core/gate-bounded-await.js +53 -0
- package/dist/core/gate-fold.js +20 -48
- package/dist/core/gate-lanes.js +17 -9
- package/dist/core/gate-outcome.d.ts +34 -6
- package/dist/core/gate-outcome.js +7 -0
- package/dist/core/governance-codes.d.ts +16 -1
- package/dist/core/governance-codes.js +18 -0
- package/dist/core/hooks.d.ts +8 -8
- package/dist/core/memory-engine/engine.d.ts +14 -9
- package/dist/core/memory-engine/engine.js +21 -8
- package/dist/core/memory-engine/layout.d.ts +31 -0
- package/dist/core/memory-engine/layout.js +68 -3
- package/dist/core/memory-engine/types.d.ts +24 -0
- package/dist/core/memory.d.ts +19 -0
- package/dist/core/memory.js +12 -0
- package/dist/core/read-root-candidate.d.ts +12 -0
- package/dist/core/read-root-candidate.js +24 -0
- package/dist/core/remote-env.d.ts +32 -0
- package/dist/core/remote-env.js +4 -1
- package/dist/core/roles.d.ts +9 -1
- package/dist/core/roles.js +4 -0
- package/dist/core/runner/advertised-writable-dirs.d.ts +59 -1
- package/dist/core/runner/advertised-writable-dirs.js +51 -3
- package/dist/core/runner/announce-once-ledger.d.ts +15 -0
- package/dist/core/runner/announce-once-ledger.js +8 -0
- package/dist/core/runner/classifier-ladder.d.ts +154 -11
- package/dist/core/runner/classifier-ladder.js +170 -10
- package/dist/core/runner/contracts.d.ts +46 -1
- package/dist/core/runner/denial-limit-arms.d.ts +14 -1
- package/dist/core/runner/denial-limit-arms.js +7 -6
- package/dist/core/runner/env-capability-census.d.ts +30 -0
- package/dist/core/runner/env-capability-census.js +38 -0
- package/dist/core/runner/forward-sink.d.ts +32 -0
- package/dist/core/runner/forward-sink.js +41 -0
- package/dist/core/runner/gate-exit.d.ts +4 -0
- package/dist/core/runner/gate-exit.js +4 -1
- package/dist/core/runner/git-status-frame.d.ts +0 -9
- package/dist/core/runner/listing-fold.d.ts +57 -0
- package/dist/core/runner/listing-fold.js +75 -0
- package/dist/core/runner/prepare-ask-lane.js +1 -0
- package/dist/core/runner/prepare-caps-and-workflow.js +20 -31
- package/dist/core/runner/prepare-config-doors.d.ts +6 -0
- package/dist/core/runner/prepare-config-doors.js +8 -10
- package/dist/core/runner/prepare-execution-env.d.ts +1 -1
- package/dist/core/runner/prepare-execution-env.js +2 -0
- package/dist/core/runner/prepare-gate-stations.d.ts +8 -0
- package/dist/core/runner/prepare-gate-stations.js +6 -2
- package/dist/core/runner/prepare-hands-readface.d.ts +4 -0
- package/dist/core/runner/prepare-hands-readface.js +3 -5
- package/dist/core/runner/prepare-listings.d.ts +3 -0
- package/dist/core/runner/prepare-listings.js +4 -2
- package/dist/core/runner/prepare-memory.js +7 -1
- package/dist/core/runner/prepare-policy-chain.js +5 -5
- package/dist/core/runner/prepare-project-context.d.ts +5 -1
- package/dist/core/runner/prepare-project-context.js +3 -2
- package/dist/core/runner/prepare-prompt-inputs.d.ts +2 -2
- package/dist/core/runner/prepare-run-refs.d.ts +4 -2
- package/dist/core/runner/prepare-run-refs.js +5 -4
- package/dist/core/runner/prepare-task.js +8 -6
- package/dist/core/runner/resume-apply.js +3 -3
- package/dist/core/runner/run-clock-and-content.js +3 -7
- package/dist/core/runner/run-harness-handlers.js +10 -10
- package/dist/core/runner/run-leg.js +31 -16
- package/dist/core/runner/run-settle-and-teardown.js +2 -5
- package/dist/core/runner/run-turn-boundary.js +44 -25
- package/dist/core/runner/runtask.js +2 -1
- package/dist/core/runner/synthetic-tools.d.ts +41 -29
- package/dist/core/runner/synthetic-tools.js +25 -40
- package/dist/core/runner/task-root-canonical.d.ts +45 -0
- package/dist/core/runner/task-root-canonical.js +18 -0
- package/dist/core/runner/turn-attachments.d.ts +160 -53
- package/dist/core/runner/turn-attachments.js +153 -51
- package/dist/core/safe-notify.d.ts +5 -0
- package/dist/core/safe-notify.js +24 -2
- package/dist/core/sensitive-path-policy.d.ts +84 -17
- package/dist/core/sensitive-path-policy.js +37 -19
- package/dist/core/skill-listing-budget.d.ts +106 -0
- package/dist/core/skill-listing-budget.js +57 -0
- package/dist/core/skills-directory.js +17 -10
- package/dist/core/task-registry.js +2 -1
- package/dist/core/task-spec.d.ts +29 -2
- package/dist/core/tool-policy.d.ts +31 -5
- package/dist/core/tool-policy.js +45 -32
- package/dist/core/tool-spec.d.ts +37 -4
- package/dist/core/types.d.ts +1 -1
- package/dist/core/write-protect.d.ts +105 -20
- package/dist/core/write-protect.js +89 -32
- package/dist/index.d.ts +8 -7
- package/dist/index.js +6 -5
- package/dist/orchestration/workflow-primitives.js +10 -20
- package/dist/stores/file/memory-store.d.ts +5 -2
- package/dist/stores/file/memory-store.js +8 -4
- package/dist/tools/fs/bash-readonly-classifier.d.ts +40 -2
- package/dist/tools/fs/bash-readonly-classifier.js +376 -41
- package/dist/tools/fs/fs-bash.d.ts +1 -1
- package/dist/tools/fs/fs-bash.js +19 -28
- package/dist/tools/fs/fs-shared.d.ts +22 -0
- package/dist/tools/fs/fs-shared.js +30 -0
- package/dist/tools/fs/index.d.ts +1 -1
- package/dist/tools/fs/index.js +1 -1
- package/dist/tools/fs/read-deny.d.ts +4 -3
- package/dist/tools/fs/safety.d.ts +15 -0
- package/dist/tools/fs/safety.js +7 -0
- package/package.json +2 -1
- package/test/export-surface.snapshot.json +31 -5
- package/wire-changes/core@7.18.0.json +1273 -0
- package/wire-changes/core@7.19.0.json +457 -0
- package/wire-changes/core@7.20.0.json +568 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { sanitizeUntrustedText, SHELLED_BODY_ENVELOPE_TAGS } from "../untrusted-text.js";
|
|
2
|
+
export function foldListingWithin(items, spec) {
|
|
3
|
+
if (items.length === 0)
|
|
4
|
+
return undefined;
|
|
5
|
+
const bytes = (s) => Buffer.byteLength(s, "utf8");
|
|
6
|
+
const render = spec.frame;
|
|
7
|
+
const lineCost = (l) => bytes(l) + 1;
|
|
8
|
+
const bodyBudget = spec.blockMaxBytes - bytes(render([]));
|
|
9
|
+
const fullLines = items.map((it) => spec.full(it));
|
|
10
|
+
const fullTotal = fullLines.reduce((acc, l) => acc + lineCost(l), 0);
|
|
11
|
+
if (fullTotal <= bodyBudget)
|
|
12
|
+
return { text: render(fullLines), nameOnly: [], dropped: [] };
|
|
13
|
+
const nameLines = items.map((it) => spec.nameOnly(it));
|
|
14
|
+
const namePrefix = new Array(nameLines.length + 1);
|
|
15
|
+
namePrefix[0] = 0;
|
|
16
|
+
for (let i = 0; i < nameLines.length; i++)
|
|
17
|
+
namePrefix[i + 1] = namePrefix[i] + lineCost(nameLines[i]);
|
|
18
|
+
const markerCost = lineCost(spec.capMarker);
|
|
19
|
+
const floorCost = markerCost + namePrefix[nameLines.length];
|
|
20
|
+
if (floorCost <= bodyBudget) {
|
|
21
|
+
let slack = bodyBudget - floorCost;
|
|
22
|
+
const byPriority = items
|
|
23
|
+
.map((it, i) => ({ i, rank: spec.rank?.(it, i) ?? i }))
|
|
24
|
+
.toSorted((a, b) => a.rank - b.rank || a.i - b.i);
|
|
25
|
+
const withFull = new Set();
|
|
26
|
+
for (const { i } of byPriority) {
|
|
27
|
+
const delta = lineCost(fullLines[i]) - lineCost(nameLines[i]);
|
|
28
|
+
if (delta <= slack) {
|
|
29
|
+
withFull.add(i);
|
|
30
|
+
slack -= delta;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
text: render([spec.capMarker, ...items.map((_it, i) => (withFull.has(i) ? fullLines[i] : nameLines[i]))]),
|
|
35
|
+
nameOnly: items.filter((_it, i) => !withFull.has(i)).map((it) => spec.nameOf(it)),
|
|
36
|
+
dropped: [],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
for (let m = items.length - 1; m >= 0; m--) {
|
|
40
|
+
const overflow = spec.overflowLine(items.length - m);
|
|
41
|
+
if (markerCost + namePrefix[m] + lineCost(overflow) <= bodyBudget) {
|
|
42
|
+
return {
|
|
43
|
+
text: render([spec.capMarker, ...nameLines.slice(0, m), overflow]),
|
|
44
|
+
nameOnly: items.slice(0, m).map((it) => spec.nameOf(it)),
|
|
45
|
+
dropped: items.slice(m).map((it) => spec.nameOf(it)),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
text: render([spec.capMarker, spec.overflowLine(items.length)]),
|
|
51
|
+
nameOnly: [],
|
|
52
|
+
dropped: items.map((it) => spec.nameOf(it)),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export function removalArmFloor(header, count) {
|
|
56
|
+
return [header, `(… +${count} more are no longer available)`].join("\n");
|
|
57
|
+
}
|
|
58
|
+
export function foldRemovalArmWithin(removed, header, maxBytes) {
|
|
59
|
+
const bytes = (s) => Buffer.byteLength(s, "utf8");
|
|
60
|
+
const lines = removed.map((n) => `- ${sanitizeUntrustedText(n, SHELLED_BODY_ENVELOPE_TAGS)}`);
|
|
61
|
+
const head = bytes(header);
|
|
62
|
+
const lineCost = (l) => bytes(l) + 1;
|
|
63
|
+
const prefix = new Array(lines.length + 1);
|
|
64
|
+
prefix[0] = 0;
|
|
65
|
+
for (let i = 0; i < lines.length; i++)
|
|
66
|
+
prefix[i + 1] = prefix[i] + lineCost(lines[i]);
|
|
67
|
+
if (head + prefix[lines.length] <= maxBytes)
|
|
68
|
+
return [header, ...lines].join("\n");
|
|
69
|
+
for (let m = lines.length - 1; m >= 0; m--) {
|
|
70
|
+
const tail = `(… +${lines.length - m} more are no longer available)`;
|
|
71
|
+
if (head + prefix[m] + lineCost(tail) <= maxBytes)
|
|
72
|
+
return [header, ...lines.slice(0, m), tail].join("\n");
|
|
73
|
+
}
|
|
74
|
+
return removalArmFloor(header, lines.length);
|
|
75
|
+
}
|
|
@@ -102,6 +102,7 @@ export function prepareAskLane(input) {
|
|
|
102
102
|
...(decision.action === "ask" ? gateAskCarry(decision, isLiveApproverSeat(onAsk), autoModeDenialTracking) : {}),
|
|
103
103
|
...(decision.action === "ask" && decision.probeReason !== undefined ? { probeReason: decision.probeReason } : {}),
|
|
104
104
|
...(decision.action === "ask" && decision.probeCause !== undefined ? { probeCause: decision.probeCause } : {}),
|
|
105
|
+
...(decision.action === "ask" && decision.probeReadRootCandidate !== undefined ? { readRootCandidate: decision.probeReadRootCandidate } : {}),
|
|
105
106
|
...(decision.action === "ask" && decision.ruleEvidence !== undefined ? { ruleEvidence: decision.ruleEvidence } : {}),
|
|
106
107
|
}, onAsk, composedCallSignal(callSignal), lateAskSettlementObserver({ toolName: req.toolName, toolCallId: req.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
|
|
107
108
|
const waitMs = Math.max(0, now() - t0);
|
|
@@ -11,26 +11,14 @@ import { autoModeClassifierFallbackOf, autoModeRoundNotAsked, createAutoModeDeci
|
|
|
11
11
|
import { checkpointStoreOfSeat } from "../checkpoint-seat.js";
|
|
12
12
|
import { COMPLIANCE_CAPABILITIES, WEB_FETCH_TOOL_NAME, resolveComplianceDenies } from "../compliance.js";
|
|
13
13
|
import { createPresentPlanTool, createEnterPlanModeTool } from "../present-plan-tool.js";
|
|
14
|
-
import { resolveTaskModel } from "../roles.js";
|
|
15
14
|
import { brainToRuntime } from "../runtime.js";
|
|
16
15
|
import { defaultTaskRegistry } from "../task-registry.js";
|
|
17
16
|
import { defineTool, isDefineToolProduct, rebindDefineToolCtx } from "../tools.js";
|
|
18
17
|
const MOUNT_REMEDY = "author it as a plain-object ToolSpec (own `name` and `execute`), hand the defineTool product itself, or wrap a product through the supported wrapper form (a fresh object re-stamped with `stampDefineToolBrand` whose rebind re-wraps the rebound product)";
|
|
19
18
|
import { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource } from "../runtime-caps.js";
|
|
20
19
|
import { derivedRouteFallsBack } from "./derived-route-fallback.js";
|
|
21
|
-
import {
|
|
20
|
+
import { CLASSIFIER_ATTEMPT_SUFFIX, CLASSIFIER_THINK_SEAT, announceClassifierRoleDefaulted, classifierLadderRungs, classifierReplyAsksAgain, classifierReplyFailure, classifierReplyVerdictText, classifierRoundObserver, classifierRungSeat, classifierSeatModel } from "./classifier-ladder.js";
|
|
22
21
|
import { REPORT_FINDINGS_TOOL_NAME, createReportBlockedTool, createReportFindingsTool } from "./synthetic-tools.js";
|
|
23
|
-
function classifierReplyText(reply) {
|
|
24
|
-
return reply.content
|
|
25
|
-
.filter((c) => c.type === "text")
|
|
26
|
-
.map((c) => c.text)
|
|
27
|
-
.join("");
|
|
28
|
-
}
|
|
29
|
-
function classifierReplyEmptyAtCap(reply) {
|
|
30
|
-
if (classifierReplyText(reply) !== "")
|
|
31
|
-
return false;
|
|
32
|
-
return reply.stopReason === "length" || (reply.stopReason === "error" && reply.errorKind === "length_empty");
|
|
33
|
-
}
|
|
34
22
|
import { RosterBuilder, callerMountSource } from "../tool-roster.js";
|
|
35
23
|
function assembleParentCaptureState(o, i, ctl, ancestors) {
|
|
36
24
|
const build = (optedOut, indeterminate) => ({
|
|
@@ -209,16 +197,12 @@ export async function prepareCapsAndWorkflow(input) {
|
|
|
209
197
|
const autoModeArmReason = autoModeArmReasonOf(autoModeIntent, deps.autoMode !== undefined, runtimeCaps, runtimeCapsFaulted);
|
|
210
198
|
if (autoModeIntent && deps.autoMode !== undefined && runtimeCaps?.autoMode !== false) {
|
|
211
199
|
const am = deps.autoMode;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
classifierModel = resolveTaskModel({ modelRole: "classifier" }, deps).model;
|
|
215
|
-
}
|
|
216
|
-
catch {
|
|
217
|
-
classifierModel = model;
|
|
218
|
-
}
|
|
200
|
+
const classifierSeat = classifierSeatModel({ deps, main: model });
|
|
201
|
+
let classifierModel = classifierSeat.model;
|
|
219
202
|
if (await derivedRouteFallsBack({ seat: "auto-mode-classifier", derived: classifierModel, primary: model, brain: deps.brain, getApiKeyAndHeaders: spec.getApiKeyAndHeaders, onNotice: deps.onNotice, sessionId, runId, onBrokenPairing: autoModeClassifierFallbackOf(am.classifierFallback) === "none" ? "refuse" : "fall_back" })) {
|
|
220
203
|
classifierModel = model;
|
|
221
204
|
}
|
|
205
|
+
announceClassifierRoleDefaulted({ onNotice: deps.onNotice, seat: { ...classifierSeat, model: classifierModel }, ...(sessionId !== undefined ? { sessionId } : {}), ...(runId !== undefined ? { runId } : {}) });
|
|
222
206
|
const classifierLaneRule = peerLaneActive && peerSendMessageBuiltIn;
|
|
223
207
|
const classifierSystemPrompt = buildAutoModePrompt(classifierLaneRule ? { ...am, crossSessionMessagesRule: CROSS_SESSION_CLASSIFIER_RULE } : am);
|
|
224
208
|
const classifierRuntime = brainToRuntime(deps.brain);
|
|
@@ -245,17 +229,22 @@ export async function prepareCapsAndWorkflow(input) {
|
|
|
245
229
|
}
|
|
246
230
|
const userPrompt = await build.catch((e) => { throw autoModeRoundNotAsked(e); });
|
|
247
231
|
const classifierAuth = await spec.getApiKeyAndHeaders?.(rungModel);
|
|
248
|
-
const request = (seat
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
232
|
+
const request = async (seat, suffix) => {
|
|
233
|
+
const response = await classifierRuntime.completeSimple(rungModel, { systemPrompt: classifierSystemPrompt, messages: [{ role: "user", content: userPrompt + suffix, timestamp: Date.now() }] }, {
|
|
234
|
+
signal,
|
|
235
|
+
...seat,
|
|
236
|
+
...(classifierAuth?.apiKey !== undefined ? { apiKey: classifierAuth.apiKey } : {}),
|
|
237
|
+
...(classifierAuth?.headers !== undefined ? { headers: classifierAuth.headers } : {}),
|
|
238
|
+
});
|
|
239
|
+
const failure = classifierReplyFailure(response);
|
|
240
|
+
if (failure !== undefined)
|
|
241
|
+
throw failure;
|
|
242
|
+
return response;
|
|
243
|
+
};
|
|
244
|
+
let response = await request(classifierRungSeat(rungModel), CLASSIFIER_ATTEMPT_SUFFIX.first);
|
|
245
|
+
if (classifierReplyAsksAgain(response) && signal?.aborted !== true)
|
|
246
|
+
response = await request(CLASSIFIER_THINK_SEAT, CLASSIFIER_ATTEMPT_SUFFIX.reask);
|
|
247
|
+
return classifierReplyVerdictText(response);
|
|
259
248
|
},
|
|
260
249
|
});
|
|
261
250
|
if (am.persistArming === true)
|
|
@@ -25,6 +25,7 @@ import type { ThinkingLevel } from "../../internal/harness.js";
|
|
|
25
25
|
import { type MicroCompactMachine } from "../context-edit.js";
|
|
26
26
|
import { type LockedPreflight } from "../locked-config.js";
|
|
27
27
|
import { type ResolvedRole } from "../roles.js";
|
|
28
|
+
import { type ResolvedSkillListingBudget } from "../skill-listing-budget.js";
|
|
28
29
|
import type { SessionStore } from "../session.js";
|
|
29
30
|
import type { RunnerDeps, TaskLimits, TaskSpec } from "../types.js";
|
|
30
31
|
import { type UsageWindow } from "../usage-window-store.js";
|
|
@@ -184,6 +185,11 @@ export interface PrepareConfigDoorsResult {
|
|
|
184
185
|
* mutated (or getter-backed) after the door must not present an unscreened value to the mark
|
|
185
186
|
* branch (the read-once posture). */
|
|
186
187
|
memoryDelegationEvidence: "static-face" | "attested-only";
|
|
188
|
+
/** owned — the lowered skills-listing budget seats (`TaskSpec.skillListingBudgetFraction` /
|
|
189
|
+
* `skillListingMaxDescChars`): the renderer's two numbers, resolved once against the resolved main
|
|
190
|
+
* model's window. Read at the door so a bad value refuses BEFORE session acquisition, and carried so
|
|
191
|
+
* the listing phase and the boundary renderer measure against the same resolution. */
|
|
192
|
+
skillListingBudget: ResolvedSkillListingBudget;
|
|
187
193
|
/** owned — the NORMALIZED memory-provenance mode: the deps seat read ONCE in
|
|
188
194
|
* the synchronous pre-await stretch, screened, absent folded to the `"carry"` default.
|
|
189
195
|
* The memory phase constructs every engine with THIS value — never a live deps re-read. */
|
|
@@ -7,6 +7,8 @@ import { isModelPriced, malformedPricingField, modelCostToPricing } from "../pri
|
|
|
7
7
|
import { preflightLockedConfig } from "../locked-config.js";
|
|
8
8
|
import { assertRetentionCapability } from "../retention.js";
|
|
9
9
|
import { resolveModel, resolveTaskModel, roleModelIfSet } from "../roles.js";
|
|
10
|
+
import { resolveSkillListingBudget } from "../skill-listing-budget.js";
|
|
11
|
+
import { describeRejectedValue } from "../tool-search-setting.js";
|
|
10
12
|
import { applyToolModelGate, assertRestoreGatedToolsValue, modelIdTail } from "../tool-model-gate.js";
|
|
11
13
|
import { resolveUsageWindows } from "../usage-window-store.js";
|
|
12
14
|
import { deriveAskEffective, resolveAskSeamForm, resolveQuestionSeam } from "../wiring-manifest.js";
|
|
@@ -35,16 +37,6 @@ export function limitConfigError(code, message) {
|
|
|
35
37
|
e.code = code;
|
|
36
38
|
return e;
|
|
37
39
|
}
|
|
38
|
-
function describeRejectedValue(value) {
|
|
39
|
-
try {
|
|
40
|
-
const quoted = JSON.stringify(value);
|
|
41
|
-
if (quoted !== undefined)
|
|
42
|
-
return quoted;
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
}
|
|
46
|
-
return `a ${typeof value} value`;
|
|
47
|
-
}
|
|
48
40
|
export function resolveTaskLimits(limits) {
|
|
49
41
|
if (limits === undefined)
|
|
50
42
|
return undefined;
|
|
@@ -416,6 +408,11 @@ export function prepareConfigDoors(input) {
|
|
|
416
408
|
if (gateDecision.survivors !== undefined) {
|
|
417
409
|
spec = { ...spec, tools: gateDecision.survivors };
|
|
418
410
|
}
|
|
411
|
+
const skillListingBudget = resolveSkillListingBudget({
|
|
412
|
+
fraction: spec.skillListingBudgetFraction,
|
|
413
|
+
maxDescChars: spec.skillListingMaxDescChars,
|
|
414
|
+
model,
|
|
415
|
+
});
|
|
419
416
|
const modelGate = Object.freeze({
|
|
420
417
|
removedByClass: gateDecision.removedByClass,
|
|
421
418
|
unknownClasses: gateDecision.unknownClasses,
|
|
@@ -439,6 +436,7 @@ export function prepareConfigDoors(input) {
|
|
|
439
436
|
fableMitigations,
|
|
440
437
|
modelGate,
|
|
441
438
|
memoryDelegationEvidence,
|
|
439
|
+
skillListingBudget,
|
|
442
440
|
memoryProvenance,
|
|
443
441
|
memoryCapturePolicy,
|
|
444
442
|
usageWindows,
|
|
@@ -14,7 +14,7 @@ export interface OwnedEnvSeat {
|
|
|
14
14
|
export interface PrepareExecutionEnvInput {
|
|
15
15
|
/** borrowed-readonly — the deployment's env seats: the per-task factory (wins), the static env, the
|
|
16
16
|
* declared root fence, and the incident sink for the bounded teardown legs. */
|
|
17
|
-
deps: Pick<RunnerDeps, "executionEnvFactory" | "executionEnv" | "rootPath" | "onError">;
|
|
17
|
+
deps: Pick<RunnerDeps, "executionEnvFactory" | "executionEnv" | "rootPath" | "onError" | "onNotice">;
|
|
18
18
|
/** borrowed-readonly — the REBOUND spec; only the caller's task id rides into the factory ctx. */
|
|
19
19
|
spec: Pick<TaskSpec, "taskId">;
|
|
20
20
|
/** borrowed-readonly — the trusted spawn-side requests: the explicit cwd (a contract), the isolation
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { StubExecutionEnv } from "../stub-env.js";
|
|
2
2
|
import { hasDestroy } from "../remote-env.js";
|
|
3
|
+
import { announceMalformedCapabilities } from "./env-capability-census.js";
|
|
3
4
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
4
5
|
export async function prepareExecutionEnv(input) {
|
|
5
6
|
const { deps, spec, internals, resume, placementRootResolved, sessionId, forgetOnThrow, rollback } = input;
|
|
@@ -39,6 +40,7 @@ export async function prepareExecutionEnv(input) {
|
|
|
39
40
|
});
|
|
40
41
|
}
|
|
41
42
|
const executionEnv = ownedEnv ?? deps.executionEnv ?? new StubExecutionEnv();
|
|
43
|
+
announceMalformedCapabilities(executionEnv, deps.onNotice);
|
|
42
44
|
if (internals?.requestedCwd !== undefined) {
|
|
43
45
|
const canon = async (p) => {
|
|
44
46
|
const r = await executionEnv.canonicalPath(p);
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* gate outcome; nothing here changes a word, a source or an order — the stations are moved, not re-adjudicated.
|
|
24
24
|
*/
|
|
25
25
|
import type { AgentHarness, AgentTool } from "../../internal/harness.js";
|
|
26
|
+
import type { ExecutionEnv } from "../../internal/harness-types.js";
|
|
26
27
|
import type { AskClass } from "../ask-class.js";
|
|
27
28
|
import type { AutoModeDecider, AutoModeDenialTracker } from "../auto-mode.js";
|
|
28
29
|
import { type ComplianceCapability } from "../compliance.js";
|
|
@@ -147,6 +148,13 @@ export interface PrepareGateStationsInput {
|
|
|
147
148
|
handsCwdRef: CwdRef | undefined;
|
|
148
149
|
/** borrowed-readonly — the read-only env capability face every hook context carries, or undefined. */
|
|
149
150
|
hookEnvFace: HookEnvCapabilities | undefined;
|
|
151
|
+
/** borrowed-readonly — the HANDS' execution environment (the one the write tools resolve against), or
|
|
152
|
+
* undefined when the run has no hands: the write-protection judge's target view resolves in it, and with
|
|
153
|
+
* none the judge answers by the spelling alone. */
|
|
154
|
+
handsEnv: ExecutionEnv | undefined;
|
|
155
|
+
/** borrowed-readonly — the resolved task root, the base a `base: "root"` write tool's relative spelling
|
|
156
|
+
* resolves against (the same seat the path policies read as `rootPath`). */
|
|
157
|
+
taskRootFinal: string;
|
|
150
158
|
/** borrowed-readonly — the per-tool irreversibility tier handed to the gate per call. */
|
|
151
159
|
irreversibilityTier: IrreversibilityTier;
|
|
152
160
|
/** borrowed-readonly — the per-tool reversibility probes handed to the gate per call. */
|
|
@@ -42,7 +42,7 @@ export function maybeHumanRejectionHalt(input) {
|
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
export function prepareGateStations(input) {
|
|
45
|
-
const { askLane, parkAsk, tools, toolRosterDeltas, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, planModeRef, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, hookEnvFace, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider, autoModeDenialTracking, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash } = input;
|
|
45
|
+
const { askLane, parkAsk, tools, toolRosterDeltas, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, planModeRef, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, hookEnvFace, handsEnv, taskRootFinal, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider, autoModeDenialTracking, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash } = input;
|
|
46
46
|
if (askLane !== undefined && parkAsk !== undefined) {
|
|
47
47
|
const { adjudicate, resolveAskBound, notifyPermissionDenied, notifyHookError } = askLane;
|
|
48
48
|
const { resolveContentAsk, suspendAsk } = parkAsk;
|
|
@@ -51,7 +51,11 @@ export function prepareGateStations(input) {
|
|
|
51
51
|
if (eff !== undefined && !toolEffects.has(t.name))
|
|
52
52
|
toolEffects.set(t.name, eff);
|
|
53
53
|
}
|
|
54
|
-
const writeProtectionCheck = createWriteProtectionCheck(deps.writeProtectedPaths,
|
|
54
|
+
const writeProtectionCheck = createWriteProtectionCheck(deps.writeProtectedPaths, {
|
|
55
|
+
...(deps.memoryEngineDir !== undefined ? { dataRoot: deps.memoryEngineDir } : {}),
|
|
56
|
+
...(handsEnv !== undefined ? { env: handsEnv } : {}),
|
|
57
|
+
rootPath: taskRootFinal,
|
|
58
|
+
});
|
|
55
59
|
const writeProtectionArmed = writeProtectionCheck !== undefined && tools.some((t) => PATH_CONFINABLE_WRITE_TOOLS.has(t.name));
|
|
56
60
|
toolCallGateArmedRef.armed =
|
|
57
61
|
effectivePolicy !== undefined ||
|
|
@@ -110,6 +110,10 @@ export interface PrepareHandsReadFaceInput {
|
|
|
110
110
|
/** borrowed-readonly — B-4's settled root (T6 FINAL semantics); the containment root every face
|
|
111
111
|
* in this phase canonicalizes from. */
|
|
112
112
|
taskRootFinal: string;
|
|
113
|
+
/** borrowed-readonly — the same root, resolved to the spelling the fence compares with, by the ONE
|
|
114
|
+
* resolver at the driver's root receiving point ({@link import("./task-root-canonical.js").resolveTaskRootCanonical}):
|
|
115
|
+
* taken as an input, never re-canonicalized here, so the fence and the root notice cannot disagree. */
|
|
116
|
+
taskRootCanonical: string;
|
|
113
117
|
/** borrowed-readonly — B-4's checkpoint-path rebaser (T7): identity unless a divergent restore
|
|
114
118
|
* armed it; applied to the readFileState seed keys and the handsCwd seed. */
|
|
115
119
|
rebaseRestoredPath: (p: string) => string;
|
|
@@ -152,7 +152,7 @@ export function resolveHandsLessReadFace(input) {
|
|
|
152
152
|
}
|
|
153
153
|
export async function prepareHandsMount(input) {
|
|
154
154
|
const envHandToolNames = new Set();
|
|
155
|
-
const { handsEnabled, executionEnv,
|
|
155
|
+
const { handsEnabled, executionEnv, taskRootCanonical, rebaseRestoredPath, resume, session, sessionId, hostTaskId, taskScope, fullShellReachable, effectiveShellGate, toolFaceSnapshot, model, spec, deps, internals, memoryWriteGate, advertisedWritableDirs, firstPartyOffload, roster, egressTools, irreversibilityTier, irreversibleTools, reversibilityProbes } = input;
|
|
156
156
|
assertStringArraySeat(spec.additionalDirectories, "TaskSpec.additionalDirectories", "strings");
|
|
157
157
|
assertStringArraySeat(spec.additionalReadDirectories, "TaskSpec.additionalReadDirectories", "strings");
|
|
158
158
|
assertStringArraySeat(deps.readDenyPatterns, "RunnerDeps.readDenyPatterns", "compiler-owned");
|
|
@@ -176,15 +176,13 @@ export async function prepareHandsMount(input) {
|
|
|
176
176
|
let attachmentRootCanonical;
|
|
177
177
|
let readDenyMatcher;
|
|
178
178
|
if (handsEnabled) {
|
|
179
|
-
const
|
|
180
|
-
const canon = await executionEnv.canonicalPath(rootRaw);
|
|
181
|
-
const rootCanonical = canon.ok ? canon.value : rootRaw;
|
|
179
|
+
const rootCanonical = taskRootCanonical;
|
|
182
180
|
attachmentRootCanonical = rootCanonical;
|
|
183
181
|
const canonicalizeExtraDirs = async (dirs, field, sink) => {
|
|
184
182
|
for (const dir of dirs ?? []) {
|
|
185
183
|
if (!dir || !dir.trim())
|
|
186
184
|
continue;
|
|
187
|
-
const c = await executionEnv.canonicalPath(dir);
|
|
185
|
+
const c = await (advertisedWritableDirs?.canonicalizeDeclaredRoot(dir) ?? executionEnv.canonicalPath(dir));
|
|
188
186
|
if (c.ok) {
|
|
189
187
|
sink.push(c.value);
|
|
190
188
|
}
|
|
@@ -17,6 +17,7 @@ import type { AgentTool } from "../../internal/harness.js";
|
|
|
17
17
|
import type { TaskSpec } from "../types.js";
|
|
18
18
|
import type { AnnounceOnceSink, Prepared, PrepareResume, ToolFaceSnapshot } from "./contracts.js";
|
|
19
19
|
import type { AnnouncedListingSnapshot } from "../../internal/harness.js";
|
|
20
|
+
import type { ResolvedSkillListingBudget } from "../skill-listing-budget.js";
|
|
20
21
|
export interface PrepareListingsInput {
|
|
21
22
|
/** borrowed-readonly — the REBOUND spec (`spec′`). Read: `agents` (the audit's subject), `tools`
|
|
22
23
|
* (the roster-bearing delegation tool, by its `agentListing` marker), `sessionId` (continuation
|
|
@@ -44,6 +45,8 @@ export interface PrepareListingsInput {
|
|
|
44
45
|
/** borrowed-readonly — the project-context phase's skills-listing projection; read only for the
|
|
45
46
|
* skills opt-out advisory (entries.length). */
|
|
46
47
|
skillsListing: Prepared["skillsListing"];
|
|
48
|
+
/** borrowed-readonly — the listing family's resolved budget; the agent roster frames fold into it. */
|
|
49
|
+
listingBudget: ResolvedSkillListingBudget;
|
|
47
50
|
/** borrowed-mutable — the deferred roster's ToolSearch RIDE seat (declared by the disclosure
|
|
48
51
|
* mount, read at activation time through `listingRideRef.current?.(newly)`). Written here, once,
|
|
49
52
|
* on a deferred roster face; no later writer. Identity is the contract (the mount's closures read
|
|
@@ -2,7 +2,7 @@ import { formatLocalDate } from "../../prompts/default.js";
|
|
|
2
2
|
import { SKILL_TOOL_NAME } from "./synthetic-tools.js";
|
|
3
3
|
import { renderAgentListingDelta } from "./turn-attachments.js";
|
|
4
4
|
export function prepareListings(input) {
|
|
5
|
-
const { spec, tools, onceLedger, sessionId, toolFaceSnapshot, activeTools, resume, skillsListing, listingRideRef, renderWithDate, envFacts, tzValid, userTz } = input;
|
|
5
|
+
const { spec, tools, onceLedger, sessionId, toolFaceSnapshot, activeTools, resume, skillsListing, listingBudget, listingRideRef, renderWithDate, envFacts, tzValid, userTz } = input;
|
|
6
6
|
if (spec.agents !== undefined && spec.agents.length > 0) {
|
|
7
7
|
const known = new Set(tools.flatMap((t) => [t.name, ...(t.aliases ?? [])]));
|
|
8
8
|
for (const def of spec.agents) {
|
|
@@ -39,6 +39,7 @@ export function prepareListings(input) {
|
|
|
39
39
|
? {
|
|
40
40
|
entries: agentListingSpec.agentListing,
|
|
41
41
|
toolName: agentListingSpec.name,
|
|
42
|
+
budget: listingBudget,
|
|
42
43
|
seedAnnounced: resume !== undefined || spec.sessionId !== undefined,
|
|
43
44
|
...(agentListingSpec.agentModels !== undefined ? { models: agentListingSpec.agentModels } : {}),
|
|
44
45
|
}
|
|
@@ -48,6 +49,7 @@ export function prepareListings(input) {
|
|
|
48
49
|
{
|
|
49
50
|
entries: [],
|
|
50
51
|
toolName: (spec.tools ?? []).find((t) => t.agentListing !== undefined)?.name ?? "Agent",
|
|
52
|
+
budget: listingBudget,
|
|
51
53
|
seedAnnounced: true,
|
|
52
54
|
}
|
|
53
55
|
: undefined;
|
|
@@ -66,7 +68,7 @@ export function prepareListings(input) {
|
|
|
66
68
|
return undefined;
|
|
67
69
|
if ((announcedListingsRef.agents?.length ?? 0) > 0)
|
|
68
70
|
return undefined;
|
|
69
|
-
return renderAgentListingDelta({ announcedAgentTypes: undefined }, rideEntries, rosterBearingSpec.name, rideModels);
|
|
71
|
+
return renderAgentListingDelta({ announcedAgentTypes: undefined }, rideEntries, rosterBearingSpec.name, rideModels, listingBudget);
|
|
70
72
|
};
|
|
71
73
|
}
|
|
72
74
|
const dateChange = renderWithDate
|
|
@@ -4,7 +4,7 @@ import { admitMemoryScopes } from "../memory-admission.js";
|
|
|
4
4
|
import { adoptLegacyRepoDirs, canonicalize, deriveRepoControlPlaneDir, deriveProjectControlDir, deriveProjectMemoryDir, deriveRepoMemoryDir, drainMemoryAnnouncements, enqueueMemoryAnnouncement, isContainedIn, lookupProjectIdHint, recordProjectIdHint, resolveMemoryEngineRoot } from "../memory-engine/layout.js";
|
|
5
5
|
import { classifyScopePlanes, derivePersonalControlDir, derivePersonalMemoryDir, mergeHarvestReports, mergeInjections, needsDualRoots, parsedProjectPlane } from "../memory-engine/dual-root.js";
|
|
6
6
|
import { normalizeMemorySpec } from "../memory.js";
|
|
7
|
-
import { MEMORY_ANNOUNCEMENT_CAPTURE_OPTOUT_CODA, MEMORY_ANNOUNCEMENT_READONLY_CODA, MEMORY_CAPTURE_INDETERMINATE_NOTICE, MEMORY_CAPTURE_OPTOUT_NOTICE, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, MemoryEngine, entryFileHeadCarriesOrigin, memoryCaptureOptedOutNotice, memoryCaptureOptOutUnpersistedNotice, memoryConsolidationRecommendedNotice, memoryHarvestQuarantinedNotice, memoryHoldNotices, memoryRecallDisciplineSegment, memorySessionPollutedNotice, pollutionContainmentCounts, } from "../memory-engine/engine.js";
|
|
7
|
+
import { MEMORY_ANNOUNCEMENT_CAPTURE_OPTOUT_CODA, MEMORY_ANNOUNCEMENT_READONLY_CODA, MEMORY_CAPTURE_INDETERMINATE_NOTICE, MEMORY_CAPTURE_OPTOUT_NOTICE, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, MemoryEngine, entryFileHeadCarriesOrigin, memoryCaptureOptedOutNotice, memoryCaptureOptOutUnpersistedNotice, memoryConsolidationRecommendedNotice, memoryHarvestQuarantinedNotice, memoryHoldNotices, memoryRecallDisciplineSegment, memoryRestoreWithheldNotice, memorySessionPollutedNotice, pollutionContainmentCounts, } from "../memory-engine/engine.js";
|
|
8
8
|
import { adjudicateCaptureOptOut, captureOptOutDeniedError, captureOptOutUnpersistedError } from "./memory-capture-optout.js";
|
|
9
9
|
import { readSessionCaptureOptOut } from "../memory-engine/layout.js";
|
|
10
10
|
import { createMemoryEngineTools } from "../memory-engine/tools.js";
|
|
@@ -522,6 +522,9 @@ export async function prepareMemory(input) {
|
|
|
522
522
|
}
|
|
523
523
|
deliverEngineNotice(deps.onNotice, memoryHarvestQuarantinedNotice({ count, moved, escalated, ...(report.reportId !== undefined ? { reportId: report.reportId } : {}), ...(reason !== undefined ? { reason } : {}), sessionId, provenance: input.memoryProvenance, ...(indexOnly ? { indexRolledBack: true } : {}) }));
|
|
524
524
|
}
|
|
525
|
+
if (report.restoreWithheld !== undefined && report.restoreWithheld.length > 0) {
|
|
526
|
+
deliverEngineNotice(deps.onNotice, memoryRestoreWithheldNotice({ paths: report.restoreWithheld, sessionId }));
|
|
527
|
+
}
|
|
525
528
|
for (const notice of memoryHoldNotices(report, sessionId))
|
|
526
529
|
deliverEngineNotice(deps.onNotice, notice);
|
|
527
530
|
const consolidationNotice = memoryConsolidationRecommendedNotice(report, sessionId);
|
|
@@ -615,6 +618,9 @@ export async function prepareMemory(input) {
|
|
|
615
618
|
}
|
|
616
619
|
}
|
|
617
620
|
const sweep = await writeEngine.sweepSessionCaptureResidue(writeHandle);
|
|
621
|
+
if (sweep.withheld.length > 0) {
|
|
622
|
+
deliverEngineNotice(deps.onNotice, memoryRestoreWithheldNotice({ paths: sweep.withheld, sessionId }));
|
|
623
|
+
}
|
|
618
624
|
if (sweep.failures.length > 0) {
|
|
619
625
|
const named = sweep.failures
|
|
620
626
|
.slice(0, 5)
|
|
@@ -303,9 +303,9 @@ export async function preparePolicyChain(input) {
|
|
|
303
303
|
}
|
|
304
304
|
if (re.updatedInput !== undefined)
|
|
305
305
|
editArgs = re.updatedInput;
|
|
306
|
-
const judged = await judgeInheritedClassifier({ autoMode: ancestorDecider !== undefined ? { decider: ancestorDecider, denialTracking: ancestorTracker } : undefined, ask: re, req: { ...creq, args: editArgs }, signal: csignal ?? abortController.signal, subject: "the approved edit" });
|
|
306
|
+
const judged = await judgeInheritedClassifier({ autoMode: ancestorDecider !== undefined ? { decider: ancestorDecider, denialTracking: ancestorTracker } : undefined, ask: re, req: { ...creq, args: editArgs }, signal: csignal ?? abortController.signal, subject: "the approved edit", allowRewrite: editArgs });
|
|
307
307
|
if (judged.kind === "allow")
|
|
308
|
-
return
|
|
308
|
+
return judged.result;
|
|
309
309
|
if (judged.kind === "deny")
|
|
310
310
|
return judged.result;
|
|
311
311
|
const { ask: editAsk, fallback: editFallback, mintedHere: editMintedHere } = judged;
|
|
@@ -371,7 +371,7 @@ export async function preparePolicyChain(input) {
|
|
|
371
371
|
}
|
|
372
372
|
const judged = await judgeInheritedClassifier({ autoMode: pc.autoMode, ask: first, req: creq, signal: csignal ?? abortController.signal, subject: "this call" });
|
|
373
373
|
if (judged.kind === "allow")
|
|
374
|
-
return
|
|
374
|
+
return judged.result;
|
|
375
375
|
if (judged.kind === "deny")
|
|
376
376
|
return judged.result;
|
|
377
377
|
const { ask: inheritedAsk, fallback, mintedHere } = judged;
|
|
@@ -439,9 +439,9 @@ export async function preparePolicyChain(input) {
|
|
|
439
439
|
return mandateUnreconstructibleDeny(creq);
|
|
440
440
|
}
|
|
441
441
|
const presentedArgs = decision.updatedInput !== undefined ? decision.updatedInput : creq.args;
|
|
442
|
-
const judged = await judgeInheritedClassifier({ autoMode: pc.autoMode, ask: decision, req: { ...creq, args: presentedArgs }, signal: csignal ?? abortController.signal, subject: "this call" });
|
|
442
|
+
const judged = await judgeInheritedClassifier({ autoMode: pc.autoMode, ask: decision, req: { ...creq, args: presentedArgs }, signal: csignal ?? abortController.signal, subject: "this call", allowRewrite: decision.updatedInput });
|
|
443
443
|
if (judged.kind === "allow")
|
|
444
|
-
return
|
|
444
|
+
return judged.result;
|
|
445
445
|
if (judged.kind === "deny")
|
|
446
446
|
return judged.result;
|
|
447
447
|
const { ask: inheritedAsk, fallback, mintedHere } = judged;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ResolvedSkillListingBudget } from "../skill-listing-budget.js";
|
|
1
2
|
import { type StoredSession } from "../session.js";
|
|
2
3
|
import type { RunnerDeps, TaskSpec, ToolSpec } from "../types.js";
|
|
3
4
|
import type { ActiveSkillScope } from "./active-skill-scope.js";
|
|
@@ -8,7 +9,7 @@ export interface PrepareProjectContextInput {
|
|
|
8
9
|
/** borrowed-readonly — the deployment seams this phase reads: `loadProjectMemory` (the snapshot),
|
|
9
10
|
* `onError` (the operator lane for a throwing seam / seeding / oversize skill), `sharedMemoryStores`
|
|
10
11
|
* (the shared-memory pair's switch: wired ⇒ mount). Receiver preserved (`deps.onError?.(…)`). */
|
|
11
|
-
deps: Pick<RunnerDeps, "loadProjectMemory" | "onError" | "sharedMemoryStores">;
|
|
12
|
+
deps: Pick<RunnerDeps, "loadProjectMemory" | "onError" | "sharedMemoryStores" | "onNotice">;
|
|
12
13
|
/** borrowed-readonly — the REBOUND spec (`spec′`). Read: `sessionId` (phase derivation + the
|
|
13
14
|
* continuation projections), `skills` (the roster), `principal` (the shared-memory tools' trusted
|
|
14
15
|
* identity). Never mutated. */
|
|
@@ -69,6 +70,9 @@ export interface PrepareProjectContextInput {
|
|
|
69
70
|
/** borrowed-readonly — the durable-resume carrier; read for `!== undefined` (seedAnnounced) and
|
|
70
71
|
* `seed.announcedListings.skills` (the removal-to-zero projection's gate). */
|
|
71
72
|
resume: Pick<PrepareResume, "seed"> | undefined;
|
|
73
|
+
/** borrowed-readonly — the lowered listing-budget seats from the config door. The listing projection
|
|
74
|
+
* carries it to the boundary renderer, and the over-budget disclosure below is priced against it. */
|
|
75
|
+
skillListingBudget: ResolvedSkillListingBudget;
|
|
72
76
|
}
|
|
73
77
|
export interface PrepareProjectContextResult {
|
|
74
78
|
/** owned — the memory block with the project layer appended at the tail (unchanged when the seam is
|
|
@@ -9,7 +9,7 @@ import { SKILL_CONTENT_MAX_CHARS, SKILL_TOOL_NAME, createSkillTool, normalizeSki
|
|
|
9
9
|
import { substitutedSkillLength } from "./skill-base-dir.js";
|
|
10
10
|
import { RosterBuilder } from "../tool-roster.js";
|
|
11
11
|
export async function prepareProjectContext(input) {
|
|
12
|
-
const { deps, spec, internals, session, sessionId, hostTaskId, taskRootFinal, handsEnabled, delegation, seedContextFiles, memoryBlockFromEngine, skillScope, roster, toolFaceSnapshot, onceLedger, memoryTools, memoryEngineSession, memorySearchToolsPlanned, memoryPairExcludedName, memoryPairOccupiedName, resume } = input;
|
|
12
|
+
const { deps, spec, internals, session, sessionId, hostTaskId, taskRootFinal, handsEnabled, delegation, seedContextFiles, memoryBlockFromEngine, skillScope, roster, toolFaceSnapshot, onceLedger, memoryTools, memoryEngineSession, memorySearchToolsPlanned, memoryPairExcludedName, memoryPairOccupiedName, resume, skillListingBudget } = input;
|
|
13
13
|
const tools = roster.tools;
|
|
14
14
|
let memoryBlock = memoryBlockFromEngine;
|
|
15
15
|
let instructionSources;
|
|
@@ -142,11 +142,12 @@ export async function prepareProjectContext(input) {
|
|
|
142
142
|
...(declaredSkillRank.get(s.name) !== undefined ? { declaredRank: declaredSkillRank.get(s.name) } : {}),
|
|
143
143
|
})),
|
|
144
144
|
seedAnnounced: resume !== undefined || spec.sessionId !== undefined,
|
|
145
|
+
budget: skillListingBudget,
|
|
145
146
|
}
|
|
146
147
|
:
|
|
147
148
|
(resume?.seed.announcedListings?.skills?.length ?? 0) > 0 || spec.sessionId !== undefined
|
|
148
149
|
?
|
|
149
|
-
{ entries: [], seedAnnounced: true }
|
|
150
|
+
{ entries: [], seedAnnounced: true, budget: skillListingBudget }
|
|
150
151
|
: undefined;
|
|
151
152
|
return { memoryBlock, instructionSources, projectInstructionContent, skillSpecs, hasSkillManifest, skillsListing, sharedMemoryPairMounted, memoryEnginePairMounted };
|
|
152
153
|
}
|
|
@@ -117,8 +117,8 @@ export interface PreparePromptInputsResult {
|
|
|
117
117
|
tzValid: boolean;
|
|
118
118
|
/** owned — the client's declared zone as given (paired with `tzValid`; unvalidated on its own). */
|
|
119
119
|
userTz: string | undefined;
|
|
120
|
-
/** owned — the git-status lane's run-local state (empty on a hands-less leg); the run loop and the
|
|
121
|
-
*
|
|
120
|
+
/** owned — the git-status lane's run-local state (empty on a hands-less leg); the run loop and the checkpoint
|
|
121
|
+
* serializer mutate its fields from here on (`announced`, `mirrorOwed`, …; the frame's receipt rides the leg's shared `Prepared.frameReceipts`). */
|
|
122
122
|
gitStatusRef: GitStatusLaneRef;
|
|
123
123
|
}
|
|
124
124
|
/** The M10 phase body — prepareTask's prompt-inputs stretch, verbatim (see the module header). */
|
|
@@ -28,8 +28,10 @@ export interface PrepareRunRefsInput {
|
|
|
28
28
|
* worktree helper). Never mutated. */
|
|
29
29
|
spec: Pick<TaskSpec, "forwardSubagentEvents" | "retainSubagentSessions" | "handsReadOnly">;
|
|
30
30
|
/** borrowed-readonly — the trusted spawn-side channel; only `onForwardEvent` (the deployment's display
|
|
31
|
-
* sink) is read,
|
|
32
|
-
|
|
31
|
+
* sink) is read, to decide whether a forwarding wrapper exists at all, and `forwardScope` (the run's
|
|
32
|
+
* forwarding containment, read by the door). The OBJECT is what the wrapper hands the door rather than
|
|
33
|
+
* the pulled-out function: the sink keeps its receiver and the run keeps one failure count. */
|
|
34
|
+
internals: Pick<RunInternals, "onForwardEvent" | "forwardScope"> | undefined;
|
|
33
35
|
/** borrowed-readonly — the run's resolved env; the worktree helper closes over it (lazy: no I/O until a
|
|
34
36
|
* delegation actually mints a worktree). */
|
|
35
37
|
executionEnv: ExecutionEnv;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createSubagentWorktreeHelper } from "../../agents/subagent.js";
|
|
2
2
|
import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
|
|
3
3
|
import { ActiveSkillScope } from "./active-skill-scope.js";
|
|
4
|
+
import { forwardSafely } from "./forward-sink.js";
|
|
4
5
|
import { createEditedFilesLedger } from "./edited-files-ledger.js";
|
|
5
6
|
export function forwardsSubagentEvents(spec) {
|
|
6
7
|
return spec.forwardSubagentEvents === true;
|
|
@@ -14,14 +15,14 @@ export function prepareRunRefs(input) {
|
|
|
14
15
|
const harnessRef = {};
|
|
15
16
|
const toolProgressRef = {};
|
|
16
17
|
const skillScope = new ActiveSkillScope();
|
|
17
|
-
const
|
|
18
|
-
const forwardEvent =
|
|
18
|
+
const sinkWired = internals?.onForwardEvent !== undefined;
|
|
19
|
+
const forwardEvent = sinkWired
|
|
19
20
|
? (e) => {
|
|
20
21
|
if (e.type === "task_progress")
|
|
21
|
-
|
|
22
|
+
forwardSafely(internals, e);
|
|
22
23
|
else if (spec.forwardSubagentEvents === true &&
|
|
23
24
|
(e.type === "text_delta" || e.type === "text_end" || e.type === "reasoning_delta" || e.type === "tool_start" || e.type === "tool_progress" || e.type === "tool_end")) {
|
|
24
|
-
|
|
25
|
+
forwardSafely(internals, e);
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
: undefined;
|