@sema-agent/core 7.31.1 → 7.32.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 +64 -0
- package/dist/agents/inherited-tool-pool.d.ts +28 -0
- package/dist/agents/inherited-tool-pool.js +15 -0
- package/dist/agents/list-agents-tool.d.ts +7 -0
- package/dist/agents/list-agents-tool.js +9 -2
- package/dist/agents/peer-session-drain.d.ts +2 -0
- package/dist/agents/peer-session-drain.js +1 -0
- package/dist/agents/send-message-tool.d.ts +17 -0
- package/dist/agents/send-message-tool.js +11 -4
- package/dist/agents/subagent.d.ts +10 -1
- package/dist/agents/subagent.js +24 -8
- package/dist/core/ask-origin.d.ts +15 -6
- package/dist/core/ask-origin.js +15 -9
- package/dist/core/background-shell.d.ts +45 -0
- package/dist/core/background-shell.js +13 -0
- package/dist/core/checkpoint-store.d.ts +5 -0
- package/dist/core/checkpoint-store.js +3 -2
- package/dist/core/engine-notice.d.ts +9 -0
- package/dist/core/gate-lanes.js +1 -1
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +4 -0
- package/dist/core/hooks.d.ts +2 -0
- package/dist/core/read-root-grant-screen.d.ts +23 -0
- package/dist/core/read-root-grant-screen.js +17 -0
- package/dist/core/runner/contracts.d.ts +1 -1
- package/dist/core/runner/prepare-artifact.d.ts +12 -7
- package/dist/core/runner/prepare-artifact.js +10 -8
- package/dist/core/runner/prepare-ask-lane.d.ts +3 -0
- package/dist/core/runner/prepare-ask-lane.js +5 -1
- package/dist/core/runner/prepare-boundary-parks.js +5 -5
- package/dist/core/runner/prepare-delegation-surface.d.ts +18 -0
- package/dist/core/runner/prepare-delegation-surface.js +6 -25
- package/dist/core/runner/prepare-hands-readface.d.ts +12 -89
- package/dist/core/runner/prepare-hands-readface.js +14 -48
- package/dist/core/runner/prepare-inherited-gate.d.ts +3 -1
- package/dist/core/runner/prepare-inherited-gate.js +2 -1
- package/dist/core/runner/prepare-park-ask.js +6 -4
- package/dist/core/runner/prepare-policy-chain.js +4 -0
- package/dist/core/runner/prepare-task.js +5 -3
- package/dist/core/runner/read-root-grant.d.ts +66 -0
- package/dist/core/runner/read-root-grant.js +77 -0
- package/dist/core/runner/run-notification-lane.d.ts +1 -1
- package/dist/core/runner/run-settle-and-teardown.js +3 -9
- package/dist/core/runner/runtask.js +1 -1
- package/dist/core/runner/send-message-continuation.d.ts +45 -0
- package/dist/core/runner/send-message-continuation.js +27 -0
- package/dist/core/runner/session-read-file-states.d.ts +91 -0
- package/dist/core/runner/session-read-file-states.js +43 -0
- package/dist/core/store-contracts/checkpoint-store-contract.d.ts +2 -0
- package/dist/core/store-contracts/checkpoint-store-contract.js +19 -0
- package/dist/core/task-registry-monitor.js +3 -0
- package/dist/core/task-registry-shared.d.ts +16 -3
- package/dist/core/task-registry.d.ts +65 -29
- package/dist/core/task-registry.js +78 -28
- package/dist/core/task-spec.d.ts +5 -3
- package/dist/core/tool-policy.d.ts +23 -0
- package/dist/core/tool-policy.js +15 -4
- package/dist/core/tool-spec.d.ts +2 -0
- package/dist/engine/execution-env/node-execution-env.d.ts +13 -3
- package/dist/engine/execution-env/node-execution-env.js +26 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -1
- package/dist/scenarios/full-body.d.ts +5 -2
- package/dist/scenarios/full-body.js +14 -7
- package/dist/tools/fs/fs-bash-background-text.d.ts +16 -0
- package/dist/tools/fs/fs-bash-background-text.js +22 -0
- package/dist/tools/fs/fs-bash.d.ts +6 -0
- package/dist/tools/fs/fs-bash.js +9 -27
- package/dist/tools/fs/fs-read.d.ts +2 -2
- package/dist/tools/fs/fs-search-tools.d.ts +3 -2
- package/dist/tools/fs/index.d.ts +8 -1
- package/dist/tools/fs/index.js +2 -3
- package/dist/tools/fs/repo-map.d.ts +2 -1
- package/dist/tools/fs/safety.d.ts +3 -1
- package/dist/tools/fs/safety.js +2 -1
- package/dist/tools/fs/shell-read-check.d.ts +3 -0
- package/dist/tools/fs/shell-read-check.js +1 -1
- package/dist/tools/fs/shell-read-establish.d.ts +3 -1
- package/dist/tools/fs/shell-read-establish.js +2 -2
- package/dist/tools/monitor.d.ts +5 -4
- package/dist/tools/monitor.js +5 -4
- package/dist/tools/todo.d.ts +3 -3
- package/dist/tools/todo.js +5 -4
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +13 -1
- package/wire-changes/core@7.32.0.json +850 -0
|
@@ -10,6 +10,8 @@ import { createShellReadStation } from "../../tools/fs/shell-read-check.js";
|
|
|
10
10
|
import { normalizeFileText } from "../../tools/fs/encoding.js";
|
|
11
11
|
import { wholeFileRecordsFromTranscript } from "./session-file-state-replay.js";
|
|
12
12
|
import { deliverEngineNotice } from "../types.js";
|
|
13
|
+
import { readRootGrantOver } from "./read-root-grant.js";
|
|
14
|
+
import { sessionSeatAnchorHolds } from "./session-read-file-states.js";
|
|
13
15
|
import { rebaseWorkspacePath } from "./workspace-path.js";
|
|
14
16
|
import { RosterBuilder } from "../tool-roster.js";
|
|
15
17
|
const faceClampAnnouncedSinks = { read: new WeakSet(), write: new WeakSet() };
|
|
@@ -31,49 +33,6 @@ export function __resetReadFaceClampAnnouncement() {
|
|
|
31
33
|
faceClampConsoleAnnounced.read = false;
|
|
32
34
|
faceClampConsoleAnnounced.write = false;
|
|
33
35
|
}
|
|
34
|
-
export class SessionReadFileStates {
|
|
35
|
-
seats = new Map();
|
|
36
|
-
static CAP = 1024;
|
|
37
|
-
get(sessionId) {
|
|
38
|
-
const seat = this.seats.get(sessionId);
|
|
39
|
-
if (seat !== undefined) {
|
|
40
|
-
this.seats.delete(sessionId);
|
|
41
|
-
this.seats.set(sessionId, seat);
|
|
42
|
-
}
|
|
43
|
-
return seat;
|
|
44
|
-
}
|
|
45
|
-
adopt(sessionId, state, anchorLeafId) {
|
|
46
|
-
this.seats.delete(sessionId);
|
|
47
|
-
this.seats.set(sessionId, { state, anchorLeafId });
|
|
48
|
-
while (this.seats.size > SessionReadFileStates.CAP) {
|
|
49
|
-
const oldest = this.seats.keys().next().value;
|
|
50
|
-
if (oldest === undefined)
|
|
51
|
-
break;
|
|
52
|
-
this.seats.delete(oldest);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
reanchor(sessionId, state, anchorLeafId) {
|
|
56
|
-
const seat = this.seats.get(sessionId);
|
|
57
|
-
if (seat !== undefined && seat.state === state)
|
|
58
|
-
seat.anchorLeafId = anchorLeafId;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
export async function sessionSeatAnchorHolds(getEntry, anchorLeafId, currentLeafId) {
|
|
62
|
-
if (anchorLeafId === null)
|
|
63
|
-
return false;
|
|
64
|
-
let id = currentLeafId;
|
|
65
|
-
for (let steps = 0; id !== null && steps < 100_000; steps++) {
|
|
66
|
-
if (id === anchorLeafId)
|
|
67
|
-
return true;
|
|
68
|
-
const entry = await getEntry(id);
|
|
69
|
-
if (entry === null || entry === undefined)
|
|
70
|
-
return false;
|
|
71
|
-
if (entry.type === "compaction")
|
|
72
|
-
return false;
|
|
73
|
-
id = entry.parentId;
|
|
74
|
-
}
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
36
|
function assertStringArraySeat(value, seat, entryLaw) {
|
|
78
37
|
if (value === undefined)
|
|
79
38
|
return;
|
|
@@ -149,6 +108,8 @@ export function resolveHandsLessReadFace(input) {
|
|
|
149
108
|
handsLessResolvedWriteFace,
|
|
150
109
|
additionalRootsCanonical: [],
|
|
151
110
|
additionalReadRootsCanonical: [],
|
|
111
|
+
grantReadRoot: undefined,
|
|
112
|
+
artifactStoreRef: undefined,
|
|
152
113
|
attachmentRootCanonical: undefined,
|
|
153
114
|
readDenyMatcher: undefined,
|
|
154
115
|
envHandToolNames: new Set(),
|
|
@@ -181,6 +142,8 @@ export async function prepareHandsMount(input) {
|
|
|
181
142
|
const shellGatedTools = new Set();
|
|
182
143
|
const additionalRootsCanonical = [];
|
|
183
144
|
const additionalReadRootsCanonical = [];
|
|
145
|
+
let grantReadRoot;
|
|
146
|
+
const artifactStoreRef = { current: undefined };
|
|
184
147
|
let attachmentRootCanonical;
|
|
185
148
|
let readDenyMatcher;
|
|
186
149
|
if (handsEnabled) {
|
|
@@ -220,6 +183,7 @@ export async function prepareHandsMount(input) {
|
|
|
220
183
|
}
|
|
221
184
|
for (const a of [additionalRootsCanonical, additionalReadRootsCanonical])
|
|
222
185
|
a.splice(0, a.length, ...new Set(a));
|
|
186
|
+
grantReadRoot = readRootGrantOver({ env: executionEnv, writeRoots: additionalRootsCanonical, readRoots: additionalReadRootsCanonical, artifactStore: artifactStoreRef });
|
|
223
187
|
const readFileState = new Map((resume?.seed.readFileState ?? []).map(([k, v]) => [rebaseRestoredPath(k), v]));
|
|
224
188
|
readFileStateForCheckpoint = readFileState;
|
|
225
189
|
seedContextFiles = async (files) => {
|
|
@@ -341,19 +305,18 @@ export async function prepareHandsMount(input) {
|
|
|
341
305
|
...(resolvedReadFace !== undefined ? { face: resolvedReadFace } : {}),
|
|
342
306
|
});
|
|
343
307
|
if (handsIncludeShell) {
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
: undefined;
|
|
308
|
+
const spoolFence = handsReadOnly ? createShellOverflowSpoolFence(executionEnv) : undefined;
|
|
309
|
+
const readonlyBoundary = () => spoolFence !== undefined ? { roots: [rootCanonical, ...additionalRootsCanonical, ...additionalReadRootsCanonical], cwd: rootCanonical, ...denySeat, alsoInside: spoolFence } : undefined;
|
|
347
310
|
shellReadStation = createShellReadStation({
|
|
348
311
|
env: executionEnv,
|
|
349
312
|
tools: new Set(handsReadOnly ? ["Bash"] : backgroundTaskToolsActive ? ["Bash", "Monitor"] : ["Bash"]),
|
|
350
313
|
seatOf: () => (handsReadOnly ? "readonly" : effectiveShellGate === "classify" ? "bash_classify" : "bash_off"),
|
|
351
|
-
boundaryOf: () => readonlyBoundary ?? shellReadBoundary(),
|
|
314
|
+
boundaryOf: () => readonlyBoundary() ?? shellReadBoundary(), rootsCanonical: true,
|
|
352
315
|
});
|
|
353
316
|
}
|
|
354
317
|
const band = createHandsToolkit(executionEnv, readFileState, rootCanonical, {
|
|
355
318
|
...(additionalRootsCanonical.length > 0 ? { additionalRoots: additionalRootsCanonical } : {}),
|
|
356
|
-
|
|
319
|
+
additionalReadRoots: additionalReadRootsCanonical,
|
|
357
320
|
...(readDenyAdditions.length > 0 ? { readDenyPatterns: readDenyAdditions } : {}),
|
|
358
321
|
...(readDenyBuiltinCfg.tiers !== undefined ? { readDenyBuiltinTiers: readDenyBuiltinCfg.tiers } : {}),
|
|
359
322
|
...(readDenyBuiltinCfg.exclude !== undefined ? { readDenyBuiltinExclude: readDenyBuiltinCfg.exclude } : {}),
|
|
@@ -372,6 +335,7 @@ export async function prepareHandsMount(input) {
|
|
|
372
335
|
taskScope,
|
|
373
336
|
oneShot: spec.oneShot,
|
|
374
337
|
...(sessionId !== undefined ? { sessionId } : {}),
|
|
338
|
+
retainBackgroundProcesses: spec.retainBackgroundProcesses === true,
|
|
375
339
|
...(internals?.onTaskNotification !== undefined ? { taskNotification: internals.onTaskNotification } : {}),
|
|
376
340
|
...(internals?.detachHub !== undefined ? { detachHub: internals.detachHub } : {}),
|
|
377
341
|
onToolProgress: (body) => input.toolProgressRef.current?.emit(body),
|
|
@@ -449,6 +413,8 @@ export async function prepareHandsMount(input) {
|
|
|
449
413
|
handsLessResolvedFace,
|
|
450
414
|
additionalRootsCanonical,
|
|
451
415
|
additionalReadRootsCanonical,
|
|
416
|
+
grantReadRoot,
|
|
417
|
+
artifactStoreRef,
|
|
452
418
|
attachmentRootCanonical,
|
|
453
419
|
readDenyMatcher,
|
|
454
420
|
envHandToolNames,
|
|
@@ -44,7 +44,7 @@ import type { DelegationContentSafety } from "../memory-engine/delegation-proven
|
|
|
44
44
|
import { type SessionPermissionRules } from "../session-policy-store.js";
|
|
45
45
|
import type { SessionStore } from "../session.js";
|
|
46
46
|
import { type OnAsk } from "../tool-policy.js";
|
|
47
|
-
import type { ApproverPosture, RunnerDeps, RuntimeCaps, ShellGateDoctrine, TaskEvent, TaskSpec, ToolExecuteContext } from "../types.js";
|
|
47
|
+
import type { ApproverPosture, RunnerDeps, RuntimeCaps, ShellGateDoctrine, TaskEvent, TaskSpec, ToolExecuteContext, ToolSpec } from "../types.js";
|
|
48
48
|
import type { ActiveSkillScope } from "./active-skill-scope.js";
|
|
49
49
|
import type { InheritedGate, Prepared, PrepareResume, RunInternals, StopForDenialLimit, ToolFaceSnapshot } from "./contracts.js";
|
|
50
50
|
export interface PrepareInheritedGateInput {
|
|
@@ -89,6 +89,8 @@ export interface PrepareInheritedGateInput {
|
|
|
89
89
|
/** borrowed-readonly — the frozen tool-face snapshot (the ctx forwards exclude / defer / alwaysLoad / restoreGated; the
|
|
90
90
|
* full-shell predicate reads the exclusion of Bash). */
|
|
91
91
|
toolFaceSnapshot: ToolFaceSnapshot;
|
|
92
|
+
/** borrowed-readonly — the ctx's `mountsCallerTool` seat: identity membership in the caller's `spec.tools`, pre-gate. */
|
|
93
|
+
mountsCallerTool: (tool: ToolSpec) => boolean;
|
|
92
94
|
/** borrowed-readonly — the resolved prompt profile (ctx seat). */
|
|
93
95
|
promptProfile: ToolExecuteContext["promptProfile"];
|
|
94
96
|
/** borrowed-readonly — the resolved interaction posture (ctx seat; absent ⇒ no key). */
|
|
@@ -8,7 +8,7 @@ import { askApproverIdentity, durableMandateSourceOf } from "../tool-policy.js";
|
|
|
8
8
|
import { attachRebuiltDenialTrackers, createDenialLimitStop } from "./denial-limit-arms.js";
|
|
9
9
|
import { asThrownError } from "../thrown-value.js";
|
|
10
10
|
export function prepareInheritedGate(input) {
|
|
11
|
-
const { spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, autoModeSeat, approverPostureSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun } = input;
|
|
11
|
+
const { spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, toolFaceSnapshot, mountsCallerTool, promptProfile, resolvedInteractionPosture, autoModeSeat, approverPostureSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun } = input;
|
|
12
12
|
const liveInheritedGate = internals?.inheritedGate;
|
|
13
13
|
const seedInheritedGate = resume?.seed.inheritedGate;
|
|
14
14
|
const inheritedAncestorRules = (() => {
|
|
@@ -205,6 +205,7 @@ export function prepareInheritedGate(input) {
|
|
|
205
205
|
...(readDenyAdditionsNormalized.length > 0 ? { readDenyPatterns: Object.freeze(readDenyAdditionsNormalized.map((e) => ({ ...e }))) } : {}),
|
|
206
206
|
...(spec.interactiveTools === false ? { interactiveTools: false } : {}),
|
|
207
207
|
oneShot: spec.oneShot,
|
|
208
|
+
mountsCallerTool,
|
|
208
209
|
parentSpecSeat,
|
|
209
210
|
...(internals?.questionFaceStripped === true ? { questionFaceStripped: true } : {}),
|
|
210
211
|
excludeTools: toolFaceSnapshot.exclude,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { uuidv7 } from "../../engine/session/uuid.js";
|
|
2
2
|
import { Value } from "typebox/value";
|
|
3
3
|
import { ASK_USER_QUESTION_TOOL_NAME, classifyQuestionOutcome, validateAskQuestions } from "../ask-question.js";
|
|
4
|
-
import { hasBackgroundShell, sweepBackgroundShells } from "../background-shell.js";
|
|
4
|
+
import { hasBackgroundShell, sweepBackgroundShells, sweepParkOnlyBackgroundShells } from "../background-shell.js";
|
|
5
5
|
import { boundInputHashOf } from "../canonical-json.js";
|
|
6
6
|
import { buildRiskDescriptor, checkpointVersionFor, debitLedger, encodeAtFidelity, mintCheckpointId, mintCheckpointToken, resolveDeclaredFidelity, samePlainValue, } from "../checkpoint-store.js";
|
|
7
7
|
import { pauseOf } from "../pause-registry.js";
|
|
@@ -229,8 +229,8 @@ export function prepareParkAsk(input) {
|
|
|
229
229
|
}
|
|
230
230
|
}
|
|
231
231
|
else if (parkOnlyRemoteEnv !== undefined) {
|
|
232
|
-
if (hasBackgroundShell(parkOnlyRemoteEnv)
|
|
233
|
-
await
|
|
232
|
+
if (hasBackgroundShell(parkOnlyRemoteEnv))
|
|
233
|
+
await sweepParkOnlyBackgroundShells(parkOnlyRemoteEnv, defaultTaskRegistry, { task: spec.retainBackgroundProcesses === true });
|
|
234
234
|
remoteHandle = parkOnlyHandle(parkOnlyRemoteEnv);
|
|
235
235
|
}
|
|
236
236
|
token = mintCheckpointToken();
|
|
@@ -277,8 +277,10 @@ export function prepareParkAsk(input) {
|
|
|
277
277
|
}
|
|
278
278
|
const approvalLedger = debitLedger(priorLedger, liveSpendRef.get?.() ?? { costMicroUsd: 0, tokens: 0, turns: 0, walltimeMs: 0 }, resourceTotal, { countSlice: false });
|
|
279
279
|
const rowMatchedAskRule = matchedAskRule ?? storedShapeFacts?.matchedAskRule;
|
|
280
|
+
const storedShapeMandated = askMandateMember(storedShapeFacts).mandated === true;
|
|
280
281
|
const clearanceFacts = {
|
|
281
|
-
...(carry?.mandated === true ||
|
|
282
|
+
...(carry?.mandated === true || storedShapeMandated ? { mandated: true } : {}),
|
|
283
|
+
...(carry?.mandate !== undefined && !storedShapeMandated ? { mandate: carry.mandate } : {}),
|
|
282
284
|
...(rowRealApproval !== undefined ? { requiresRealApproval: true } : {}),
|
|
283
285
|
...(shadowedRule !== undefined ? { persistedRuleShadowed: shadowedRule } : {}),
|
|
284
286
|
...(askDecisionReason !== undefined ? { decisionReason: askDecisionReason } : {}),
|
|
@@ -13,6 +13,7 @@ import { createActiveSkillScopePolicy } from "./active-skill-scope.js";
|
|
|
13
13
|
import { askMandateMember, probeMandatedAsk } from "../ask-origin.js";
|
|
14
14
|
const ancestorFacts = (ask) => ({ ...(ask ?? {}), ancestorResolved: true });
|
|
15
15
|
import { headlessDenyAtFold, headlessDenyAtRecheck, inheritedAskCarry, judgeInheritedClassifier, lateAskSettlementObserver, settleDenialLimitFallback } from "./denial-limit-arms.js";
|
|
16
|
+
import { settleReadRootGrant } from "./read-root-grant.js";
|
|
16
17
|
import { askGrantShapeOf } from "./inherited-ask-grants.js";
|
|
17
18
|
import { createPermissionRuleLanes, inheritedAskRuleEvidence, createRuleOffersOf } from "./permission-rule-lanes.js";
|
|
18
19
|
import { createLiveSessionRules, createSessionRulePolicy } from "./session-rule-policy.js";
|
|
@@ -360,6 +361,7 @@ export async function preparePolicyChain(input) {
|
|
|
360
361
|
...inheritedAskCarry(judged, isLiveApproverSeat(onAskOf), ancestorTracker),
|
|
361
362
|
ruleEvidence: inheritedAskEvidence,
|
|
362
363
|
}, onAskOf, csignal ?? abortController.signal, lateAskSettlementObserver({ toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
|
|
364
|
+
void settleReadRootGrant(rr.readRootGrant, undefined, { toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice });
|
|
363
365
|
settleDenialLimitFallback({ fallback: editFallback, mintedHere: editMintedHere, tracker: ancestorTracker, resolved: rr, headless: headlessDenyAtRecheck, stop: stopForDenialLimit, toolName: creq.toolName, toolCallId: creq.toolCallId });
|
|
364
366
|
if (rr.action !== "allow")
|
|
365
367
|
return rr;
|
|
@@ -436,6 +438,7 @@ export async function preparePolicyChain(input) {
|
|
|
436
438
|
...inheritedAskCarry(judged, isLiveApproverSeat(pc.onAsk), pc.autoMode?.denialTracking),
|
|
437
439
|
ruleEvidence: inheritedAskEvidence,
|
|
438
440
|
}, pc.onAsk, csignal ?? abortController.signal, lateAskSettlementObserver({ toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
|
|
441
|
+
void settleReadRootGrant(resolved.readRootGrant, undefined, { toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice });
|
|
439
442
|
const askWaitMs = Math.max(0, now() - askT0);
|
|
440
443
|
if (resolved.action === "deny" && resolved.approverUnavailable === true) {
|
|
441
444
|
if (markInheritedUnavailable(creq.toolCallId))
|
|
@@ -506,6 +509,7 @@ export async function preparePolicyChain(input) {
|
|
|
506
509
|
...inheritedAskCarry(judged, isLiveApproverSeat(pc.onAsk), pc.autoMode?.denialTracking),
|
|
507
510
|
ruleEvidence: inheritedAskEvidence,
|
|
508
511
|
}, pc.onAsk, csignal ?? abortController.signal, lateAskSettlementObserver({ toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
|
|
512
|
+
void settleReadRootGrant(resolved.readRootGrant, undefined, { toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice });
|
|
509
513
|
const askWaitMs = Math.max(0, now() - askT0);
|
|
510
514
|
if (resolved.action === "deny" && resolved.approverUnavailable === true) {
|
|
511
515
|
if (markInheritedUnavailable(creq.toolCallId))
|
|
@@ -7,6 +7,7 @@ import { uuidv7 } from "../../engine/session/uuid.js";
|
|
|
7
7
|
import { brainToRuntime } from "../runtime.js";
|
|
8
8
|
import { hasSessionFork } from "../session.js";
|
|
9
9
|
import { SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
|
|
10
|
+
import { callerToolMembership } from "../../agents/inherited-tool-pool.js";
|
|
10
11
|
import { loadAnnounceOnceLedger } from "./announce-once-ledger.js";
|
|
11
12
|
import { isValidReminderMark, mintReminderMark } from "../reminder-mint.js";
|
|
12
13
|
import { refuseInvalidApproverPosture } from "../auto-mode-directive.js";
|
|
@@ -271,6 +272,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
271
272
|
const runId = uuidv7();
|
|
272
273
|
if (runIdSink !== undefined)
|
|
273
274
|
runIdSink.runId = runId;
|
|
275
|
+
const mountsCallerTool = callerToolMembership(spec.tools);
|
|
274
276
|
const doors = prepareConfigDoors({ spec, deps, sessions, resume, internals });
|
|
275
277
|
spec = doors.spec;
|
|
276
278
|
const checkpointStore = resolveCheckpointStore(spec, deps), checkpointSeat = checkpointSeatOf(spec, checkpointStore);
|
|
@@ -390,7 +392,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
390
392
|
let readDenyAdditionsNormalized = [];
|
|
391
393
|
let handsLessResolvedFace;
|
|
392
394
|
let handsLessResolvedWriteFace;
|
|
393
|
-
const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, approverPosture: approverPostureResolved, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, checkpointSeat, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, autoModeSeat, approverPostureSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun: () => { abortController.abort(); void harness.abort(); }, runtimeCaps: () => runtimeCaps, hooks: () => hooks, preToolUseObservational: () => preToolUseObservational, hookTimeoutMs: () => hookTimeoutMs, hookEnvFace: () => hookEnvFace, autoModeDecider: () => autoModeDecider, autoModeDenialTracking: () => autoModeDenialTracking, autoModeArming: () => autoModeArming, agentForkDenial: () => agentForkDenial, observersActive: () => observersActive, resolvedReadFace: () => resolvedReadFace, resolvedWriteFace: () => resolvedWriteFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, handsLessResolvedWriteFace: () => handsLessResolvedWriteFace, centerAdoption: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
|
|
395
|
+
const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, approverPosture: approverPostureResolved, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, checkpointSeat, toolFaceSnapshot, mountsCallerTool, promptProfile, resolvedInteractionPosture, autoModeSeat, approverPostureSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun: () => { abortController.abort(); void harness.abort(); }, runtimeCaps: () => runtimeCaps, hooks: () => hooks, preToolUseObservational: () => preToolUseObservational, hookTimeoutMs: () => hookTimeoutMs, hookEnvFace: () => hookEnvFace, autoModeDecider: () => autoModeDecider, autoModeDenialTracking: () => autoModeDenialTracking, autoModeArming: () => autoModeArming, agentForkDenial: () => agentForkDenial, observersActive: () => observersActive, resolvedReadFace: () => resolvedReadFace, resolvedWriteFace: () => resolvedWriteFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, handsLessResolvedWriteFace: () => handsLessResolvedWriteFace, centerAdoption: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
|
|
394
396
|
const { roster, blockedRef, runtimeCaps, runtimeCapsFaulted, complianceDenies, complianceDegraded, agentForkDenial, observersActive, autoModeArmReason, autoModeDecider, autoModeDenialTracking, autoModeArming, selfOrchestrationActive, workflowToolsActive, workflowSizeGuideline } = await prepareCapsAndWorkflow({ spec, deps, internals, session, sessionId, runId, hostTaskId, taskScope, taskRootFinal, model, thinking, lockedPreflight, checkpointSeat, toolEffects, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, harnessRef, frozenOnAsk, parentSpecSeat, carrierReadFace, carrierWriteFace, forwardEvent, inheritedGateForChildren, enrichSpecToolCtx, maybeOffload, requestReview, enterPlanMode, autoModeIntent, peerLaneActive, peerSendMessageBuiltIn, runnerSelf, memoryEngineSession: () => memoryEngineSession, centerAdoption: () => centerAdoptionRef.current, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized });
|
|
395
397
|
const tools = roster.tools;
|
|
396
398
|
const pausedRef = {};
|
|
@@ -557,7 +559,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
557
559
|
`(per-principal entitlement governance, not a misconfiguration — Agent(subagent_type:"fork") will refuse honestly)`), { phase: "config", sessionId });
|
|
558
560
|
}
|
|
559
561
|
const { lspDiagnostics, nudgeLspOnEdit, lspRunIdent, managerWired: lspManagerWired } = prepareLsp({ spec, deps, executionEnv, taskRootFinal, handsEnabled, sessionId, handsCwdRef, roster, envHandToolNames });
|
|
560
|
-
prepareArtifact({ deps, runId, taskId: hostTaskId, sessionId, agentName: internals?.explicitAgentName, onAsk: frozenOnAsk, executionEnv, rootCanonical: attachmentRootCanonical, additionalRootsCanonical: [...additionalRootsCanonical, ...additionalReadRootsCanonical], readDenyMatcher, resolvedReadFace, handsCwdRef, roster, firstPartyOffload, envHandToolNames });
|
|
562
|
+
prepareArtifact({ deps, runId, taskId: hostTaskId, sessionId, agentName: internals?.explicitAgentName, onAsk: frozenOnAsk, executionEnv, rootCanonical: attachmentRootCanonical, additionalRootsCanonical: () => [...additionalRootsCanonical, ...additionalReadRootsCanonical], readDenyMatcher, resolvedReadFace, handsCwdRef, roster, firstPartyOffload, envHandToolNames, ...(handsReadFace.artifactStoreRef !== undefined ? { artifactStoreRef: handsReadFace.artifactStoreRef } : {}) });
|
|
561
563
|
if (internals?.delegationProvenance !== undefined && resume !== undefined) {
|
|
562
564
|
const live = internals.delegationProvenance.ref.current;
|
|
563
565
|
const seed = resume.seed.delegationProvenance;
|
|
@@ -716,7 +718,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
716
718
|
return undefined;
|
|
717
719
|
});
|
|
718
720
|
}
|
|
719
|
-
const { askLane } = prepareAskLane({ gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash });
|
|
721
|
+
const { askLane } = prepareAskLane({ gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash, grantReadRoot: handsReadFace.grantReadRoot });
|
|
720
722
|
const { saga } = prepareSuspendSaga({ gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, autoModeIntent, approverPosture: approverPostureResolved, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter });
|
|
721
723
|
const { suspendForResource, suspendForPlatformLimit, suspendForReview } = prepareBoundaryParks({ saga, spec, checkpointStore, abortController, harness, session, sessions, sessionId, deps, priorLedger, maxSlices, maxSuspends, resourceTotal, priorSuspendCount, suspendChainBase, humanReviewRef, liveSpendRef, now, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, pausedRef, remoteEnvFailures, resourceSuspendEligible, durableSuspendInfraReady, incompleteSuspendAdapter });
|
|
722
724
|
const { parkAsk } = prepareParkAsk({ askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas: rosterSeat, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, pausedRef });
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** The run's IN-FLIGHT read-root grant: the one place a screened grant ({@link ReadRootGrantScreen}) widens this run's
|
|
2
|
+
* read directories (through the hands phase's {@link GrantReadRoot}, built by {@link readRootGrantOver}), the one
|
|
3
|
+
* composer of the two notices, and the home of the store-isolation rule both root admission points judge by (the
|
|
4
|
+
* Artifact phase's mount and the grant) — machinery both phases reach down for. */
|
|
5
|
+
import type { ExecutionEnv } from "../../internal/harness.js";
|
|
6
|
+
import type { ReadRootGrantCandidate } from "../types.js";
|
|
7
|
+
import type { ReadRootGrantScreen } from "../read-root-grant-screen.js";
|
|
8
|
+
import { type RunnerDeps } from "../types.js";
|
|
9
|
+
/**
|
|
10
|
+
* THE store-isolation rule, one judgment for every root admission point (the Artifact mount over the prepare-time
|
|
11
|
+
* roots, an in-flight read-root grant over the granted root): a root that CONTAINS the local artifact store, lies
|
|
12
|
+
* INSIDE it, or IS it hands the file tools the store's ledger and stored files. Returns the first such root, or
|
|
13
|
+
* undefined.
|
|
14
|
+
*
|
|
15
|
+
* `storePhysical` is the store's physical path on THIS process's filesystem (`physicalPathOfExisting`, resolved
|
|
16
|
+
* once per run by the Artifact phase); a root is the execution env's canonical key. The judgment touches no
|
|
17
|
+
* filesystem — the grant runs after the approver's wait under the call's cut, and a synchronous lookup on a stalled
|
|
18
|
+
* store mount would block the event loop past that cut. The two are compared as identities only when both spell the
|
|
19
|
+
* same family (`within` reads both under the root's family); a root of the other family names a filesystem the store
|
|
20
|
+
* cannot be on, and no claim is made against it.
|
|
21
|
+
*/
|
|
22
|
+
export declare function rootAdmittingArtifactStore(storePhysical: string, rootsCanonical: readonly string[]): string | undefined;
|
|
23
|
+
/** The run's cell holding the local artifact store a granted root must never admit (its physical path, as the
|
|
24
|
+
* Artifact phase resolved it): minted with the grant by the hands phase, written by the Artifact phase (the one
|
|
25
|
+
* reader of the hosting seat) once the declaration validates. */
|
|
26
|
+
export interface ArtifactStoreRef {
|
|
27
|
+
current: string | undefined;
|
|
28
|
+
}
|
|
29
|
+
/** Why the run's station applied no grant: the spelling did not resolve (or its lookup was cut), it no longer
|
|
30
|
+
* resolves to itself (a directory swapped for a link while the card waited), or it would admit the local artifact
|
|
31
|
+
* store (the store-isolation rule the Artifact phase judges its mount by). */
|
|
32
|
+
type GrantRefusal = "unresolvable" | "resolves_elsewhere" | "admits_artifact_store";
|
|
33
|
+
/** Widen THIS run's live read roots by one granted candidate: the offered spelling must still be its own canonical
|
|
34
|
+
* form and must not admit the local artifact store, then one deduplicated push; `root` = the spelling held. Nothing
|
|
35
|
+
* is pushed once `signal` has fired (a late lookup never widens). */
|
|
36
|
+
export type GrantReadRoot = (candidate: ReadRootGrantCandidate, signal: AbortSignal) => Promise<{
|
|
37
|
+
ok: true;
|
|
38
|
+
root: string;
|
|
39
|
+
} | {
|
|
40
|
+
ok: false;
|
|
41
|
+
reason: GrantRefusal;
|
|
42
|
+
cause: string;
|
|
43
|
+
}>;
|
|
44
|
+
/** The hands phase's {@link GrantReadRoot} over the run's live root arrays: the offered spelling must still be its own
|
|
45
|
+
* canonical form, must not admit the store in `artifactStore` ({@link rootAdmittingArtifactStore}), then ONE
|
|
46
|
+
* deduplicated push onto `readRoots` (the live read set every read face reads per call); the write roots are only
|
|
47
|
+
* consulted for the dedup, never widened. */
|
|
48
|
+
export declare function readRootGrantOver(seats: {
|
|
49
|
+
env: Pick<ExecutionEnv, "canonicalPath">;
|
|
50
|
+
writeRoots: readonly string[];
|
|
51
|
+
readRoots: string[];
|
|
52
|
+
artifactStore: ArtifactStoreRef;
|
|
53
|
+
}): GrantReadRoot;
|
|
54
|
+
/** Act on one screened grant: `granted` widens through `grant` (absent on a station that cannot widen — an inherited
|
|
55
|
+
* ask, a hands-less leg) and is announced; every other outcome changes nothing and is announced too — never silent.
|
|
56
|
+
* Returns a promise ONLY while a grant is applied, so an answer with no grant costs no extra event-loop turn. */
|
|
57
|
+
export declare function settleReadRootGrant(screen: ReadRootGrantScreen | undefined, grant: GrantReadRoot | undefined, at: {
|
|
58
|
+
toolName: string;
|
|
59
|
+
toolCallId: string;
|
|
60
|
+
sessionId: string;
|
|
61
|
+
runId: string;
|
|
62
|
+
taskId?: string;
|
|
63
|
+
onNotice: RunnerDeps["onNotice"];
|
|
64
|
+
signal?: AbortSignal;
|
|
65
|
+
}): Promise<void> | undefined;
|
|
66
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { deliverEngineNotice } from "../types.js";
|
|
2
|
+
import { READ_ROOT_CANDIDATE_DIR_MAX } from "../read-root-candidate.js";
|
|
3
|
+
import { DEFAULT_GATE_AWAIT_TIMEOUT_MS } from "../gate-bounded-await.js";
|
|
4
|
+
import { thrownText } from "../thrown-value.js";
|
|
5
|
+
import { pathFamilyOf, withinAnyRoot } from "../../tools/fs/safety.js";
|
|
6
|
+
export function rootAdmittingArtifactStore(storePhysical, rootsCanonical) {
|
|
7
|
+
const storeFamily = pathFamilyOf({ root: storePhysical });
|
|
8
|
+
return rootsCanonical.find((root) => pathFamilyOf({ root }) === storeFamily && (withinAnyRoot([root], storePhysical) || withinAnyRoot([storePhysical], root)));
|
|
9
|
+
}
|
|
10
|
+
export function readRootGrantOver(seats) {
|
|
11
|
+
const { env, writeRoots, readRoots, artifactStore } = seats;
|
|
12
|
+
return async (candidate, signal) => {
|
|
13
|
+
const c = await env.canonicalPath(candidate.dir);
|
|
14
|
+
if (signal.aborted)
|
|
15
|
+
return { ok: false, reason: "unresolvable", cause: "the lookup was cut" };
|
|
16
|
+
if (!c.ok)
|
|
17
|
+
return { ok: false, reason: "unresolvable", cause: `${c.error.code}: ${c.error.message}` };
|
|
18
|
+
if (c.value !== candidate.dir)
|
|
19
|
+
return { ok: false, reason: "resolves_elsewhere", cause: `it resolves to ${c.value}` };
|
|
20
|
+
const store = artifactStore.current;
|
|
21
|
+
if (store !== undefined && rootAdmittingArtifactStore(store, [c.value]) !== undefined)
|
|
22
|
+
return { ok: false, reason: "admits_artifact_store", cause: "the Artifact tool's ledger and stored files stay outside every root the file tools reach" };
|
|
23
|
+
if (!writeRoots.includes(c.value) && !readRoots.includes(c.value))
|
|
24
|
+
readRoots.push(c.value);
|
|
25
|
+
return { ok: true, root: c.value };
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function settleReadRootGrant(screen, grant, at) {
|
|
29
|
+
if (screen === undefined)
|
|
30
|
+
return undefined;
|
|
31
|
+
const ids = { toolName: at.toolName, toolCallId: at.toolCallId, sessionId: at.sessionId, runId: at.runId, ...(at.taskId !== undefined ? { taskId: at.taskId } : {}) };
|
|
32
|
+
const reject = (reason, offered, requested, why) => deliverEngineNotice(at.onNotice, {
|
|
33
|
+
code: "approval.read_root_grant_rejected",
|
|
34
|
+
message: `the approval for "${at.toolName}" asked to add a read directory, and nothing was added: ${why}. The approval's own verdict is unchanged.`,
|
|
35
|
+
detail: { reason, ...(offered !== undefined ? { offered } : {}), ...(requested !== undefined ? { requested: requested.slice(0, READ_ROOT_CANDIDATE_DIR_MAX) } : {}), ...ids },
|
|
36
|
+
});
|
|
37
|
+
if (screen.verdict === "rejected") {
|
|
38
|
+
const why = {
|
|
39
|
+
on_refusal: "a directory grant rides an approval, and this answer refused the call",
|
|
40
|
+
not_a_string: "the grant is not a string",
|
|
41
|
+
no_candidate: "this approval card offered no directory",
|
|
42
|
+
mismatch: "the grant is not the directory the card offered (it must be sent back exactly as offered)",
|
|
43
|
+
}[screen.reason];
|
|
44
|
+
reject(screen.reason, screen.offered, screen.requested, why);
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
const { candidate } = screen;
|
|
48
|
+
if (grant === undefined) {
|
|
49
|
+
reject("no_read_roots", candidate.dir, candidate.dir, "this run has no read directories to widen");
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
const cut = AbortSignal.any([...(at.signal !== undefined ? [at.signal] : []), AbortSignal.timeout(DEFAULT_GATE_AWAIT_TIMEOUT_MS)]);
|
|
53
|
+
const cutShort = new Promise((resolve) => {
|
|
54
|
+
const onCut = () => resolve({ ok: false, reason: "unresolvable", cause: at.signal?.aborted === true ? "the call ended before it resolved" : `it did not resolve within ${DEFAULT_GATE_AWAIT_TIMEOUT_MS}ms` });
|
|
55
|
+
if (cut.aborted)
|
|
56
|
+
onCut();
|
|
57
|
+
else
|
|
58
|
+
cut.addEventListener("abort", onCut, { once: true });
|
|
59
|
+
});
|
|
60
|
+
const attempt = grant(candidate, cut).catch((err) => ({ ok: false, reason: "unresolvable", cause: thrownText(err) }));
|
|
61
|
+
return Promise.race([attempt, cutShort]).then((applied) => {
|
|
62
|
+
if (!applied.ok) {
|
|
63
|
+
const why = {
|
|
64
|
+
unresolvable: `the directory could not be resolved (${applied.cause})`,
|
|
65
|
+
resolves_elsewhere: `the offered path no longer resolves to itself (${applied.cause})`,
|
|
66
|
+
admits_artifact_store: `the directory would admit the artifact store (${applied.cause})`,
|
|
67
|
+
}[applied.reason];
|
|
68
|
+
reject(applied.reason, candidate.dir, candidate.dir, why);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
deliverEngineNotice(at.onNotice, {
|
|
72
|
+
code: "approval.read_root_granted",
|
|
73
|
+
message: `the approval for "${at.toolName}" added ${candidate.covers === "exact" ? "the path" : "the directory"} ${candidate.dir} to this run's read directories (reads only; for the rest of this run).`,
|
|
74
|
+
detail: { dir: candidate.dir, ...(candidate.covers !== undefined ? { covers: candidate.covers } : {}), root: applied.root, ...ids },
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -3,7 +3,7 @@ import type { PushQueue } from "../push-queue.js";
|
|
|
3
3
|
import type { SessionStore } from "../session.js";
|
|
4
4
|
import { type NotificationInjectionOutcome, type PendingSessionNotifications, type SystemInjectionPriority, type TaskNotificationPayload } from "../task-notification.js";
|
|
5
5
|
import type { RunnerDeps, TaskEvent, TaskSpec } from "../types.js";
|
|
6
|
-
import type { SessionReadFileStates } from "./
|
|
6
|
+
import type { SessionReadFileStates } from "./session-read-file-states.js";
|
|
7
7
|
import type { NotificationLaneBindings, Prepared, PrepareTaskFn, ResumeRun, RunInternals, RunnerDepsSeat, RunnerSelfSeat, TaskIdRef } from "./contracts.js";
|
|
8
8
|
export interface RunNotificationLaneInput<R> {
|
|
9
9
|
/** borrowed-readonly — the task spec: prepare's input, and the principal the peer scope is minted from. */
|
|
@@ -2,7 +2,7 @@ import { MAX_CONSECUTIVE_COMPACTION_FAILURES } from "./compaction-knobs.js";
|
|
|
2
2
|
import { isSyntheticApiErrorMessage } from "../../internal/harness.js";
|
|
3
3
|
import { adjudicateDerivedRoute, fallbackToPrimaryNotice, sameRouteIdentity } from "../../brain/route-adjudicator.js";
|
|
4
4
|
import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
|
|
5
|
-
import { hasBackgroundShell, sweepBackgroundShells } from "../background-shell.js";
|
|
5
|
+
import { hasBackgroundShell, retainsBackgroundProcesses, sweepBackgroundShells } from "../background-shell.js";
|
|
6
6
|
import { resolveCheckpointStore } from "../checkpoint-store.js";
|
|
7
7
|
import { hookSeatExpiredError, runHookSeat } from "../hooks.js";
|
|
8
8
|
import { modelCostToPricing } from "../pricing.js";
|
|
@@ -315,16 +315,10 @@ export async function runSettleAndTeardown(input) {
|
|
|
315
315
|
retainProcesses: spec.retainBackgroundProcesses === true,
|
|
316
316
|
}), "taskRegistry.settleKilledForOwner", (e) => runner.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
|
|
317
317
|
const bgEnv = prepared.ownedEnv ?? runner.deps.executionEnv;
|
|
318
|
-
const retainBgBySpec = spec.retainBackgroundProcesses === true
|
|
318
|
+
const retainBgBySpec = retainsBackgroundProcesses({ task: spec.retainBackgroundProcesses === true, envDying: ownedEnvDying });
|
|
319
319
|
if (bgEnv && hasBackgroundShell(bgEnv) && !retainBgBySpec) {
|
|
320
320
|
try {
|
|
321
|
-
const keepAlive = ownedEnvDying && bgEnv === prepared.ownedEnv
|
|
322
|
-
? []
|
|
323
|
-
: [
|
|
324
|
-
...defaultTaskRegistry.sessionResidentShellIds(prepared.sessionId, bgEnv),
|
|
325
|
-
...defaultTaskRegistry.timeoutResidentShellIds(bgEnv),
|
|
326
|
-
...defaultTaskRegistry.retainedShellIds(bgEnv),
|
|
327
|
-
];
|
|
321
|
+
const keepAlive = ownedEnvDying && bgEnv === prepared.ownedEnv ? [] : defaultTaskRegistry.runningShellIds(bgEnv);
|
|
328
322
|
await settleTeardownLeg(() => sweepBackgroundShells(bgEnv, defaultTaskRegistry, keepAlive.length > 0 ? { except: keepAlive } : undefined), "sweepBackgroundShells", (e) => runner.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
|
|
329
323
|
}
|
|
330
324
|
catch (e) {
|
|
@@ -15,7 +15,7 @@ import { PushQueue } from "../push-queue.js";
|
|
|
15
15
|
import { TtlSessionStore } from "../session-store.js";
|
|
16
16
|
import { errorCodeOf } from "./assemble-result.js";
|
|
17
17
|
import { effectiveDelegationFacts, prepareTask } from "./prepare-task.js";
|
|
18
|
-
import { SessionReadFileStates } from "./
|
|
18
|
+
import { SessionReadFileStates } from "./session-read-file-states.js";
|
|
19
19
|
import { RunnerSharedToolResultStore } from "../tool-result-store.js";
|
|
20
20
|
import { PAUSE_REGISTRY } from "../pause-registry.js";
|
|
21
21
|
import { toolPolicyNameSets } from "../tool-policy.js";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { RunnerDeps, ToolSpec } from "../types.js";
|
|
2
|
+
import type { RunnerSelfSeat } from "./contracts.js";
|
|
3
|
+
import type { SubagentTranscriptTier } from "../wiring-manifest.js";
|
|
4
|
+
/**
|
|
5
|
+
* The tier-3 revival spawner of the SendMessage mount: re-enters the
|
|
6
|
+
* MOUNTED delegation tool (identified by object, never by wire name) through the raw ToolSpec execute +
|
|
7
|
+
* the leg's ctx enricher, with a fresh toolCallId and the trusted `ctx.reviveClaim`, so a revival is the
|
|
8
|
+
* complete spawn chain. `undefined` when no delegation tool is mounted. Module-level: closes over
|
|
9
|
+
* nothing of the leg but the two values it is handed.
|
|
10
|
+
*/
|
|
11
|
+
declare function buildTier3ReviveSpawn(delegationForRevive: ToolSpec | undefined, enrichSpecToolCtx: (ctx: import("../types.js").ToolExecuteContext) => import("../types.js").ToolExecuteContext): ((req: {
|
|
12
|
+
row: import("../background-agent-store.js").BackgroundAgentRecord;
|
|
13
|
+
rev: number;
|
|
14
|
+
prompt: string;
|
|
15
|
+
peerSeed?: {
|
|
16
|
+
hopChain: string[];
|
|
17
|
+
};
|
|
18
|
+
}) => Promise<{
|
|
19
|
+
isError?: boolean;
|
|
20
|
+
content: string;
|
|
21
|
+
details?: unknown;
|
|
22
|
+
}>) | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* The tier-3 revival spawner: identify the mounted delegation
|
|
25
|
+
* tool by OBJECT via its `agentListing` marker (filled only by createSubagentTool; a caller's
|
|
26
|
+
* same-named shadow tool never carries it, so the trusted ctx.reviveClaim can never reach
|
|
27
|
+
* one). The call goes through the RAW ToolSpec execute + `enrichSpecToolCtx` (the same
|
|
28
|
+
* enrichment the injection map applies — the AgentTool adapter face forwards only
|
|
29
|
+
* {toolCallId, signal} and would strip the claim), so the revival is the complete spawn
|
|
30
|
+
* chain: notify lane, observer sink, roster, durable store. The request is built by
|
|
31
|
+
* SendMessage AT EXECUTE TIME (ctx-first identity); this closure holds only the tool object
|
|
32
|
+
* reference. Fresh toolCallId per revival (F-11③ — never SendMessage's own call id).
|
|
33
|
+
* `tier` is {@link sendMessageContinuationTier} over the same seats the mount receives.
|
|
34
|
+
*/
|
|
35
|
+
export declare function sendMessageContinuationSeats(seats: {
|
|
36
|
+
specTools: readonly ToolSpec[];
|
|
37
|
+
agentStore: RunnerDeps["backgroundAgentStore"];
|
|
38
|
+
mailbox: RunnerDeps["mailboxStore"];
|
|
39
|
+
runner: RunnerSelfSeat;
|
|
40
|
+
enrichSpecToolCtx: (ctx: import("../types.js").ToolExecuteContext) => import("../types.js").ToolExecuteContext;
|
|
41
|
+
}): {
|
|
42
|
+
reviveSpawn: ReturnType<typeof buildTier3ReviveSpawn>;
|
|
43
|
+
tier: SubagentTranscriptTier;
|
|
44
|
+
};
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { sendMessageContinuationTier } from "../../agents/send-message-tool.js";
|
|
3
|
+
function buildTier3ReviveSpawn(delegationForRevive, enrichSpecToolCtx) {
|
|
4
|
+
if (delegationForRevive === undefined)
|
|
5
|
+
return undefined;
|
|
6
|
+
return async (req) => {
|
|
7
|
+
const out = await delegationForRevive.execute({
|
|
8
|
+
description: req.row.description ?? "revived teammate",
|
|
9
|
+
prompt: req.prompt,
|
|
10
|
+
run_in_background: true,
|
|
11
|
+
...(req.row.agentType !== undefined ? { subagent_type: req.row.agentType } : {}),
|
|
12
|
+
...(req.row.name !== undefined ? { name: req.row.name } : {}),
|
|
13
|
+
...(req.row.model !== undefined ? { model: req.row.model } : {}),
|
|
14
|
+
}, enrichSpecToolCtx({ toolCallId: `rv-${randomBytes(8).toString("hex")}`, reviveClaim: { row: req.row, rev: req.rev, ...(req.peerSeed !== undefined ? { peerSeed: req.peerSeed } : {}) } }));
|
|
15
|
+
const o = (typeof out === "string" ? { content: out } : out);
|
|
16
|
+
return {
|
|
17
|
+
content: typeof o.content === "string" ? o.content : JSON.stringify(o.content),
|
|
18
|
+
...(o.isError === true ? { isError: true } : {}),
|
|
19
|
+
...(o.details !== undefined ? { details: o.details } : {}),
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function sendMessageContinuationSeats(seats) {
|
|
24
|
+
const delegationForRevive = seats.specTools.find((t) => t.agentListing !== undefined);
|
|
25
|
+
const reviveSpawn = seats.agentStore !== undefined && seats.mailbox !== undefined ? buildTier3ReviveSpawn(delegationForRevive, seats.enrichSpecToolCtx) : undefined;
|
|
26
|
+
return { reviveSpawn, tier: sendMessageContinuationTier({ agentStore: seats.agentStore, mailbox: seats.mailbox, reviveSpawn, runner: seats.runner }) };
|
|
27
|
+
}
|