@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.
Files changed (96) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/dist/agents/send-message-tool.js +6 -3
  3. package/dist/agents/subagent.d.ts +6 -0
  4. package/dist/agents/subagent.js +48 -6
  5. package/dist/brain/errors.d.ts +20 -0
  6. package/dist/brain/errors.js +40 -0
  7. package/dist/brain/retry.d.ts +16 -2
  8. package/dist/brain/retry.js +3 -2
  9. package/dist/brain/status-sink.d.ts +9 -2
  10. package/dist/brain/stream-engine.d.ts +22 -0
  11. package/dist/brain/stream-engine.js +41 -10
  12. package/dist/core/ask-class.d.ts +48 -0
  13. package/dist/core/ask-class.js +33 -0
  14. package/dist/core/checkpoint-store.d.ts +103 -10
  15. package/dist/core/checkpoint-store.js +3 -1
  16. package/dist/core/governance-codes.d.ts +38 -0
  17. package/dist/core/governance-codes.js +14 -0
  18. package/dist/core/hooks.d.ts +101 -1
  19. package/dist/core/hooks.js +116 -14
  20. package/dist/core/locked-config.d.ts +7 -1
  21. package/dist/core/locked-config.js +2 -1
  22. package/dist/core/memory-engine/delegation-provenance.d.ts +62 -0
  23. package/dist/core/memory-engine/delegation-provenance.js +26 -0
  24. package/dist/core/memory-engine/engine.d.ts +94 -1
  25. package/dist/core/memory-engine/engine.js +329 -12
  26. package/dist/core/memory-engine/header-hints.d.ts +30 -0
  27. package/dist/core/memory-engine/header-hints.js +41 -0
  28. package/dist/core/memory-engine/index.d.ts +3 -2
  29. package/dist/core/memory-engine/index.js +3 -2
  30. package/dist/core/memory-engine/layout.d.ts +232 -0
  31. package/dist/core/memory-engine/layout.js +469 -1
  32. package/dist/core/memory-engine/tools.d.ts +30 -0
  33. package/dist/core/memory-engine/tools.js +108 -17
  34. package/dist/core/permission-rule-consent.d.ts +25 -9
  35. package/dist/core/permission-rule-consent.js +93 -21
  36. package/dist/core/permission-rule-model.d.ts +9 -1
  37. package/dist/core/permission-rule-model.js +2 -2
  38. package/dist/core/permission-rule-org.d.ts +195 -0
  39. package/dist/core/permission-rule-org.js +234 -0
  40. package/dist/core/permission-rule-store.d.ts +249 -6
  41. package/dist/core/permission-rule-store.js +314 -3
  42. package/dist/core/permission-rule-sync.d.ts +139 -0
  43. package/dist/core/permission-rule-sync.js +343 -0
  44. package/dist/core/runner/prepare-memory.js +35 -8
  45. package/dist/core/runner/prepare-task.d.ts +64 -3
  46. package/dist/core/runner/prepare-task.js +357 -32
  47. package/dist/core/runner/runtask.js +166 -6
  48. package/dist/core/shared-memory/contract.js +19 -4
  49. package/dist/core/shared-memory/normalize.d.ts +3 -1
  50. package/dist/core/shared-memory/tools.js +73 -17
  51. package/dist/core/shared-memory/types.d.ts +27 -1
  52. package/dist/core/store-contracts/permission-rule-sync-contract.d.ts +33 -0
  53. package/dist/core/store-contracts/permission-rule-sync-contract.js +186 -0
  54. package/dist/core/task-notification.d.ts +5 -2
  55. package/dist/core/task-registry-agent.d.ts +1 -1
  56. package/dist/core/task-registry-agent.js +6 -2
  57. package/dist/core/task-registry-shared.d.ts +9 -2
  58. package/dist/core/task-registry.d.ts +9 -3
  59. package/dist/core/task-registry.js +2 -0
  60. package/dist/core/tool-policy.d.ts +155 -4
  61. package/dist/core/tool-policy.js +148 -10
  62. package/dist/core/tool-result-store.d.ts +9 -1
  63. package/dist/core/tool-result-store.js +2 -1
  64. package/dist/core/trace.d.ts +79 -1
  65. package/dist/core/types.d.ts +94 -3
  66. package/dist/core/wiring-manifest.d.ts +16 -1
  67. package/dist/core/wiring-manifest.js +7 -1
  68. package/dist/index.d.ts +15 -8
  69. package/dist/index.js +12 -5
  70. package/dist/orchestration/goal.d.ts +10 -0
  71. package/dist/orchestration/goal.js +6 -5
  72. package/dist/stores/file/adoption/adopt.d.ts +146 -0
  73. package/dist/stores/file/adoption/adopt.js +616 -0
  74. package/dist/stores/file/adoption/marker.d.ts +194 -0
  75. package/dist/stores/file/adoption/marker.js +198 -0
  76. package/dist/stores/file/background-agent-store.js +2 -0
  77. package/dist/stores/file/checkpoint-store.d.ts +4 -0
  78. package/dist/stores/file/checkpoint-store.js +3 -0
  79. package/dist/stores/file/file-snapshot-store.js +2 -0
  80. package/dist/stores/file/index.d.ts +2 -0
  81. package/dist/stores/file/index.js +4 -0
  82. package/dist/stores/file/mailbox-store.js +2 -0
  83. package/dist/stores/file/memory-store.js +2 -0
  84. package/dist/stores/file/permission-rule-adopt.d.ts +62 -0
  85. package/dist/stores/file/permission-rule-adopt.js +95 -0
  86. package/dist/stores/file/permission-rule-store.d.ts +80 -2
  87. package/dist/stores/file/permission-rule-store.js +189 -46
  88. package/dist/stores/file/session-policy-store.js +2 -0
  89. package/dist/stores/file/session-store.js +2 -0
  90. package/dist/stores/file/task-list-store.js +2 -0
  91. package/dist/stores/file/tool-result-store.js +2 -0
  92. package/dist/stores/file/usage-window-store.js +2 -0
  93. package/dist/stores/file/workflow-journal-store.js +2 -0
  94. package/dist/stores/file/workflow-run-store.js +2 -0
  95. package/dist/tools/fs/bash-readonly-classifier.js +59 -10
  96. 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.hookEnv === hookEnvSource)
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
- if (memoryEngineSession !== undefined) {
2316
- const { pollution, contentSafety } = memoryEngineSession;
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: contentSafety.trustedTools.has(t.name),
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: contentSafety.trustedTools.has(t.name),
2337
- }), contentSafety.execIsExternalContent);
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, contentSafety.execIsExternalContent);
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.markPolluted(reason);
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
- if (external) {
2375
- mark(`tool "${t.name}" returned the output of a delegated agent whose tool face can reach external content` +
2376
- (typeof requested === "string" ? ` (subagent_type "${requested}")` : " (no subagent_type named classified fail-closed)"));
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 narrowingLayers = [sessionRulePolicy, ...ancestorRulePolicies, skillScopePolicy].filter((p) => p !== undefined);
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
- if (spec.principal === undefined || spec.principal === "")
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 = await provider.forPrincipal(spec.principal).list();
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 && (pc.durableMandate !== true || markInheritedUnavailable(creq.toolCallId))) {
3138
- return first;
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 && (pc.durableMandate !== true || markInheritedUnavailable(creq.toolCallId))) {
3197
- return decision;
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
- try {
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;