@sema-agent/core 5.21.1 → 5.23.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 +103 -0
- package/dist/agents/send-message-tool.js +6 -3
- package/dist/agents/subagent.d.ts +6 -0
- package/dist/agents/subagent.js +48 -6
- package/dist/brain/errors.d.ts +20 -0
- package/dist/brain/errors.js +40 -0
- package/dist/brain/retry.d.ts +16 -2
- package/dist/brain/retry.js +3 -2
- package/dist/brain/status-sink.d.ts +9 -2
- package/dist/brain/stream-engine.d.ts +22 -0
- package/dist/brain/stream-engine.js +41 -10
- package/dist/core/ask-class.d.ts +48 -0
- package/dist/core/ask-class.js +33 -0
- package/dist/core/checkpoint-store.d.ts +103 -10
- package/dist/core/checkpoint-store.js +3 -1
- package/dist/core/governance-codes.d.ts +38 -0
- package/dist/core/governance-codes.js +14 -0
- package/dist/core/hooks.d.ts +101 -1
- package/dist/core/hooks.js +116 -14
- package/dist/core/locked-config.d.ts +7 -1
- package/dist/core/locked-config.js +2 -1
- package/dist/core/memory-engine/delegation-provenance.d.ts +62 -0
- package/dist/core/memory-engine/delegation-provenance.js +26 -0
- package/dist/core/memory-engine/engine.d.ts +94 -1
- package/dist/core/memory-engine/engine.js +329 -12
- package/dist/core/memory-engine/header-hints.d.ts +30 -0
- package/dist/core/memory-engine/header-hints.js +41 -0
- package/dist/core/memory-engine/index.d.ts +3 -2
- package/dist/core/memory-engine/index.js +3 -2
- package/dist/core/memory-engine/layout.d.ts +232 -0
- package/dist/core/memory-engine/layout.js +469 -1
- package/dist/core/memory-engine/tools.d.ts +30 -0
- package/dist/core/memory-engine/tools.js +108 -17
- package/dist/core/permission-rule-consent.d.ts +25 -9
- package/dist/core/permission-rule-consent.js +93 -21
- package/dist/core/permission-rule-model.d.ts +9 -1
- package/dist/core/permission-rule-model.js +2 -2
- package/dist/core/permission-rule-org.d.ts +195 -0
- package/dist/core/permission-rule-org.js +234 -0
- package/dist/core/permission-rule-store.d.ts +249 -6
- package/dist/core/permission-rule-store.js +314 -3
- package/dist/core/permission-rule-sync.d.ts +139 -0
- package/dist/core/permission-rule-sync.js +343 -0
- package/dist/core/runner/prepare-memory.js +35 -8
- package/dist/core/runner/prepare-task.d.ts +64 -3
- package/dist/core/runner/prepare-task.js +357 -32
- package/dist/core/runner/runtask.js +166 -6
- package/dist/core/shared-memory/contract.js +19 -4
- package/dist/core/shared-memory/normalize.d.ts +3 -1
- package/dist/core/shared-memory/tools.js +73 -17
- package/dist/core/shared-memory/types.d.ts +27 -1
- package/dist/core/store-contracts/permission-rule-sync-contract.d.ts +33 -0
- package/dist/core/store-contracts/permission-rule-sync-contract.js +186 -0
- package/dist/core/task-notification.d.ts +5 -2
- package/dist/core/task-registry-agent.d.ts +1 -1
- package/dist/core/task-registry-agent.js +6 -2
- package/dist/core/task-registry-shared.d.ts +9 -2
- package/dist/core/task-registry.d.ts +9 -3
- package/dist/core/task-registry.js +2 -0
- package/dist/core/tool-policy.d.ts +155 -4
- package/dist/core/tool-policy.js +148 -10
- package/dist/core/tool-result-store.d.ts +9 -1
- package/dist/core/tool-result-store.js +2 -1
- package/dist/core/trace.d.ts +79 -1
- package/dist/core/types.d.ts +94 -3
- package/dist/core/wiring-manifest.d.ts +16 -1
- package/dist/core/wiring-manifest.js +7 -1
- package/dist/index.d.ts +15 -8
- package/dist/index.js +12 -5
- package/dist/orchestration/goal.d.ts +10 -0
- package/dist/orchestration/goal.js +6 -5
- package/dist/stores/file/adoption/adopt.d.ts +146 -0
- package/dist/stores/file/adoption/adopt.js +616 -0
- package/dist/stores/file/adoption/marker.d.ts +194 -0
- package/dist/stores/file/adoption/marker.js +198 -0
- package/dist/stores/file/background-agent-store.js +2 -0
- package/dist/stores/file/checkpoint-store.d.ts +4 -0
- package/dist/stores/file/checkpoint-store.js +3 -0
- package/dist/stores/file/file-snapshot-store.js +2 -0
- package/dist/stores/file/index.d.ts +2 -0
- package/dist/stores/file/index.js +4 -0
- package/dist/stores/file/mailbox-store.js +2 -0
- package/dist/stores/file/memory-store.js +2 -0
- package/dist/stores/file/permission-rule-adopt.d.ts +62 -0
- package/dist/stores/file/permission-rule-adopt.js +95 -0
- package/dist/stores/file/permission-rule-store.d.ts +80 -2
- package/dist/stores/file/permission-rule-store.js +189 -46
- package/dist/stores/file/session-policy-store.js +2 -0
- package/dist/stores/file/session-store.js +2 -0
- package/dist/stores/file/task-list-store.js +2 -0
- package/dist/stores/file/tool-result-store.js +2 -0
- package/dist/stores/file/usage-window-store.js +2 -0
- package/dist/stores/file/workflow-journal-store.js +2 -0
- package/dist/stores/file/workflow-run-store.js +2 -0
- package/dist/tools/fs/bash-readonly-classifier.js +59 -10
- package/package.json +3 -2
|
@@ -20,15 +20,17 @@ import { createSubagentWorktreeHelper, forkGovernanceDenial } from "../../agents
|
|
|
20
20
|
import { createAgentTranscriptTool, AGENT_TRANSCRIPT_TOOL_NAME } from "../../agents/agent-transcript-tool.js";
|
|
21
21
|
import { createSendMessageTool, SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
|
|
22
22
|
import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
|
|
23
|
-
import { askApproverIdentity, combinePolicies, isApprovalSettledBy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, describeThrown, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, tryCloneArgs } from "../tool-policy.js";
|
|
23
|
+
import { askApproverIdentity, checkToolPolicyProjection, combinePolicies, constraintChainDigest, constraintChainEntryOf, isApprovalSettledBy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, describeThrown, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, tryCloneArgs } from "../tool-policy.js";
|
|
24
24
|
const PERSISTED_RULE_TOOL = "Bash";
|
|
25
25
|
import { findAdmittingRule, suggestRulesForCommand } from "../permission-rule-model.js";
|
|
26
26
|
import { ActiveSkillScope, createActiveSkillScopePolicy } from "./active-skill-scope.js";
|
|
27
27
|
import { CHANGED_FILES_MTIME_EPS_MS, fenceMcpServerInstructions, renderAgentListingDelta } from "./turn-attachments.js";
|
|
28
28
|
import { inlineUntrusted } from "../untrusted-text.js";
|
|
29
|
+
import { policyAskClassOf } from "../ask-class.js";
|
|
29
30
|
import { emitTrace } from "../trace.js";
|
|
30
31
|
import { createSessionRulePolicy } from "./session-rule-policy.js";
|
|
31
32
|
import { cloneObserverInput, createHookEnvCapabilities, createPreToolUseConstraintPolicy, formatHookFeedback, runToolGate } from "../hooks.js";
|
|
33
|
+
import { orgRuleVerdictFor } from "../permission-rule-org.js";
|
|
32
34
|
import { reconcileInterruptedSession } from "../session-reconcile.js";
|
|
33
35
|
import { CacheBreakDetector, toolsToFingerprintInputs } from "../cache-break-detector.js";
|
|
34
36
|
import { reservedCollisions, reservedFor } from "../../brain/request-params.js";
|
|
@@ -46,6 +48,7 @@ import { SHARED_MEMORY_TOOL_NAMES } from "../shared-memory/types.js";
|
|
|
46
48
|
import { MEMORY_ENGINE_TOOL_NAMES } from "../memory-engine/tools.js";
|
|
47
49
|
import { MEMORY_RECALL_DISCIPLINE } from "../memory-engine/engine.js";
|
|
48
50
|
import { classifyToolContentOrigin, contentOriginPollutes, delegationCallIsExternal } from "../memory-engine/content-origin.js";
|
|
51
|
+
import { narrowContentSafety, readCardAttestation } from "../memory-engine/delegation-provenance.js";
|
|
49
52
|
import { composeMemoryBlock } from "../memory.js";
|
|
50
53
|
import { preflightLockedConfig } from "../locked-config.js";
|
|
51
54
|
import { COMPLIANCE_CAPABILITIES, WEB_FETCH_TOOL_NAME, complianceCallDenial, resolveComplianceDenies } from "../compliance.js";
|
|
@@ -82,7 +85,7 @@ import { resolveWorkflowSizeGuideline } from "../../orchestration/workflow-size-
|
|
|
82
85
|
import { createLspTool, gitCheckIgnoreFilter, resolveLspPath } from "../lsp.js";
|
|
83
86
|
import { resolveKey } from "../../tools/fs/safety.js";
|
|
84
87
|
import { wholeFileRecordsFromTranscript } from "./session-file-state-replay.js";
|
|
85
|
-
import { BINDING_CHECKPOINT_VERSION, mintCheckpointToken, ORG_ADMISSION_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, buildRiskDescriptor, debitLedger, encodeAtFidelity, remainingBudgetMicroUsd, resolveCheckpointStore, resolveDeclaredFidelity, samePlainValue, } from "../checkpoint-store.js";
|
|
88
|
+
import { BINDING_CHECKPOINT_VERSION, mintCheckpointToken, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, buildRiskDescriptor, debitLedger, encodeAtFidelity, remainingBudgetMicroUsd, resolveCheckpointStore, resolveDeclaredFidelity, samePlainValue, } from "../checkpoint-store.js";
|
|
86
89
|
import { boundInputHashOf } from "../canonical-json.js";
|
|
87
90
|
import { countElicitOptIns, deriveAskEffective, deriveWiringManifest, resolveAskSeamForm, resolveDeclaredDurability, resolveElicitSeam, resolveQuestionSeam } from "../wiring-manifest.js";
|
|
88
91
|
import { GLOBAL_USAGE_KEY, resolveUsageWindows, usageRetryAfterMs } from "../usage-window-store.js";
|
|
@@ -1057,6 +1060,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1057
1060
|
const memoryAdmittedOrgScopesRef = { current: [] };
|
|
1058
1061
|
const ownOrgVerdictRef = { current: undefined };
|
|
1059
1062
|
const orgAdmissionCheckpointState = () => inheritedAdmittedOrgScopes !== undefined || ownOrgVerdictRef.current !== undefined || orgGovernedProvenance;
|
|
1063
|
+
const f012CheckpointState = () => (inheritedParentConstraints?.length ?? 0) > 0 ||
|
|
1064
|
+
seedInheritedGate?.constraintChain !== undefined ||
|
|
1065
|
+
internals?.delegationProvenance !== undefined;
|
|
1060
1066
|
const frozenOnAsk = spec.onAsk ?? deps.onAsk;
|
|
1061
1067
|
const hookEnvSource = (ownedEnv ?? deps.executionEnv) != null ? executionEnv : undefined;
|
|
1062
1068
|
const notifyOwnHookCrash = (err) => {
|
|
@@ -1067,6 +1073,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1067
1073
|
}
|
|
1068
1074
|
};
|
|
1069
1075
|
const frozenOnQuestion = spec.onQuestion ?? deps.onQuestion;
|
|
1076
|
+
const provenanceForChildrenRef = {};
|
|
1077
|
+
const delegationProvenanceForChildren = () => provenanceForChildrenRef.current;
|
|
1070
1078
|
const inheritedGateForChildren = () => {
|
|
1071
1079
|
const ancestorRules = [
|
|
1072
1080
|
...(inheritedAncestorRules ?? []),
|
|
@@ -1075,12 +1083,16 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1075
1083
|
const ownCallerPolicy = lockedPreflight.toolPolicy;
|
|
1076
1084
|
const durableMandate = runtimeCaps?.forceDurableGate === true ||
|
|
1077
1085
|
(spec.durableApproval !== undefined && !isLiveApproverSeat(frozenOnAsk));
|
|
1086
|
+
const contentMandate = runtimeCaps?.forceDurableGate === true ||
|
|
1087
|
+
(spec.durableApproval !== undefined && !isLiveQuestionFace(frozenOnQuestion));
|
|
1078
1088
|
const ownPreToolUse = preToolUseObservational ? undefined : hooks?.preToolUse;
|
|
1079
1089
|
const hookConstraint = ownPreToolUse !== undefined &&
|
|
1080
1090
|
!(inheritedParentConstraints ?? []).some((pc) => pc.preToolUse === ownPreToolUse &&
|
|
1081
1091
|
askApproverIdentity(pc.onAsk) === askApproverIdentity(frozenOnAsk) &&
|
|
1082
1092
|
(pc.durableMandate === true) === durableMandate &&
|
|
1083
|
-
pc.
|
|
1093
|
+
(pc.contentMandate === true) === contentMandate &&
|
|
1094
|
+
pc.hookEnv === hookEnvSource &&
|
|
1095
|
+
pc.autoMode?.decider === autoModeDecider)
|
|
1084
1096
|
? [
|
|
1085
1097
|
{
|
|
1086
1098
|
policy: createPreToolUseConstraintPolicy(ownPreToolUse, hookEnvFace, notifyOwnHookCrash),
|
|
@@ -1088,6 +1100,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1088
1100
|
...(hookEnvSource !== undefined ? { hookEnv: hookEnvSource } : {}),
|
|
1089
1101
|
...(frozenOnAsk !== undefined ? { onAsk: frozenOnAsk } : {}),
|
|
1090
1102
|
...(durableMandate ? { durableMandate: true } : {}),
|
|
1103
|
+
...(contentMandate ? { contentMandate: true } : {}),
|
|
1104
|
+
...(autoModeDecider !== undefined ? { autoMode: { decider: autoModeDecider } } : {}),
|
|
1091
1105
|
},
|
|
1092
1106
|
]
|
|
1093
1107
|
: [];
|
|
@@ -1100,6 +1114,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1100
1114
|
policy: ownCallerPolicy,
|
|
1101
1115
|
...(frozenOnAsk !== undefined ? { onAsk: frozenOnAsk } : {}),
|
|
1102
1116
|
...(durableMandate ? { durableMandate: true } : {}),
|
|
1117
|
+
...(contentMandate ? { contentMandate: true } : {}),
|
|
1118
|
+
...(autoModeDecider !== undefined ? { autoMode: { decider: autoModeDecider } } : {}),
|
|
1103
1119
|
},
|
|
1104
1120
|
]
|
|
1105
1121
|
: []),
|
|
@@ -1140,6 +1156,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1140
1156
|
...(centerAdoption?.sourceRevision !== undefined ? { centerSourceRevision: centerAdoption.sourceRevision } : {}),
|
|
1141
1157
|
activeSkillScope: () => skillScope.active(),
|
|
1142
1158
|
inheritedGateForChildren,
|
|
1159
|
+
delegationProvenanceForChildren,
|
|
1143
1160
|
...(autoModeDecider ? { autoModeReview: { decider: autoModeDecider } } : {}),
|
|
1144
1161
|
...(spec.durableApproval !== undefined ? { durableApprovalForChildren: { ...spec.durableApproval } } : {}),
|
|
1145
1162
|
...(spec.checkpointStore === null ? { checkpointStoreDisabledForChildren: true } : {}),
|
|
@@ -1939,6 +1956,19 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1939
1956
|
if (internals?.ownOrgAdmissionRef !== undefined && ownOrgVerdictRef.current !== undefined) {
|
|
1940
1957
|
internals.ownOrgAdmissionRef.current = ownOrgVerdictRef.current;
|
|
1941
1958
|
}
|
|
1959
|
+
if (internals?.delegationProvenance !== undefined && resume !== undefined) {
|
|
1960
|
+
const live = internals.delegationProvenance.ref.current;
|
|
1961
|
+
const seed = resume.seed.delegationProvenance;
|
|
1962
|
+
if (seed === undefined || seed.version !== 1 || seed.coverageStartTurn !== 0) {
|
|
1963
|
+
live.incomplete = true;
|
|
1964
|
+
}
|
|
1965
|
+
else {
|
|
1966
|
+
if (seed.sawExternal)
|
|
1967
|
+
live.sawExternal = true;
|
|
1968
|
+
if (seed.incomplete)
|
|
1969
|
+
live.incomplete = true;
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1942
1972
|
let memoryBlock = memoryBlockFromEngine;
|
|
1943
1973
|
if (memorySeedFiles?.length && seedContextFiles) {
|
|
1944
1974
|
try {
|
|
@@ -2312,15 +2342,28 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2312
2342
|
else {
|
|
2313
2343
|
deferred = classifyDeferredOverFace(tools, []);
|
|
2314
2344
|
}
|
|
2315
|
-
|
|
2316
|
-
|
|
2345
|
+
const delegationProvenanceChannel = internals?.delegationProvenance;
|
|
2346
|
+
if (memoryEngineSession !== undefined || delegationProvenanceChannel !== undefined) {
|
|
2347
|
+
const pollution = memoryEngineSession?.pollution;
|
|
2348
|
+
const effectiveSafety = narrowContentSafety(delegationProvenanceChannel?.contentSafety, memoryEngineSession?.contentSafety);
|
|
2349
|
+
const trustedTools = effectiveSafety.trustedTools;
|
|
2350
|
+
const execIsExternalContent = effectiveSafety.execIsExternalContent;
|
|
2351
|
+
provenanceForChildrenRef.current = { trustedTools: [...trustedTools], execIsExternalContent };
|
|
2352
|
+
const recordExternal = () => {
|
|
2353
|
+
if (delegationProvenanceChannel !== undefined)
|
|
2354
|
+
delegationProvenanceChannel.ref.current.sawExternal = true;
|
|
2355
|
+
};
|
|
2356
|
+
const recordIncomplete = () => {
|
|
2357
|
+
if (delegationProvenanceChannel !== undefined)
|
|
2358
|
+
delegationProvenanceChannel.ref.current.incomplete = true;
|
|
2359
|
+
};
|
|
2317
2360
|
const callerToolNames = new Set((spec.tools ?? []).map((t) => t.name));
|
|
2318
2361
|
const contentOriginWrapped = new WeakSet();
|
|
2319
2362
|
const classOf = (t) => classifyToolContentOrigin({
|
|
2320
2363
|
declared: t.contentOrigin,
|
|
2321
2364
|
isProtocolTool: protocolOf(t.name) !== undefined,
|
|
2322
2365
|
isCallerTool: callerToolNames.has(t.name),
|
|
2323
|
-
trusted:
|
|
2366
|
+
trusted: trustedTools.has(t.name),
|
|
2324
2367
|
});
|
|
2325
2368
|
const delegationByName = new Map();
|
|
2326
2369
|
for (const t of spec.tools ?? []) {
|
|
@@ -2333,8 +2376,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2333
2376
|
declared: t.contentOrigin,
|
|
2334
2377
|
isProtocolTool: protocolOf(t.name) !== undefined,
|
|
2335
2378
|
isCallerTool: true,
|
|
2336
|
-
trusted:
|
|
2337
|
-
}),
|
|
2379
|
+
trusted: trustedTools.has(t.name),
|
|
2380
|
+
}), execIsExternalContent);
|
|
2338
2381
|
contentOriginWrapRef.current = () => {
|
|
2339
2382
|
for (let i = 0; i < tools.length; i++) {
|
|
2340
2383
|
const t = tools[i];
|
|
@@ -2343,7 +2386,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2343
2386
|
const origin = classOf(t);
|
|
2344
2387
|
const delegation = delegationByName.get(t.name);
|
|
2345
2388
|
const isDelegation = delegation !== undefined;
|
|
2346
|
-
const pollutes = contentOriginPollutes(origin,
|
|
2389
|
+
const pollutes = contentOriginPollutes(origin, execIsExternalContent);
|
|
2347
2390
|
if (!pollutes && !isDelegation) {
|
|
2348
2391
|
contentOriginWrapped.add(t);
|
|
2349
2392
|
continue;
|
|
@@ -2351,7 +2394,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2351
2394
|
const inner = t.execute.bind(t);
|
|
2352
2395
|
const mark = (reason) => {
|
|
2353
2396
|
try {
|
|
2354
|
-
pollution
|
|
2397
|
+
pollution?.markPolluted(reason);
|
|
2355
2398
|
}
|
|
2356
2399
|
catch {
|
|
2357
2400
|
}
|
|
@@ -2359,8 +2402,10 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2359
2402
|
const wrapped = {
|
|
2360
2403
|
...t,
|
|
2361
2404
|
execute: async (toolCallId, params, signal, onUpdate) => {
|
|
2362
|
-
if (pollutes)
|
|
2405
|
+
if (pollutes) {
|
|
2363
2406
|
mark(`tool "${t.name}" (${origin} content class) was invoked in this session`);
|
|
2407
|
+
recordExternal();
|
|
2408
|
+
}
|
|
2364
2409
|
if (!isDelegation)
|
|
2365
2410
|
return inner(toolCallId, params, signal, onUpdate);
|
|
2366
2411
|
const requested = params?.subagent_type;
|
|
@@ -2371,9 +2416,18 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2371
2416
|
isPolluting: poolToolPollutes,
|
|
2372
2417
|
});
|
|
2373
2418
|
const result = await inner(toolCallId, params, signal, onUpdate);
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2419
|
+
const att = readCardAttestation(result?.details);
|
|
2420
|
+
if (att === "external") {
|
|
2421
|
+
mark(`tool "${t.name}" returned the output of a delegated agent that invoked an external-content-class tool at runtime` +
|
|
2422
|
+
(typeof requested === "string" ? ` (subagent_type "${requested}")` : ""));
|
|
2423
|
+
recordExternal();
|
|
2424
|
+
}
|
|
2425
|
+
else if (att !== "clean") {
|
|
2426
|
+
if (external) {
|
|
2427
|
+
mark(`tool "${t.name}" returned the output of a delegated agent whose tool face can reach external content` +
|
|
2428
|
+
(typeof requested === "string" ? ` (subagent_type "${requested}")` : " (no subagent_type named — classified fail-closed)"));
|
|
2429
|
+
}
|
|
2430
|
+
recordIncomplete();
|
|
2377
2431
|
}
|
|
2378
2432
|
return result;
|
|
2379
2433
|
},
|
|
@@ -2941,11 +2995,16 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2941
2995
|
toolEffects,
|
|
2942
2996
|
})
|
|
2943
2997
|
: undefined;
|
|
2944
|
-
const
|
|
2998
|
+
const frozenConstraintLayers = (seedInheritedGate?.constraintChain ?? [])
|
|
2999
|
+
.filter((e) => !("opaque" in e))
|
|
3000
|
+
.map((entry) => ({
|
|
3001
|
+
check: (creq) => checkToolPolicyProjection(entry, creq) ?? { action: "allow" },
|
|
3002
|
+
}));
|
|
3003
|
+
const narrowingLayers = [sessionRulePolicy, ...ancestorRulePolicies, ...frozenConstraintLayers, skillScopePolicy].filter((p) => p !== undefined);
|
|
2945
3004
|
for (const layer of narrowingLayers)
|
|
2946
3005
|
auditPolicyNames(layer);
|
|
2947
3006
|
const denyNarrowingPolicy = narrowingLayers.length === 0 ? undefined : narrowingLayers.length === 1 ? narrowingLayers[0] : combinePolicies(...narrowingLayers);
|
|
2948
|
-
const basePolicyForResumeEdit = lockedPreflight.toolPolicy;
|
|
3007
|
+
const basePolicyForResumeEdit = lockedPreflight.basePolicyForResumeEdit ?? lockedPreflight.toolPolicy;
|
|
2949
3008
|
const hooks = spec.hooks ?? deps.hooks;
|
|
2950
3009
|
const preToolUseObservational = hooks?.preToolUse !== undefined && hooks.preToolUseObservational === true;
|
|
2951
3010
|
const ownGatePreToolUse = !preToolUseObservational
|
|
@@ -2997,6 +3056,62 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2997
3056
|
return;
|
|
2998
3057
|
inheritedAskGrants.set(toolCallId, { approver, argsJson, presented, waitMs });
|
|
2999
3058
|
};
|
|
3059
|
+
const sandboxDeclared = isIsolated(executionEnv);
|
|
3060
|
+
const sandboxAdmissionArmed = (() => {
|
|
3061
|
+
if (!sandboxDeclared)
|
|
3062
|
+
return false;
|
|
3063
|
+
const store = resolveCheckpointStore(spec, deps);
|
|
3064
|
+
const missing = [];
|
|
3065
|
+
if (store === undefined)
|
|
3066
|
+
missing.push("a wired CheckpointStore");
|
|
3067
|
+
else if (store.redecision?.reopen !== true)
|
|
3068
|
+
missing.push("a CheckpointStore that DECLARES redecision.reopen (declaration, never duck-typing)");
|
|
3069
|
+
if (spec.durableApproval === undefined && runtimeCaps?.forceDurableGate !== true)
|
|
3070
|
+
missing.push("a durable park (spec.durableApproval, or the forceDurableGate entitlement)");
|
|
3071
|
+
if (missing.length === 0)
|
|
3072
|
+
return true;
|
|
3073
|
+
try {
|
|
3074
|
+
const e = new Error(`sandbox admission mode NOT armed: the execution env declares an isolated sandbox (capabilities.isolation), but the mode's ` +
|
|
3075
|
+
`park prerequisites are missing — ${missing.join("; ")}. Asks keep the conservative default chain (no auto-admission, no boundary park).`);
|
|
3076
|
+
e.code = "config.sandbox_admission_unavailable";
|
|
3077
|
+
deps.onError?.(e, { phase: "config", sessionId, classification: "sandbox-admission-unavailable" });
|
|
3078
|
+
}
|
|
3079
|
+
catch {
|
|
3080
|
+
}
|
|
3081
|
+
return false;
|
|
3082
|
+
})();
|
|
3083
|
+
const foldAskClasses = new Map();
|
|
3084
|
+
const FOLD_ASK_CLASSES_CAP = 256;
|
|
3085
|
+
const recordFoldAskClass = (toolCallId, cls, layer) => {
|
|
3086
|
+
if (!foldAskClasses.has(toolCallId) && foldAskClasses.size >= FOLD_ASK_CLASSES_CAP)
|
|
3087
|
+
return;
|
|
3088
|
+
const list = foldAskClasses.get(toolCallId) ?? [];
|
|
3089
|
+
list.push({ cls, layer });
|
|
3090
|
+
foldAskClasses.set(toolCallId, list);
|
|
3091
|
+
};
|
|
3092
|
+
const sandboxBoundaryCapable = (toolName) => egressTools.has(toolName) || toolName.includes("__") || ownToolNames.has(toolName);
|
|
3093
|
+
const emitSandboxAdmitted = (info) => {
|
|
3094
|
+
emitTrace(deps.tracer, () => ({
|
|
3095
|
+
kind: "permission.sandbox_admitted",
|
|
3096
|
+
version: 1,
|
|
3097
|
+
taskId: spec.taskId ?? sessionId,
|
|
3098
|
+
toolCallId: info.toolCallId,
|
|
3099
|
+
toolName: info.toolName,
|
|
3100
|
+
askClasses: info.askClasses.map((a) => a.cls),
|
|
3101
|
+
sourceLayers: info.askClasses.map((a) => a.layer),
|
|
3102
|
+
boundary: info.boundary,
|
|
3103
|
+
ts: Date.now(),
|
|
3104
|
+
}));
|
|
3105
|
+
};
|
|
3106
|
+
const ancestorSandboxAdmissions = new Map();
|
|
3107
|
+
const recordAncestorSandboxAdmission = (toolCallId, toolName) => {
|
|
3108
|
+
const list = ancestorSandboxAdmissions.get(toolCallId);
|
|
3109
|
+
if (list !== undefined)
|
|
3110
|
+
list.push({ cls: "sandbox_local", layer: "ancestor_constraint" });
|
|
3111
|
+
else
|
|
3112
|
+
ancestorSandboxAdmissions.set(toolCallId, [{ cls: "sandbox_local", layer: "ancestor_constraint" }]);
|
|
3113
|
+
void toolName;
|
|
3114
|
+
};
|
|
3000
3115
|
const callerPolicyLayer = policy !== undefined && sameInstanceAncestorCount > 0
|
|
3001
3116
|
? {
|
|
3002
3117
|
check: async (creq, csignal) => {
|
|
@@ -3030,12 +3145,22 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3030
3145
|
};
|
|
3031
3146
|
const permissionRuleLane = (() => {
|
|
3032
3147
|
const provider = deps.permissionRuleStore;
|
|
3148
|
+
const localOwnerDeclared = deps.localOwnerRules === true;
|
|
3149
|
+
if (localOwnerDeclared) {
|
|
3150
|
+
if (provider === undefined) {
|
|
3151
|
+
throw new Error("RunnerDeps.localOwnerRules is declared but no permissionRuleStore provider is wired — there is no bucket for the local owner to hold rules in; refusing rather than running as if the declaration were absent");
|
|
3152
|
+
}
|
|
3153
|
+
if (provider.forLocalOwner === undefined) {
|
|
3154
|
+
throw new Error("RunnerDeps.localOwnerRules is declared but the wired permissionRuleStore provider implements no forLocalOwner() face — a provider without a local-owner bucket cannot honor the declaration; refusing rather than silently resolving zero rules");
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3033
3157
|
if (provider === undefined)
|
|
3034
3158
|
return undefined;
|
|
3035
3159
|
const root = taskRootPath;
|
|
3036
3160
|
return {
|
|
3037
3161
|
admits: async (req) => {
|
|
3038
|
-
|
|
3162
|
+
const anonymous = spec.principal === undefined || spec.principal === "";
|
|
3163
|
+
if (anonymous && !localOwnerDeclared)
|
|
3039
3164
|
return undefined;
|
|
3040
3165
|
if (req.toolName !== PERSISTED_RULE_TOOL)
|
|
3041
3166
|
return undefined;
|
|
@@ -3044,7 +3169,10 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3044
3169
|
return undefined;
|
|
3045
3170
|
let listed;
|
|
3046
3171
|
try {
|
|
3047
|
-
listed =
|
|
3172
|
+
listed = anonymous
|
|
3173
|
+
?
|
|
3174
|
+
await provider.forLocalOwner().list()
|
|
3175
|
+
: await provider.forPrincipal(spec.principal).list();
|
|
3048
3176
|
}
|
|
3049
3177
|
catch (err) {
|
|
3050
3178
|
emitTrace(deps.tracer, () => ({
|
|
@@ -3060,6 +3188,31 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3060
3188
|
},
|
|
3061
3189
|
};
|
|
3062
3190
|
})();
|
|
3191
|
+
const permissionRuleOrgLane = (() => {
|
|
3192
|
+
const overlay = deps.permissionRuleOrg;
|
|
3193
|
+
if (overlay === undefined)
|
|
3194
|
+
return undefined;
|
|
3195
|
+
return {
|
|
3196
|
+
adjudicate: async (req) => {
|
|
3197
|
+
if (req.toolName === ASK_USER_QUESTION_TOOL_NAME && questionToolMounted)
|
|
3198
|
+
return { status: "available" };
|
|
3199
|
+
let resolution;
|
|
3200
|
+
try {
|
|
3201
|
+
resolution = await overlay.resolve();
|
|
3202
|
+
}
|
|
3203
|
+
catch (err) {
|
|
3204
|
+
return { status: "unavailable", disclosures: [`the org rule overlay threw: ${err instanceof Error ? err.message : String(err)}`] };
|
|
3205
|
+
}
|
|
3206
|
+
if (resolution.status === "unavailable")
|
|
3207
|
+
return { status: "unavailable", disclosures: resolution.disclosures };
|
|
3208
|
+
const command = req.args?.command;
|
|
3209
|
+
if (req.toolName !== PERSISTED_RULE_TOOL || typeof command !== "string")
|
|
3210
|
+
return { status: "available" };
|
|
3211
|
+
const verdict = orgRuleVerdictFor(resolution.rules, { tool: req.toolName, command });
|
|
3212
|
+
return verdict === undefined ? { status: "available" } : { status: "available", verdict };
|
|
3213
|
+
},
|
|
3214
|
+
};
|
|
3215
|
+
})();
|
|
3063
3216
|
const ruleSuggestionsOf = (toolName, args) => {
|
|
3064
3217
|
if (permissionRuleLane === undefined || toolName !== PERSISTED_RULE_TOOL)
|
|
3065
3218
|
return {};
|
|
@@ -3069,7 +3222,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3069
3222
|
const suggestions = suggestRulesForCommand(command);
|
|
3070
3223
|
return suggestions.length > 0 ? { ruleSuggestions: suggestions } : {};
|
|
3071
3224
|
};
|
|
3072
|
-
const recheckApprovedEdit = async (pol, onAskOf, creq, edit, csignal) => {
|
|
3225
|
+
const recheckApprovedEdit = async (pol, onAskOf, creq, edit, csignal, ancestorDecider) => {
|
|
3073
3226
|
let editArgs = edit;
|
|
3074
3227
|
for (let round = 0;; round++) {
|
|
3075
3228
|
if (round >= 3) {
|
|
@@ -3095,6 +3248,22 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3095
3248
|
}
|
|
3096
3249
|
if (re.updatedInput !== undefined)
|
|
3097
3250
|
editArgs = re.updatedInput;
|
|
3251
|
+
if (ancestorDecider !== undefined) {
|
|
3252
|
+
const verdict = await ancestorDecider
|
|
3253
|
+
.decide({ req: { ...creq, args: editArgs }, ...(re.message !== undefined ? { askMessage: re.message } : {}) }, csignal ?? abortController.signal)
|
|
3254
|
+
.catch(() => ({ kind: "unavailable", cause: "error" }));
|
|
3255
|
+
if (verdict.kind === "allow")
|
|
3256
|
+
return { action: "allow", updatedInput: editArgs };
|
|
3257
|
+
if (verdict.kind === "block") {
|
|
3258
|
+
const reason = verdict.reason ? inlineUntrusted(verdict.reason) : "";
|
|
3259
|
+
const category = verdict.category ? inlineUntrusted(verdict.category) : "";
|
|
3260
|
+
return {
|
|
3261
|
+
action: "deny",
|
|
3262
|
+
message: `auto-mode classifier blocked the approved edit at an inherited ancestor layer${reason ? `: ${reason}` : category ? `: [${category}]` : ""}`,
|
|
3263
|
+
decisionReason: "classifier",
|
|
3264
|
+
};
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3098
3267
|
const rr = await resolveAsk({
|
|
3099
3268
|
toolName: creq.toolName,
|
|
3100
3269
|
toolCallId: creq.toolCallId,
|
|
@@ -3116,8 +3285,10 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3116
3285
|
pc.preToolUse === hooks?.preToolUse &&
|
|
3117
3286
|
!preToolUseObservational &&
|
|
3118
3287
|
pc.durableMandate !== true &&
|
|
3288
|
+
pc.contentMandate !== true &&
|
|
3119
3289
|
askApproverIdentity(pc.onAsk) === askApproverIdentity(frozenOnAsk) &&
|
|
3120
|
-
pc.hookEnv === hookEnvSource
|
|
3290
|
+
pc.hookEnv === hookEnvSource &&
|
|
3291
|
+
pc.autoMode?.decider === autoModeDecider
|
|
3121
3292
|
? { check: () => ({ action: "allow" }) }
|
|
3122
3293
|
: policy !== undefined && pc.policy === policy
|
|
3123
3294
|
? {
|
|
@@ -3134,8 +3305,14 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3134
3305
|
return first;
|
|
3135
3306
|
if (first.action === "allow")
|
|
3136
3307
|
return { action: "allow" };
|
|
3137
|
-
if (creq.toolName === ASK_USER_QUESTION_TOOL_NAME
|
|
3138
|
-
|
|
3308
|
+
if (creq.toolName === ASK_USER_QUESTION_TOOL_NAME) {
|
|
3309
|
+
if (pc.contentMandate !== true || markInheritedUnavailable(creq.toolCallId))
|
|
3310
|
+
return first;
|
|
3311
|
+
return {
|
|
3312
|
+
action: "deny",
|
|
3313
|
+
message: `inherited parent policy requires durable handling of the question raised for "${creq.toolName}" and the marker channel is at capacity — ` +
|
|
3314
|
+
`denied fail-closed (tighten-only)`,
|
|
3315
|
+
};
|
|
3139
3316
|
}
|
|
3140
3317
|
if (pc.durableMandate === true) {
|
|
3141
3318
|
if (resolveCheckpointStore(spec, deps) !== undefined &&
|
|
@@ -3149,6 +3326,26 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3149
3326
|
`ask cannot be reconstructed in a delegated child; denied fail-closed (tighten-only)`,
|
|
3150
3327
|
};
|
|
3151
3328
|
}
|
|
3329
|
+
if (pc.autoMode !== undefined) {
|
|
3330
|
+
const verdict = await pc.autoMode.decider
|
|
3331
|
+
.decide({ req: creq, ...(first.message !== undefined ? { askMessage: first.message } : {}) }, csignal ?? abortController.signal)
|
|
3332
|
+
.catch(() => ({ kind: "unavailable", cause: "error" }));
|
|
3333
|
+
if (verdict.kind === "allow")
|
|
3334
|
+
return { action: "allow" };
|
|
3335
|
+
if (verdict.kind === "block") {
|
|
3336
|
+
const reason = verdict.reason ? inlineUntrusted(verdict.reason) : "";
|
|
3337
|
+
const category = verdict.category ? inlineUntrusted(verdict.category) : "";
|
|
3338
|
+
return {
|
|
3339
|
+
action: "deny",
|
|
3340
|
+
message: `auto-mode classifier blocked this call at an inherited ancestor layer${reason ? `: ${reason}` : category ? `: [${category}]` : ""}`,
|
|
3341
|
+
decisionReason: "classifier",
|
|
3342
|
+
};
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
if (sandboxAdmissionArmed && policyAskClassOf(pc.policy) === "sandbox_local" && !sandboxBoundaryCapable(creq.toolName)) {
|
|
3346
|
+
recordAncestorSandboxAdmission(creq.toolCallId, creq.toolName);
|
|
3347
|
+
return { action: "allow" };
|
|
3348
|
+
}
|
|
3152
3349
|
const presentedArgs = creq.args;
|
|
3153
3350
|
const askT0 = now();
|
|
3154
3351
|
const resolved = await resolveAsk({
|
|
@@ -3173,7 +3370,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3173
3370
|
if (resolved.action !== "allow")
|
|
3174
3371
|
return resolved;
|
|
3175
3372
|
if (resolved.updatedInput !== undefined) {
|
|
3176
|
-
return recheckApprovedEdit(pc.policy, pc.onAsk, creq, resolved.updatedInput, csignal);
|
|
3373
|
+
return recheckApprovedEdit(pc.policy, pc.onAsk, creq, resolved.updatedInput, csignal, pc.autoMode?.decider);
|
|
3177
3374
|
}
|
|
3178
3375
|
recordInheritedAskGrant(creq.toolCallId, pc.onAsk, resolved.presentedInput, askWaitMs);
|
|
3179
3376
|
return { action: "allow" };
|
|
@@ -3193,8 +3390,14 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3193
3390
|
}
|
|
3194
3391
|
if (decision.action !== "ask")
|
|
3195
3392
|
return decision;
|
|
3196
|
-
if (creq.toolName === ASK_USER_QUESTION_TOOL_NAME
|
|
3197
|
-
|
|
3393
|
+
if (creq.toolName === ASK_USER_QUESTION_TOOL_NAME) {
|
|
3394
|
+
if (pc.contentMandate !== true || markInheritedUnavailable(creq.toolCallId))
|
|
3395
|
+
return decision;
|
|
3396
|
+
return {
|
|
3397
|
+
action: "deny",
|
|
3398
|
+
message: `inherited parent policy requires durable handling of the question raised for "${creq.toolName}" and the marker channel is at capacity — ` +
|
|
3399
|
+
`denied fail-closed (tighten-only)`,
|
|
3400
|
+
};
|
|
3198
3401
|
}
|
|
3199
3402
|
if (pc.durableMandate === true) {
|
|
3200
3403
|
if (resolveCheckpointStore(spec, deps) !== undefined &&
|
|
@@ -3209,6 +3412,27 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3209
3412
|
};
|
|
3210
3413
|
}
|
|
3211
3414
|
const presentedArgs = decision.updatedInput !== undefined ? decision.updatedInput : creq.args;
|
|
3415
|
+
if (pc.autoMode !== undefined) {
|
|
3416
|
+
const verdict = await pc.autoMode.decider
|
|
3417
|
+
.decide({ req: { ...creq, args: presentedArgs }, ...(decision.message !== undefined ? { askMessage: decision.message } : {}) }, csignal ?? abortController.signal)
|
|
3418
|
+
.catch(() => ({ kind: "unavailable", cause: "error" }));
|
|
3419
|
+
if (verdict.kind === "allow") {
|
|
3420
|
+
return decision.updatedInput !== undefined ? { action: "allow", updatedInput: decision.updatedInput } : { action: "allow" };
|
|
3421
|
+
}
|
|
3422
|
+
if (verdict.kind === "block") {
|
|
3423
|
+
const reason = verdict.reason ? inlineUntrusted(verdict.reason) : "";
|
|
3424
|
+
const category = verdict.category ? inlineUntrusted(verdict.category) : "";
|
|
3425
|
+
return {
|
|
3426
|
+
action: "deny",
|
|
3427
|
+
message: `auto-mode classifier blocked this call at an inherited ancestor layer${reason ? `: ${reason}` : category ? `: [${category}]` : ""}`,
|
|
3428
|
+
decisionReason: "classifier",
|
|
3429
|
+
};
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
if (sandboxAdmissionArmed && policyAskClassOf(pc.policy) === "sandbox_local" && !sandboxBoundaryCapable(creq.toolName)) {
|
|
3433
|
+
recordAncestorSandboxAdmission(creq.toolCallId, creq.toolName);
|
|
3434
|
+
return decision.updatedInput !== undefined ? { action: "allow", updatedInput: decision.updatedInput } : { action: "allow" };
|
|
3435
|
+
}
|
|
3212
3436
|
const askT0 = now();
|
|
3213
3437
|
const resolved = await resolveAsk({
|
|
3214
3438
|
toolName: creq.toolName,
|
|
@@ -3232,7 +3456,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3232
3456
|
if (resolved.action !== "allow")
|
|
3233
3457
|
return resolved;
|
|
3234
3458
|
if (resolved.updatedInput !== undefined) {
|
|
3235
|
-
return recheckApprovedEdit(pc.policy, pc.onAsk, creq, resolved.updatedInput, csignal);
|
|
3459
|
+
return recheckApprovedEdit(pc.policy, pc.onAsk, creq, resolved.updatedInput, csignal, pc.autoMode?.decider);
|
|
3236
3460
|
}
|
|
3237
3461
|
if (pc.preToolUse === undefined) {
|
|
3238
3462
|
recordInheritedAskGrant(creq.toolCallId, pc.onAsk, resolved.presentedInput, askWaitMs);
|
|
@@ -3257,6 +3481,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3257
3481
|
const d = await layer.check(creq, csignal);
|
|
3258
3482
|
if (d.updatedInput !== undefined)
|
|
3259
3483
|
rewriteEmitterIndex.set(creq.toolCallId, idx);
|
|
3484
|
+
if (d.action === "ask") {
|
|
3485
|
+
recordFoldAskClass(creq.toolCallId, idx < callerSlotOffset ? policyAskClassOf(policy) : "mandate", idx < callerSlotOffset ? "caller_policy" : "ancestor_constraint");
|
|
3486
|
+
}
|
|
3260
3487
|
return d;
|
|
3261
3488
|
},
|
|
3262
3489
|
}));
|
|
@@ -3433,6 +3660,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3433
3660
|
sessionDurability: resolveDeclaredDurability(sessions, "sessionStore"),
|
|
3434
3661
|
backgroundAgentStoreWired: deps.backgroundAgentStore !== undefined,
|
|
3435
3662
|
permissionRuleStoreWired: deps.permissionRuleStore !== undefined,
|
|
3663
|
+
permissionRuleSyncWired: deps.permissionRuleSyncWired === true,
|
|
3664
|
+
permissionRuleOrgGoverned: deps.permissionRuleOrg !== undefined,
|
|
3436
3665
|
hostChildEventSinkWired: deps.onBackgroundChildEvent !== undefined,
|
|
3437
3666
|
lockedConfigWired: deps.lockedConfig !== undefined,
|
|
3438
3667
|
complianceWired: deps.compliancePostureResolver !== undefined,
|
|
@@ -3607,6 +3836,12 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3607
3836
|
inheritedGate: (() => {
|
|
3608
3837
|
const requiresParentConstraint = (inheritedParentConstraints?.length ?? 0) > 0 || seedInheritedGate?.requiresParentConstraint === true;
|
|
3609
3838
|
const parentConstraintCount = (inheritedParentConstraints?.length ?? 0) > 0 ? inheritedParentConstraints.length : seedInheritedGate?.parentConstraintCount;
|
|
3839
|
+
const constraintChain = (inheritedParentConstraints?.length ?? 0) > 0
|
|
3840
|
+
? inheritedParentConstraints.map((pc) => constraintChainEntryOf(pc.policy, { ...(pc.autoMode !== undefined ? { autoModeArmed: true } : {}), ...(pc.durableMandate === true ? { durableMandate: true } : {}), ...(pc.contentMandate === true ? { contentMandate: true } : {}) }))
|
|
3841
|
+
: seedInheritedGate?.constraintChain;
|
|
3842
|
+
const constraintDigest = (inheritedParentConstraints?.length ?? 0) > 0
|
|
3843
|
+
? constraintChainDigest(constraintChain)
|
|
3844
|
+
: seedInheritedGate?.constraintDigest;
|
|
3610
3845
|
return inheritedAncestorRules !== undefined ||
|
|
3611
3846
|
inheritedShellGate !== undefined ||
|
|
3612
3847
|
inheritedAdmittedOrgScopes !== undefined ||
|
|
@@ -3623,6 +3858,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3623
3858
|
...(orgGovernedProvenance ? { orgAdmissionGoverned: true } : {}),
|
|
3624
3859
|
requiresParentConstraint,
|
|
3625
3860
|
...(requiresParentConstraint && parentConstraintCount !== undefined ? { parentConstraintCount } : {}),
|
|
3861
|
+
...(requiresParentConstraint && constraintChain !== undefined
|
|
3862
|
+
? { constraintChain: structuredClone(constraintChain), ...(constraintDigest !== undefined ? { constraintDigest } : {}) }
|
|
3863
|
+
: {}),
|
|
3626
3864
|
}
|
|
3627
3865
|
: undefined;
|
|
3628
3866
|
})(),
|
|
@@ -3633,14 +3871,14 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3633
3871
|
...(announcedListingsRef.models !== undefined ? { models: [...announcedListingsRef.models] } : {}),
|
|
3634
3872
|
}
|
|
3635
3873
|
: undefined,
|
|
3874
|
+
delegationProvenance: internals?.delegationProvenance !== undefined ? { ...internals.delegationProvenance.ref.current } : undefined,
|
|
3636
3875
|
});
|
|
3637
3876
|
const commitSuspendSaga = async (token, cp, remoteEnv, remoteHandle) => {
|
|
3638
3877
|
if (!checkpointStore)
|
|
3639
3878
|
return { ok: false };
|
|
3640
3879
|
if (memoryEngineSession)
|
|
3641
3880
|
await memoryEngineSession.harvest("checkpoint");
|
|
3642
|
-
|
|
3643
|
-
await checkpointStore.put(token, cp);
|
|
3881
|
+
const announceCommittedScreeningPark = () => {
|
|
3644
3882
|
const committedCount = cp.state.inheritedGate?.parentConstraintCount;
|
|
3645
3883
|
if (cp.state.inheritedGate?.requiresParentConstraint === true &&
|
|
3646
3884
|
!screeningParkDisclosedRef.done &&
|
|
@@ -3657,10 +3895,59 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3657
3895
|
catch {
|
|
3658
3896
|
}
|
|
3659
3897
|
}
|
|
3898
|
+
};
|
|
3899
|
+
const confirmPutOutcome = async (putErr) => {
|
|
3900
|
+
if (putErr?.code === "checkpoint.already_exists")
|
|
3901
|
+
return "absent";
|
|
3902
|
+
try {
|
|
3903
|
+
const row = await checkpointStore.get(token);
|
|
3904
|
+
if (row == null)
|
|
3905
|
+
return "absent";
|
|
3906
|
+
const sameCall = (a, b) => a.kind === b.kind && (a.kind !== "tool_approval" || b.kind !== "tool_approval" || a.toolCallId === b.toolCallId);
|
|
3907
|
+
const isOurs = row.scope === cp.scope && row.sessionId === cp.sessionId && row.leafId === cp.leafId && sameCall(row.pendingAction, cp.pendingAction);
|
|
3908
|
+
return isOurs ? "committed" : "absent";
|
|
3909
|
+
}
|
|
3910
|
+
catch (readErr) {
|
|
3911
|
+
deps.onError?.(readErr, { phase: "config", sessionId });
|
|
3912
|
+
return "unknown";
|
|
3913
|
+
}
|
|
3914
|
+
};
|
|
3915
|
+
try {
|
|
3916
|
+
await checkpointStore.put(token, cp);
|
|
3917
|
+
announceCommittedScreeningPark();
|
|
3660
3918
|
return { ok: true };
|
|
3661
3919
|
}
|
|
3662
3920
|
catch (putErr) {
|
|
3663
3921
|
deps.onError?.(putErr, { phase: "config", sessionId });
|
|
3922
|
+
const confirmed = await confirmPutOutcome(putErr);
|
|
3923
|
+
if (confirmed === "committed") {
|
|
3924
|
+
try {
|
|
3925
|
+
deps.onError?.(new Error(`durable approval checkpoint: the store's write reported a failure but the row EXISTS — the commit ` +
|
|
3926
|
+
`landed and its acknowledgement was lost (a network backend can complete the INSERT and drop the ` +
|
|
3927
|
+
`connection before replying). The run suspends on that row rather than continuing, which would leave ` +
|
|
3928
|
+
`a redeemable pending checkpoint behind. This backend's acknowledgement path is lossy — the operator ` +
|
|
3929
|
+
`face carries the store's own message.`), { phase: "degraded", sessionId, classification: "checkpoint-put-confirmation-lost" });
|
|
3930
|
+
}
|
|
3931
|
+
catch {
|
|
3932
|
+
}
|
|
3933
|
+
announceCommittedScreeningPark();
|
|
3934
|
+
return { ok: true };
|
|
3935
|
+
}
|
|
3936
|
+
if (confirmed === "unknown") {
|
|
3937
|
+
const unknownReason = "the approval checkpoint's state cannot be established (the store rejected the write and then could not be read back; a committed-but-unacknowledged row may exist) — the run is stopped rather than continued past an approval whose durable record is unknown";
|
|
3938
|
+
try {
|
|
3939
|
+
deps.onError?.(new Error(`durable approval checkpoint: ${unknownReason}`), {
|
|
3940
|
+
phase: "degraded",
|
|
3941
|
+
sessionId,
|
|
3942
|
+
classification: "checkpoint-put-outcome-unknown",
|
|
3943
|
+
});
|
|
3944
|
+
}
|
|
3945
|
+
catch {
|
|
3946
|
+
}
|
|
3947
|
+
abortController.abort();
|
|
3948
|
+
void harness.abort();
|
|
3949
|
+
return { ok: false, reason: unknownReason };
|
|
3950
|
+
}
|
|
3664
3951
|
const reason = "the approval checkpoint could not be persisted (the store rejected the write; the deployment's error face carries the store's own message)";
|
|
3665
3952
|
if (remoteEnv !== undefined && remoteHandle?.snapshotId !== undefined) {
|
|
3666
3953
|
const { outcome: back, attempts: backAttempts } = await restoreWorkspaceWithRetry(remoteEnv, remoteHandle.snapshotId, {
|
|
@@ -3752,7 +4039,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3752
4039
|
const mintedAt = Date.now();
|
|
3753
4040
|
const cp = {
|
|
3754
4041
|
token,
|
|
3755
|
-
version: orgAdmissionCheckpointState() ? ORG_ADMISSION_CHECKPOINT_VERSION : resourceLedgerOut.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : RESOURCE_CHECKPOINT_VERSION,
|
|
4042
|
+
version: f012CheckpointState() ? F012_CHECKPOINT_VERSION : orgAdmissionCheckpointState() ? ORG_ADMISSION_CHECKPOINT_VERSION : resourceLedgerOut.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : RESOURCE_CHECKPOINT_VERSION,
|
|
3756
4043
|
scope,
|
|
3757
4044
|
sessionId,
|
|
3758
4045
|
leafId,
|
|
@@ -3834,7 +4121,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3834
4121
|
const mintedAt = Date.now();
|
|
3835
4122
|
const cp = {
|
|
3836
4123
|
token,
|
|
3837
|
-
version: orgAdmissionCheckpointState() ? ORG_ADMISSION_CHECKPOINT_VERSION : reviewLedger.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : BINDING_CHECKPOINT_VERSION,
|
|
4124
|
+
version: f012CheckpointState() ? F012_CHECKPOINT_VERSION : orgAdmissionCheckpointState() ? ORG_ADMISSION_CHECKPOINT_VERSION : reviewLedger.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : BINDING_CHECKPOINT_VERSION,
|
|
3838
4125
|
scope,
|
|
3839
4126
|
sessionId,
|
|
3840
4127
|
leafId,
|
|
@@ -3983,6 +4270,10 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3983
4270
|
}
|
|
3984
4271
|
const parkedArgs = captured.value;
|
|
3985
4272
|
const projectionMovedArgs = backendFidelity !== "structured-clone" && !samePlainValue(parked.value, parkedArgs);
|
|
4273
|
+
if (projectionMovedArgs && basePolicyForResumeEdit === undefined) {
|
|
4274
|
+
throw new ParkRefusal(`the arguments of "${req.toolName}" change shape when stored by this checkpoint backend, and no policy is wired to re-adjudicate the stored shape ` +
|
|
4275
|
+
`(no basePolicyForResumeEdit override and no caller toolPolicy) — refusing to file a row whose executable form nothing adjudicated`);
|
|
4276
|
+
}
|
|
3986
4277
|
if (projectionMovedArgs && basePolicyForResumeEdit !== undefined) {
|
|
3987
4278
|
const presented = encodeAtFidelity(backendFidelity, parkedArgs);
|
|
3988
4279
|
if (!presented.ok) {
|
|
@@ -3992,6 +4283,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3992
4283
|
toolName: req.toolName,
|
|
3993
4284
|
args: presented.value,
|
|
3994
4285
|
toolCallId: req.toolCallId,
|
|
4286
|
+
budget: budgetSnapshot,
|
|
3995
4287
|
...(handsCwdRef !== undefined ? { cwd: handsCwdRef.current } : {}),
|
|
3996
4288
|
}, abortController.signal));
|
|
3997
4289
|
const demanded = reprojected.updatedInput !== undefined ? tryCloneArgs(reprojected.updatedInput) : undefined;
|
|
@@ -4081,7 +4373,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4081
4373
|
const mintedAt = Date.now();
|
|
4082
4374
|
cp = {
|
|
4083
4375
|
token,
|
|
4084
|
-
version: orgAdmissionCheckpointState() ? ORG_ADMISSION_CHECKPOINT_VERSION : approvalLedger.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : BINDING_CHECKPOINT_VERSION,
|
|
4376
|
+
version: f012CheckpointState() ? F012_CHECKPOINT_VERSION : orgAdmissionCheckpointState() ? ORG_ADMISSION_CHECKPOINT_VERSION : approvalLedger.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : BINDING_CHECKPOINT_VERSION,
|
|
4085
4377
|
scope,
|
|
4086
4378
|
sessionId,
|
|
4087
4379
|
leafId,
|
|
@@ -4219,12 +4511,45 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4219
4511
|
},
|
|
4220
4512
|
}
|
|
4221
4513
|
: {}),
|
|
4514
|
+
...(permissionRuleOrgLane
|
|
4515
|
+
? {
|
|
4516
|
+
orgRules: {
|
|
4517
|
+
adjudicate: permissionRuleOrgLane.adjudicate,
|
|
4518
|
+
contentAskToolMounted: questionToolMounted,
|
|
4519
|
+
onUnavailable: (info) => emitTrace(deps.tracer, () => ({
|
|
4520
|
+
kind: "permission.org_snapshot_unavailable",
|
|
4521
|
+
version: 1,
|
|
4522
|
+
taskId: spec.taskId ?? sessionId,
|
|
4523
|
+
toolName: info.toolName,
|
|
4524
|
+
toolCallId: info.toolCallId,
|
|
4525
|
+
message: info.message,
|
|
4526
|
+
ts: Date.now(),
|
|
4527
|
+
})),
|
|
4528
|
+
},
|
|
4529
|
+
}
|
|
4530
|
+
: {}),
|
|
4222
4531
|
isMarkedUnresolvable: (toolCallId) => inheritedUnavailableAsks.has(toolCallId),
|
|
4532
|
+
...(sandboxAdmissionArmed
|
|
4533
|
+
? {
|
|
4534
|
+
sandboxAdmission: {
|
|
4535
|
+
askClassesOf: (toolCallId) => foldAskClasses.get(toolCallId),
|
|
4536
|
+
boundaryCapable: sandboxBoundaryCapable(e.toolName),
|
|
4537
|
+
onAdmitted: emitSandboxAdmitted,
|
|
4538
|
+
},
|
|
4539
|
+
}
|
|
4540
|
+
: {}),
|
|
4223
4541
|
});
|
|
4224
4542
|
}
|
|
4225
4543
|
finally {
|
|
4226
4544
|
inheritedUnavailableAsks.delete(e.toolCallId);
|
|
4227
4545
|
inheritedAskGrants.delete(e.toolCallId);
|
|
4546
|
+
foldAskClasses.delete(e.toolCallId);
|
|
4547
|
+
}
|
|
4548
|
+
const ancestorAdmitted = ancestorSandboxAdmissions.get(e.toolCallId);
|
|
4549
|
+
ancestorSandboxAdmissions.delete(e.toolCallId);
|
|
4550
|
+
if (ancestorAdmitted !== undefined && !result.block && !result.suspend) {
|
|
4551
|
+
emitSandboxAdmitted({ toolCallId: e.toolCallId, toolName: e.toolName, askClasses: ancestorAdmitted, boundary: "sandbox_internal" });
|
|
4552
|
+
result.preToolContext.push(`permission.sandbox_admitted: this call's inherited pending approval was auto-admitted because the deployment declares an isolated sandbox execution env and the request stays inside it. Recorded for audit.`);
|
|
4228
4553
|
}
|
|
4229
4554
|
if (!result.block && !result.suspend && result.preToolContext.length > 0) {
|
|
4230
4555
|
preToolContexts.set(e.toolCallId, result.preToolContext);
|
|
@@ -4531,7 +4856,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4531
4856
|
: undefined;
|
|
4532
4857
|
overheadState.promptChars = systemPrompt.length;
|
|
4533
4858
|
const preparedHolder = {};
|
|
4534
|
-
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, approvalSettledBy, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, suspendRef, suspendProgressRef, reviewRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, brainCallGuardrailRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
4859
|
+
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, approvalSettledBy, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, suspendRef, suspendProgressRef, reviewRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, brainCallGuardrailRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
4535
4860
|
const prepared = buildPrepared();
|
|
4536
4861
|
preparedHolder.current = prepared;
|
|
4537
4862
|
return prepared;
|