@sema-agent/core 7.31.0 → 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 +106 -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 +5 -0
- package/dist/core/hooks.d.ts +2 -0
- package/dist/core/memory-engine/engine.d.ts +10 -1
- package/dist/core/memory-engine/engine.js +30 -6
- package/dist/core/memory-engine/file-backend.d.ts +15 -0
- package/dist/core/memory-engine/file-backend.js +53 -2
- package/dist/core/memory-engine/layout.d.ts +29 -4
- package/dist/core/memory-engine/layout.js +24 -4
- 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/secret-env.d.ts +1 -1
- package/dist/core/secret-env.js +7 -2
- 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/fs/shell-read-plan.d.ts +2 -2
- package/dist/tools/fs/shell-read-plan.js +15 -10
- package/dist/tools/fs/shell-read-word.d.ts +5 -4
- package/dist/tools/fs/shell-read-word.js +3 -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.31.1.json +189 -0
- package/wire-changes/core@7.32.0.json +850 -0
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Runner's session-scoped read-file-state CACHE and the licence to inherit from it: session-scoped cache
|
|
3
|
+
* machinery, not a phase step — the Runner owns the instance for its lifetime, the run internals carry it to the
|
|
4
|
+
* hands phase, and the hands phase adopts, inherits and seals seats through it.
|
|
5
|
+
*/
|
|
6
|
+
import type { ReadFileState } from "../../tools/fs/safety.js";
|
|
7
|
+
/**
|
|
8
|
+
* The Runner's per-session read-file-state seats (CC parity: readFileState is minted
|
|
9
|
+
* once per REPL session and mutated across turns; it has NO turn-boundary and NO abort clear site —
|
|
10
|
+
* its only clears are compaction/fork/subagent-cleanup/dispose). sema's map stays per-task (the §11
|
|
11
|
+
* ruling-A closure wiring), so the SESSION scope lives here instead: prepare adopts each task's live
|
|
12
|
+
* map under its sessionId, and the next same-session, same-Runner task INHERITS it (through the same
|
|
13
|
+
* containment judgment the transcript-replay seed applies — see `inheritSessionReadFileState`).
|
|
14
|
+
*
|
|
15
|
+
* This seat is a CACHE, never an authority:
|
|
16
|
+
* - a durable resume's checkpoint seed stays the sole authority on that leg (the seed was cut
|
|
17
|
+
* atomically with the leaf; this seat may be older OR newer than it);
|
|
18
|
+
* - a cross-process continuation misses here and falls back to the transcript replay (rung-2);
|
|
19
|
+
* - eviction (the LRU cap, a Runner restart) degrades to that same replay — never to wrong state.
|
|
20
|
+
*
|
|
21
|
+
* Bounded: an LRU over sessionIds (insertion-order refresh on read), because the SessionStore's own
|
|
22
|
+
* eviction is not observable from here — a dropped seat costs one transcript replay, nothing else.
|
|
23
|
+
* The stored value is the task's LIVE map (object identity), so in-turn mutations — tool write-backs,
|
|
24
|
+
* and above all the compaction clear — are the seat's truth by construction; a snapshot copy
|
|
25
|
+
* here would resurrect summarized-away reads on the next turn.
|
|
26
|
+
*
|
|
27
|
+
* BRANCH BINDING: a sessionId alone does not name a transcript — the same id
|
|
28
|
+
* can be rebranched (`resumeAt`), rewound out-of-band, or DELETED and re-created (`release()` + the
|
|
29
|
+
* default create-on-miss acquire), and the seat's map would then vouch for reads the CURRENT branch
|
|
30
|
+
* never saw. Each seat therefore records `anchorLeafId` — a leaf on the lineage its task produced —
|
|
31
|
+
* and inheritance must first prove the CURRENT branch still descends from that anchor
|
|
32
|
+
* (see {@link sessionSeatAnchorHolds}). The anchor is written twice: at ADOPT (the leaf at prepare;
|
|
33
|
+
* `null` when the branch was still empty) and SEALED at the task's settle to the task's own tail
|
|
34
|
+
* (`reanchor`). A `null` anchor is NEVER inheritable ("the branch is non-empty now" proves
|
|
35
|
+
* nothing about WHOSE messages those are — another Runner repopulating a released id, or a session
|
|
36
|
+
* import, makes a non-empty branch that never contained this seat's reads). An unprovable anchor
|
|
37
|
+
* falls back to the transcript replay, which reads the current branch by construction — cost, never
|
|
38
|
+
* wrongness.
|
|
39
|
+
*/
|
|
40
|
+
export declare class SessionReadFileStates {
|
|
41
|
+
private readonly seats;
|
|
42
|
+
private static readonly CAP;
|
|
43
|
+
/** The session's seat row, or undefined (cross-process / evicted / first turn) ⇒ caller replays.
|
|
44
|
+
* The caller must verify the anchor against the session's CURRENT branch before using `state`. */
|
|
45
|
+
get(sessionId: string): {
|
|
46
|
+
state: ReadFileState;
|
|
47
|
+
anchorLeafId: string | null;
|
|
48
|
+
} | undefined;
|
|
49
|
+
/** Adopt THIS task's live map as the session's seat (every hands leg: fresh, continuation, resume).
|
|
50
|
+
* `anchorLeafId` = the session's leaf as read at THIS prepare (null = the branch was empty; a
|
|
51
|
+
* null-anchored seat is not inheritable until the settle-time {@link reanchor} names a real leaf). */
|
|
52
|
+
adopt(sessionId: string, state: ReadFileState, anchorLeafId: string | null): void;
|
|
53
|
+
/** The settle-time SEAL: re-anchor the seat to the finished task's own tail, so a
|
|
54
|
+
* first-task (null-anchor) seat becomes inheritable only through a leaf its task provably wrote.
|
|
55
|
+
* Identity-guarded: touches the row only while it still holds THIS task's map (a later prepare on
|
|
56
|
+
* the same session has already replaced the seat and owns its own anchor). */
|
|
57
|
+
reanchor(sessionId: string, state: ReadFileState, anchorLeafId: string): void;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Does the session's CURRENT branch still descend from the seat's anchor?
|
|
61
|
+
* TRUE is the licence to inherit; anything unprovable answers FALSE (the caller then replays the
|
|
62
|
+
* transcript — the fail-closed direction costs a rebuild, never admits a wrong entry).
|
|
63
|
+
*
|
|
64
|
+
* - anchor `null`: NEVER inheritable (r2). The seat was adopted on an empty branch and its task
|
|
65
|
+
* never sealed (crashed pre-settle, or the leaf read failed) — "the branch is non-empty now"
|
|
66
|
+
* would also be true of a released-and-repopulated id or an imported log, whose messages are not
|
|
67
|
+
* this seat's lineage. The settle-time reanchor is what upgrades such a seat to a real leaf.
|
|
68
|
+
* - anchor non-null: holds iff walking parentIds from the current leaf reaches the anchor within
|
|
69
|
+
* a bounded number of steps (the walk length is one task's appends in the ordinary continuation).
|
|
70
|
+
* A rebranch/rewind moved the leaf OFF the anchor's subtree ⇒ the walk misses ⇒ no inherit; a
|
|
71
|
+
* recreated/foreign log does not contain the anchor ⇒ same answer; a re-IMPORT of the same log
|
|
72
|
+
* keeps the anchor's lineage ⇒ inheritance stays correct there by the same test.
|
|
73
|
+
* - a COMPACTION on the walked segment (A-082 idx2): refuses, even though the branch is perfectly
|
|
74
|
+
* intact. The walked segment is exactly what landed AFTER this seat was anchored, and a
|
|
75
|
+
* compaction there summarized older Read results OUT of the context the model will be shown —
|
|
76
|
+
* the very state the compaction clear removes from the live map at the landing site. That clear reaches the seat
|
|
77
|
+
* only when the SAME Runner did both (the seat IS that map, by object identity); a second Runner
|
|
78
|
+
* on the shared durable session clears its own map and commits the compaction as an ORDINARY
|
|
79
|
+
* LINEAR APPEND, which every other test here passes. Inheriting across it would let requireRead
|
|
80
|
+
* and checkStale both pass on a read whose card the model no longer has — a blind edit from a
|
|
81
|
+
* summarized-away recollection, the exact failure the compaction clear exists to prevent. Refusing
|
|
82
|
+
* costs one transcript replay, and the replay reads the POST-compaction branch by construction,
|
|
83
|
+
* so the fail-closed direction is also the CORRECT state. A compaction at or ABOVE the anchor is
|
|
84
|
+
* not on the walked segment and does not refuse: the seat that observed it already carries its
|
|
85
|
+
* clear (P-483-4), and an ordinary foreign turn with no compaction still inherits (G-483-5) —
|
|
86
|
+
* dropping every seat a foreign Runner touched would restore the cross-turn read-state amnesia this seat-anchor check already ended.
|
|
87
|
+
*/
|
|
88
|
+
export declare function sessionSeatAnchorHolds(getEntry: (id: string) => Promise<{
|
|
89
|
+
parentId: string | null;
|
|
90
|
+
type?: string;
|
|
91
|
+
} | null | undefined>, anchorLeafId: string | null, currentLeafId: string | null): Promise<boolean>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export class SessionReadFileStates {
|
|
2
|
+
seats = new Map();
|
|
3
|
+
static CAP = 1024;
|
|
4
|
+
get(sessionId) {
|
|
5
|
+
const seat = this.seats.get(sessionId);
|
|
6
|
+
if (seat !== undefined) {
|
|
7
|
+
this.seats.delete(sessionId);
|
|
8
|
+
this.seats.set(sessionId, seat);
|
|
9
|
+
}
|
|
10
|
+
return seat;
|
|
11
|
+
}
|
|
12
|
+
adopt(sessionId, state, anchorLeafId) {
|
|
13
|
+
this.seats.delete(sessionId);
|
|
14
|
+
this.seats.set(sessionId, { state, anchorLeafId });
|
|
15
|
+
while (this.seats.size > SessionReadFileStates.CAP) {
|
|
16
|
+
const oldest = this.seats.keys().next().value;
|
|
17
|
+
if (oldest === undefined)
|
|
18
|
+
break;
|
|
19
|
+
this.seats.delete(oldest);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
reanchor(sessionId, state, anchorLeafId) {
|
|
23
|
+
const seat = this.seats.get(sessionId);
|
|
24
|
+
if (seat !== undefined && seat.state === state)
|
|
25
|
+
seat.anchorLeafId = anchorLeafId;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function sessionSeatAnchorHolds(getEntry, anchorLeafId, currentLeafId) {
|
|
29
|
+
if (anchorLeafId === null)
|
|
30
|
+
return false;
|
|
31
|
+
let id = currentLeafId;
|
|
32
|
+
for (let steps = 0; id !== null && steps < 100_000; steps++) {
|
|
33
|
+
if (id === anchorLeafId)
|
|
34
|
+
return true;
|
|
35
|
+
const entry = await getEntry(id);
|
|
36
|
+
if (entry === null || entry === undefined)
|
|
37
|
+
return false;
|
|
38
|
+
if (entry.type === "compaction")
|
|
39
|
+
return false;
|
|
40
|
+
id = entry.parentId;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import type { RedactionConfidence, RedactionSource } from "./arg-summary.js";
|
|
20
20
|
import { type EngineNotice } from "./engine-notice.js";
|
|
21
21
|
/** Which rule classified a key as secret-shaped — the env arm's `kind` vocabulary. */
|
|
22
|
-
export type SecretEnvFindingKind = "suffix-rule" | "exact-name";
|
|
22
|
+
export type SecretEnvFindingKind = "suffix-rule" | "namespace-rule" | "exact-name";
|
|
23
23
|
/** One env entry the `scrub` policy dropped: the key (the VALUE is never carried — this record is meant
|
|
24
24
|
* to be loggable), the rule that classified it, how strong that claim is, and the minting module. */
|
|
25
25
|
export interface SecretEnvFinding {
|
package/dist/core/secret-env.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { deliverEngineNotice } from "./engine-notice.js";
|
|
2
2
|
const TOKEN_COUNT_WORDS = ["MAX", "OUTPUT", "INPUT", "THINKING", "NUM", "TOTAL"];
|
|
3
|
-
const
|
|
3
|
+
const CREDENTIAL_WORD = `KEY|KEYS|TOKEN|(?<!(?:^|_)(?:${TOKEN_COUNT_WORDS.join("|")})_)TOKENS|SECRET|SECRETS|PASSWORD|PASSWORDS|PASSWD|CREDENTIAL|CREDENTIALS|PRIVATEKEY|APIKEY`;
|
|
4
|
+
const SECRET_ENV_RE = new RegExp(`(^|_)(${CREDENTIAL_WORD})(?:_(?:\\d+|ID))?$`, "i");
|
|
5
|
+
const SEMA_NAMESPACE_SECRET_RE = new RegExp(`^SEMA_(?:.*_)?(?:${CREDENTIAL_WORD})(?:_|$)`, "is");
|
|
4
6
|
const SECRET_ENV_EXACT_NAMES = new Set([
|
|
5
7
|
"DATABASE_URL", "CONNECTION_STRING", "DSN", "NPM_AUTH",
|
|
6
8
|
"PGPASSWORD", "MYSQL_PWD", "SSHPASS", "GPG_PASSPHRASE", "GH_PAT", "GITHUB_PAT",
|
|
@@ -16,6 +18,7 @@ const NON_SECRET_ENV_EXACT_NAMES = new Set([
|
|
|
16
18
|
const SECRET_ENV_KIND_CONFIDENCE = {
|
|
17
19
|
"exact-name": "high",
|
|
18
20
|
"suffix-rule": "medium",
|
|
21
|
+
"namespace-rule": "medium",
|
|
19
22
|
};
|
|
20
23
|
export function classifyExactEnvName(key, credentialNames, exemptNames) {
|
|
21
24
|
const upper = key.toUpperCase();
|
|
@@ -31,7 +34,9 @@ function classifySecretEnvKey(key) {
|
|
|
31
34
|
return "exact-name";
|
|
32
35
|
if (byExactName === "exempt")
|
|
33
36
|
return undefined;
|
|
34
|
-
|
|
37
|
+
if (SECRET_ENV_RE.test(key))
|
|
38
|
+
return "suffix-rule";
|
|
39
|
+
return SEMA_NAMESPACE_SECRET_RE.test(key) ? "namespace-rule" : undefined;
|
|
35
40
|
}
|
|
36
41
|
export function isSecretEnvKey(key) {
|
|
37
42
|
return classifySecretEnvKey(key) !== undefined;
|
|
@@ -51,6 +51,7 @@ export declare const EXPECTED_LISTBYSCOPE_SUMMARIES: ({
|
|
|
51
51
|
toolName: string;
|
|
52
52
|
toolInput: string;
|
|
53
53
|
mandated: boolean;
|
|
54
|
+
mandate: string;
|
|
54
55
|
origin: string;
|
|
55
56
|
spentMicroUsd?: undefined;
|
|
56
57
|
} | {
|
|
@@ -68,5 +69,6 @@ export declare const EXPECTED_LISTBYSCOPE_SUMMARIES: ({
|
|
|
68
69
|
toolName?: undefined;
|
|
69
70
|
toolInput?: undefined;
|
|
70
71
|
mandated?: undefined;
|
|
72
|
+
mandate?: undefined;
|
|
71
73
|
origin?: undefined;
|
|
72
74
|
})[];
|