@sema-agent/core 7.34.0 → 7.35.1

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 (85) hide show
  1. package/CHANGELOG.md +156 -3
  2. package/dist/brain/errors.d.ts +23 -1
  3. package/dist/brain/errors.js +19 -2
  4. package/dist/brain/status-sink.d.ts +7 -1
  5. package/dist/brain/stream-shared.d.ts +3 -1
  6. package/dist/brain/stream-shared.js +16 -4
  7. package/dist/core/ask-origin.d.ts +34 -6
  8. package/dist/core/ask-origin.js +24 -23
  9. package/dist/core/ask-question.js +2 -1
  10. package/dist/core/checkpoint-store.d.ts +4 -0
  11. package/dist/core/gate-fold.js +2 -2
  12. package/dist/core/gate-lanes.js +5 -4
  13. package/dist/core/governance-codes.d.ts +17 -1
  14. package/dist/core/governance-codes.js +11 -1
  15. package/dist/core/hooks.d.ts +3 -0
  16. package/dist/core/message-utils.d.ts +2 -1
  17. package/dist/core/message-utils.js +2 -1
  18. package/dist/core/mode-policy.d.ts +3 -0
  19. package/dist/core/mode-policy.js +5 -0
  20. package/dist/core/permission-rule-model.d.ts +4 -0
  21. package/dist/core/permission-rule-model.js +4 -0
  22. package/dist/core/permission-rules.js +21 -1
  23. package/dist/core/persisted-rule-arms.d.ts +4 -1
  24. package/dist/core/persisted-rule-arms.js +4 -0
  25. package/dist/core/retired-keys.js +1 -1
  26. package/dist/core/runner/active-skill-scope.js +5 -4
  27. package/dist/core/runner/assemble-result.js +2 -2
  28. package/dist/core/runner/contracts.d.ts +26 -4
  29. package/dist/core/runner/denial-limit-arms.d.ts +6 -1
  30. package/dist/core/runner/denial-limit-arms.js +4 -4
  31. package/dist/core/runner/file-history-request.d.ts +26 -0
  32. package/dist/core/runner/file-history-request.js +90 -0
  33. package/dist/core/runner/permission-rule-lanes.d.ts +2 -1
  34. package/dist/core/runner/permission-rule-lanes.js +14 -11
  35. package/dist/core/runner/prepare-ask-lane.d.ts +1 -1
  36. package/dist/core/runner/prepare-ask-lane.js +2 -2
  37. package/dist/core/runner/prepare-file-history.d.ts +9 -3
  38. package/dist/core/runner/prepare-file-history.js +4 -90
  39. package/dist/core/runner/prepare-gate-stations.d.ts +4 -3
  40. package/dist/core/runner/prepare-gate-stations.js +15 -11
  41. package/dist/core/runner/prepare-inherited-gate.d.ts +8 -1
  42. package/dist/core/runner/prepare-inherited-gate.js +10 -4
  43. package/dist/core/runner/prepare-park-ask.d.ts +4 -2
  44. package/dist/core/runner/prepare-park-ask.js +5 -4
  45. package/dist/core/runner/prepare-policy-chain.d.ts +3 -2
  46. package/dist/core/runner/prepare-policy-chain.js +171 -175
  47. package/dist/core/runner/prepare-task.d.ts +3 -1
  48. package/dist/core/runner/prepare-task.js +9 -5
  49. package/dist/core/runner/run-brain-sinks.js +3 -1
  50. package/dist/core/runner/run-leg.js +5 -3
  51. package/dist/core/runner/run-notification-lane.d.ts +5 -0
  52. package/dist/core/runner/run-notification-lane.js +3 -2
  53. package/dist/core/runner/runtask.d.ts +3 -1
  54. package/dist/core/runner/runtask.js +20 -16
  55. package/dist/core/runner/session-mode-seats.d.ts +48 -0
  56. package/dist/core/runner/session-mode-seats.js +73 -0
  57. package/dist/core/runner/session-rule-policy.js +3 -2
  58. package/dist/core/secret-env.d.ts +30 -58
  59. package/dist/core/secret-env.js +149 -586
  60. package/dist/core/sensitive-path-policy.js +2 -1
  61. package/dist/core/store-contracts/checkpoint-store-contract.js +2 -1
  62. package/dist/core/tool-policy.d.ts +31 -6
  63. package/dist/core/tool-policy.js +35 -16
  64. package/dist/core/tool-registry.d.ts +36 -0
  65. package/dist/core/tool-registry.js +22 -1
  66. package/dist/core/tool-roster.d.ts +17 -2
  67. package/dist/core/tool-roster.js +11 -1
  68. package/dist/core/trace.d.ts +11 -0
  69. package/dist/engine/execution-env/node-execution-env.d.ts +3 -1
  70. package/dist/engine/execution-env/node-execution-env.js +5 -2
  71. package/dist/engine/harness/agent-harness.js +2 -1
  72. package/dist/engine/lsp/node-lsp-manager.js +1 -1
  73. package/dist/index.d.ts +4 -4
  74. package/dist/index.js +4 -4
  75. package/dist/stores/file/index.d.ts +5 -3
  76. package/dist/stores/file/session-store.d.ts +8 -2
  77. package/dist/stores/file/session-store.js +5 -5
  78. package/dist/tools/fs/file-read-check.d.ts +2 -1
  79. package/dist/tools/fs/file-read-check.js +15 -7
  80. package/dist/tools/fs/shell-read-check.d.ts +10 -2
  81. package/dist/tools/fs/shell-read-check.js +12 -8
  82. package/package.json +1 -1
  83. package/test/export-surface.snapshot.json +15 -1
  84. package/wire-changes/core@7.35.0.json +584 -0
  85. package/wire-changes/core@7.35.1.json +59 -0
@@ -6,7 +6,7 @@ import { cloneObserverInput, resolveHookTimeoutMs } from "../hooks.js";
6
6
  import { assertHookSeatsCallable } from "../hook-seats.js";
7
7
  import { isNamespacedCoveringRuleName, namespacedRuleNameCovers } from "../permission-rules.js";
8
8
  import { isIsolated } from "../remote-env.js";
9
- import { askApproverIdentity, carryEngineAttestations, carryPrecedingSettlement, checkToolPolicyProjection, combinePolicies, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, durableParkOptInDeclared, isLiveApproverSeat, resolveAsk, toolPolicyNameSets, attestAskUnresolvableCause } from "../tool-policy.js";
9
+ import { askApproverIdentity, carryEngineAttestations, carryPrecedingSettlement, checkToolPolicyProjection, combinePolicies, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, durableParkOptInDeclared, isLiveApproverSeat, resolveAsk, toolPolicyNameSets, wrapToolPolicy, attestAskUnresolvableCause } from "../tool-policy.js";
10
10
  import { RETIRED_TOOL_NAMES } from "../tool-name-aliases.js";
11
11
  import { NAMESPACED_NAME_SHAPES, protocolOf } from "../protocol-table.js";
12
12
  import { emitTrace } from "../trace.js";
@@ -20,6 +20,7 @@ import { askGrantShapeOf } from "./inherited-ask-grants.js";
20
20
  import { createPermissionRuleLanes, inheritedAskRuleEvidence, createRuleOffersOf } from "./permission-rule-lanes.js";
21
21
  import { createLiveSessionRules, createSessionRulePolicy } from "./session-rule-policy.js";
22
22
  import { thrownText } from "../thrown-value.js";
23
+ import { anyNamed, firstNamed, ruleNamesOf } from "../tool-roster.js";
23
24
  export async function preparePolicyChain(input) {
24
25
  const { spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, envHandToolNames, handsCwdRef, readDenyMatcher, announceRuleStoreSkipped, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, hasSkillManifest, skillScope, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, permissionMode, scratchpadDir, toolRosterDeltas, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef } = input;
25
26
  const ancestorArbitrations = new Map();
@@ -232,7 +233,7 @@ export async function preparePolicyChain(input) {
232
233
  list.push({ cls, layer });
233
234
  foldAskClasses.set(toolCallId, list);
234
235
  };
235
- const sandboxBoundaryCapable = (toolName) => egressTools.has(toolName) || !envHandToolNames.has(toolName);
236
+ const sandboxBoundaryCapable = (call) => anyNamed(egressTools, ruleNamesOf(call)) || !envHandToolNames.has(call.toolName);
236
237
  const emitSandboxAdmitted = (info) => {
237
238
  emitTrace(deps.tracer, () => ({
238
239
  kind: "permission.sandbox_admitted",
@@ -256,18 +257,16 @@ export async function preparePolicyChain(input) {
256
257
  void toolName;
257
258
  };
258
259
  const callerPolicyLayer = policy !== undefined && sameInstanceAncestorCount > 0
259
- ? {
260
- check: async (creq, csignal) => {
261
- const decision = await policy.check(creq, csignal ?? abortController.signal);
262
- sharedFirstDecision.set(creq.toolCallId, decision);
263
- if (sharedFirstDecision.size > SHARED_FIRST_DECISION_CAP) {
264
- const oldest = sharedFirstDecision.keys().next().value;
265
- if (oldest !== undefined)
266
- sharedFirstDecision.delete(oldest);
267
- }
268
- return decision;
269
- },
270
- }
260
+ ? wrapToolPolicy(policy, async (creq, csignal) => {
261
+ const decision = await policy.check(creq, csignal ?? abortController.signal);
262
+ sharedFirstDecision.set(creq.toolCallId, decision);
263
+ if (sharedFirstDecision.size > SHARED_FIRST_DECISION_CAP) {
264
+ const oldest = sharedFirstDecision.keys().next().value;
265
+ if (oldest !== undefined)
266
+ sharedFirstDecision.delete(oldest);
267
+ }
268
+ return decision;
269
+ })
271
270
  : policy;
272
271
  const askSourceIdentity = () => ({
273
272
  principal: spec.principal,
@@ -278,11 +277,12 @@ export async function preparePolicyChain(input) {
278
277
  : {}),
279
278
  ...(delegation.isDelegatedChild ? { isDelegatedChild: true } : {}),
280
279
  });
281
- const riskAxesOf = (toolName) => {
282
- const tier = irreversibilityTier.get(toolName);
283
- const negatives = axisExplicitNegatives.get(toolName);
280
+ const riskAxesOf = (call) => {
281
+ const names = ruleNamesOf(call);
282
+ const tier = firstNamed(irreversibilityTier, names)?.value;
283
+ const negatives = firstNamed(axisExplicitNegatives, names)?.value;
284
284
  const irreversible = tier === "always" || tier === "maybe" ? true : tier === "never" ? false : negatives?.irreversible;
285
- const egress = egressTools.has(toolName) ? true : negatives?.egress;
285
+ const egress = anyNamed(egressTools, names) ? true : negatives?.egress;
286
286
  if (irreversible === undefined && egress === undefined)
287
287
  return {};
288
288
  return { riskAxes: { ...(irreversible !== undefined ? { irreversible } : {}), ...(egress !== undefined ? { egress } : {}) } };
@@ -314,7 +314,9 @@ export async function preparePolicyChain(input) {
314
314
  cwdRef: handsCwdRef,
315
315
  deniesDirectoryRead: readDenyMatcher !== undefined ? (d) => readDenyMatcher.matchPath(d) !== null : undefined,
316
316
  });
317
- const recheckApprovedEdit = async (pol, onAskOf, creq, edit, approved, csignal, ancestorDecider, ancestorTracker) => {
317
+ const recheckApprovedEdit = async (pol, onAskOf, creq, edit, approved, csignal, ancestorAutoMode) => {
318
+ const ancestorDecider = ancestorAutoMode?.decider;
319
+ const ancestorTracker = ancestorAutoMode?.denialTracking;
318
320
  let editArgs = edit;
319
321
  let settledBy = approved;
320
322
  const vetoed = (d) => {
@@ -350,7 +352,7 @@ export async function preparePolicyChain(input) {
350
352
  }
351
353
  if (re.updatedInput !== undefined)
352
354
  editArgs = re.updatedInput;
353
- const judged = await judgeInheritedClassifier({ liveMode: () => permissionMode.current, autoMode: ancestorDecider !== undefined ? { decider: ancestorDecider, denialTracking: ancestorTracker } : undefined, ask: re, req: { ...creq, args: editArgs }, signal: csignal ?? abortController.signal, subject: "the approved edit", allowRewrite: editArgs, unavailableSeat: classifierUnavailableSeat({ station: "ancestor", ...(ancestorDecider?.timeoutMs !== undefined ? { budgetMs: ancestorDecider.timeoutMs } : {}), approvalSeat: isLiveApproverSeat(onAskOf), onNotice: deps.onNotice, sessionId, runId }) });
355
+ const judged = await judgeInheritedClassifier({ liveMode: () => permissionMode.current, autoMode: ancestorAutoMode, ask: re, req: { ...creq, args: editArgs }, signal: csignal ?? abortController.signal, subject: "the approved edit", allowRewrite: editArgs, unavailableSeat: classifierUnavailableSeat({ station: "ancestor", ...(ancestorDecider?.timeoutMs !== undefined ? { budgetMs: ancestorDecider.timeoutMs } : {}), approvalSeat: isLiveApproverSeat(onAskOf), onNotice: deps.onNotice, sessionId, runId }) });
354
356
  if (judged.kind === "allow") {
355
357
  const cleared = { ...judged.result };
356
358
  carryEngineAttestations(judged.result, cleared);
@@ -371,10 +373,10 @@ export async function preparePolicyChain(input) {
371
373
  toolName: creq.toolName,
372
374
  toolCallId: creq.toolCallId,
373
375
  args: editArgs,
374
- ...ruleOffersOf(creq.toolName, editArgs, ancestorFacts(re)),
376
+ ...ruleOffersOf(creq, editArgs, ancestorFacts(re)),
375
377
  message: re.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
376
378
  ...askSourceIdentity(),
377
- ...riskAxesOf(creq.toolName),
379
+ ...riskAxesOf(creq),
378
380
  ...(re.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
379
381
  ...askMandateMember(ancestorFacts(re)),
380
382
  ...(re.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: re.persistedRuleShadowed } : {}),
@@ -438,7 +440,7 @@ export async function preparePolicyChain(input) {
438
440
  if (judged.kind === "deny")
439
441
  return judged.result;
440
442
  const { ask: inheritedAsk, fallback, mintedHere } = judged;
441
- if (fallback === undefined && sandboxAdmissionArmed && policyAskClassOf(pc.policy) === "sandbox_local" && !sandboxBoundaryCapable(creq.toolName) && classifyAsk(inheritedAsk, INHERITED_STATION_FACTS).row.sandboxAdmission) {
443
+ if (fallback === undefined && sandboxAdmissionArmed && policyAskClassOf(pc.policy) === "sandbox_local" && !sandboxBoundaryCapable(creq) && classifyAsk(inheritedAsk, INHERITED_STATION_FACTS).row.sandboxAdmission) {
442
444
  recordAncestorSandboxAdmission(creq.toolCallId, creq.toolName);
443
445
  return { action: "allow" };
444
446
  }
@@ -451,10 +453,10 @@ export async function preparePolicyChain(input) {
451
453
  toolName: creq.toolName,
452
454
  toolCallId: creq.toolCallId,
453
455
  args: presentedArgs,
454
- ...ruleOffersOf(creq.toolName, presentedArgs, ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
456
+ ...ruleOffersOf(creq, presentedArgs, ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
455
457
  message: inheritedAsk.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
456
458
  ...askSourceIdentity(),
457
- ...riskAxesOf(creq.toolName),
459
+ ...riskAxesOf(creq),
458
460
  ...(inheritedAsk.action === "ask" && inheritedAsk.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
459
461
  ...askMandateMember(ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
460
462
  ...(inheritedAsk.action === "ask" && inheritedAsk.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: inheritedAsk.persistedRuleShadowed } : {}),
@@ -472,92 +474,90 @@ export async function preparePolicyChain(input) {
472
474
  if (resolved.action !== "allow")
473
475
  return resolved;
474
476
  if (resolved.updatedInput !== undefined) {
475
- return recheckApprovedEdit(pc.policy, pc.onAsk, creq, resolved.updatedInput, resolved, csignal, pc.autoMode?.decider, pc.autoMode?.denialTracking);
477
+ return recheckApprovedEdit(pc.policy, pc.onAsk, creq, resolved.updatedInput, resolved, csignal, pc.autoMode);
476
478
  }
477
479
  recordInheritedAskGrant(creq.toolCallId, pc.onAsk, resolved.presentedInput, askWaitMs, resolved);
478
480
  return { action: "allow" };
479
481
  },
480
482
  }
481
- : {
482
- check: async (creq, csignal) => {
483
- let decision;
484
- try {
485
- decision = await pc.policy.check(creq, csignal ?? abortController.signal);
486
- }
487
- catch (err) {
488
- return {
489
- action: "deny",
490
- message: `inherited parent policy errored for "${creq.toolName}": ${thrownText(err)}`,
491
- };
492
- }
493
- if (decision.action !== "ask")
483
+ : wrapToolPolicy(pc.policy, async (creq, csignal) => {
484
+ let decision;
485
+ try {
486
+ decision = await pc.policy.check(creq, csignal ?? abortController.signal);
487
+ }
488
+ catch (err) {
489
+ return {
490
+ action: "deny",
491
+ message: `inherited parent policy errored for "${creq.toolName}": ${thrownText(err)}`,
492
+ };
493
+ }
494
+ if (decision.action !== "ask")
495
+ return decision;
496
+ if (creq.toolName === ASK_USER_QUESTION_TOOL_NAME) {
497
+ if (pc.contentMandate !== true || markInheritedUnavailable(creq.toolCallId))
494
498
  return decision;
495
- if (creq.toolName === ASK_USER_QUESTION_TOOL_NAME) {
496
- if (pc.contentMandate !== true || markInheritedUnavailable(creq.toolCallId))
497
- return decision;
498
- return {
499
- action: "deny",
500
- message: `inherited parent policy requires durable handling of the question raised for "${creq.toolName}" and the marker channel is at capacity — ` +
501
- `denied fail-closed (tighten-only)`,
502
- };
503
- }
504
- if (pc.durableMandate === true) {
505
- if (mandatedCallFloats(creq.toolCallId))
506
- return decision;
507
- return mandateUnreconstructibleDeny(creq);
508
- }
509
- const presentedArgs = decision.updatedInput !== undefined ? decision.updatedInput : creq.args;
510
- const judged = await judgeInheritedClassifier({ liveMode: () => permissionMode.current, autoMode: pc.autoMode, ask: decision, req: { ...creq, args: presentedArgs }, signal: csignal ?? abortController.signal, subject: "this call", allowRewrite: decision.updatedInput, unavailableSeat: classifierUnavailableSeat({ station: "ancestor", ...(pc.autoMode?.decider.timeoutMs !== undefined ? { budgetMs: pc.autoMode.decider.timeoutMs } : {}), approvalSeat: isLiveApproverSeat(pc.onAsk), onNotice: deps.onNotice, sessionId, runId }) });
511
- if (judged.kind === "allow")
512
- return judged.result;
513
- if (judged.kind === "deny")
514
- return judged.result;
515
- const { ask: inheritedAsk, fallback, mintedHere } = judged;
516
- if (fallback === undefined && sandboxAdmissionArmed && policyAskClassOf(pc.policy) === "sandbox_local" && !sandboxBoundaryCapable(creq.toolName) && classifyAsk(inheritedAsk, INHERITED_STATION_FACTS).row.sandboxAdmission) {
517
- recordAncestorSandboxAdmission(creq.toolCallId, creq.toolName);
518
- return decision.updatedInput !== undefined ? { action: "allow", updatedInput: decision.updatedInput } : { action: "allow" };
519
- }
520
- const arbitrated = arbitrateInherited(inheritedAsk.action === "ask" ? { ...inheritedAsk, ...(decision.updatedInput !== undefined ? { updatedInput: decision.updatedInput } : {}) } : inheritedAsk, creq.toolCallId);
521
- if (arbitrated !== undefined)
522
- return arbitrated;
523
- const askT0 = now();
524
- const resolved = await resolveAsk({
525
- toolName: creq.toolName,
526
- toolCallId: creq.toolCallId,
527
- args: presentedArgs,
528
- ...ruleOffersOf(creq.toolName, presentedArgs, ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
529
- message: inheritedAsk.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
530
- ...askSourceIdentity(),
531
- ...riskAxesOf(creq.toolName),
532
- ...(inheritedAsk.action === "ask" && inheritedAsk.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
533
- ...askMandateMember(ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
534
- ...(inheritedAsk.action === "ask" && inheritedAsk.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: inheritedAsk.persistedRuleShadowed } : {}),
535
- ...inheritedAskCarry(judged, isLiveApproverSeat(pc.onAsk), pc.autoMode?.denialTracking),
536
- ruleEvidence: inheritedAskEvidence,
537
- }, pc.onAsk, csignal ?? abortController.signal, lateAskSettlementObserver({ toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
538
- void settleReadRootGrant(resolved.readRootGrant, undefined, { toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice });
539
- const askWaitMs = Math.max(0, now() - askT0);
540
- if (resolved.action === "deny" && resolved.approverUnavailable === true) {
541
- if (markInheritedUnavailable(creq.toolCallId))
542
- return inheritedAsk;
543
- return markerChannelFullDeny(creq);
544
- }
545
- settleDenialLimitFallback({ fallback, mintedHere, tracker: pc.autoMode?.denialTracking, resolved, headless: headlessDenyAtFold, stop: stopForDenialLimit, toolName: creq.toolName, toolCallId: creq.toolCallId });
546
- if (resolved.action !== "allow")
547
- return resolved;
548
- if (resolved.updatedInput !== undefined) {
549
- return recheckApprovedEdit(pc.policy, pc.onAsk, creq, resolved.updatedInput, resolved, csignal, pc.autoMode?.decider, pc.autoMode?.denialTracking);
550
- }
551
- if (pc.preToolUse === undefined) {
552
- recordInheritedAskGrant(creq.toolCallId, pc.onAsk, resolved.presentedInput, askWaitMs, resolved);
553
- }
554
- if (decision.updatedInput === undefined)
555
- return resolved;
556
- const approvedWithRewrite = { ...resolved, updatedInput: decision.updatedInput };
557
- carryEngineAttestations(resolved, approvedWithRewrite);
558
- return approvedWithRewrite;
559
- },
560
- });
499
+ return {
500
+ action: "deny",
501
+ message: `inherited parent policy requires durable handling of the question raised for "${creq.toolName}" and the marker channel is at capacity — ` +
502
+ `denied fail-closed (tighten-only)`,
503
+ };
504
+ }
505
+ if (pc.durableMandate === true) {
506
+ if (mandatedCallFloats(creq.toolCallId))
507
+ return decision;
508
+ return mandateUnreconstructibleDeny(creq);
509
+ }
510
+ const presentedArgs = decision.updatedInput !== undefined ? decision.updatedInput : creq.args;
511
+ const judged = await judgeInheritedClassifier({ liveMode: () => permissionMode.current, autoMode: pc.autoMode, ask: decision, req: { ...creq, args: presentedArgs }, signal: csignal ?? abortController.signal, subject: "this call", allowRewrite: decision.updatedInput, unavailableSeat: classifierUnavailableSeat({ station: "ancestor", ...(pc.autoMode?.decider.timeoutMs !== undefined ? { budgetMs: pc.autoMode.decider.timeoutMs } : {}), approvalSeat: isLiveApproverSeat(pc.onAsk), onNotice: deps.onNotice, sessionId, runId }) });
512
+ if (judged.kind === "allow")
513
+ return judged.result;
514
+ if (judged.kind === "deny")
515
+ return judged.result;
516
+ const { ask: inheritedAsk, fallback, mintedHere } = judged;
517
+ if (fallback === undefined && sandboxAdmissionArmed && policyAskClassOf(pc.policy) === "sandbox_local" && !sandboxBoundaryCapable(creq) && classifyAsk(inheritedAsk, INHERITED_STATION_FACTS).row.sandboxAdmission) {
518
+ recordAncestorSandboxAdmission(creq.toolCallId, creq.toolName);
519
+ return decision.updatedInput !== undefined ? { action: "allow", updatedInput: decision.updatedInput } : { action: "allow" };
520
+ }
521
+ const arbitrated = arbitrateInherited(inheritedAsk.action === "ask" ? { ...inheritedAsk, ...(decision.updatedInput !== undefined ? { updatedInput: decision.updatedInput } : {}) } : inheritedAsk, creq.toolCallId);
522
+ if (arbitrated !== undefined)
523
+ return arbitrated;
524
+ const askT0 = now();
525
+ const resolved = await resolveAsk({
526
+ toolName: creq.toolName,
527
+ toolCallId: creq.toolCallId,
528
+ args: presentedArgs,
529
+ ...ruleOffersOf(creq, presentedArgs, ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
530
+ message: inheritedAsk.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
531
+ ...askSourceIdentity(),
532
+ ...riskAxesOf(creq),
533
+ ...(inheritedAsk.action === "ask" && inheritedAsk.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
534
+ ...askMandateMember(ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
535
+ ...(inheritedAsk.action === "ask" && inheritedAsk.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: inheritedAsk.persistedRuleShadowed } : {}),
536
+ ...inheritedAskCarry(judged, isLiveApproverSeat(pc.onAsk), pc.autoMode?.denialTracking),
537
+ ruleEvidence: inheritedAskEvidence,
538
+ }, pc.onAsk, csignal ?? abortController.signal, lateAskSettlementObserver({ toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
539
+ void settleReadRootGrant(resolved.readRootGrant, undefined, { toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice });
540
+ const askWaitMs = Math.max(0, now() - askT0);
541
+ if (resolved.action === "deny" && resolved.approverUnavailable === true) {
542
+ if (markInheritedUnavailable(creq.toolCallId))
543
+ return inheritedAsk;
544
+ return markerChannelFullDeny(creq);
545
+ }
546
+ settleDenialLimitFallback({ fallback, mintedHere, tracker: pc.autoMode?.denialTracking, resolved, headless: headlessDenyAtFold, stop: stopForDenialLimit, toolName: creq.toolName, toolCallId: creq.toolCallId });
547
+ if (resolved.action !== "allow")
548
+ return resolved;
549
+ if (resolved.updatedInput !== undefined) {
550
+ return recheckApprovedEdit(pc.policy, pc.onAsk, creq, resolved.updatedInput, resolved, csignal, pc.autoMode);
551
+ }
552
+ if (pc.preToolUse === undefined) {
553
+ recordInheritedAskGrant(creq.toolCallId, pc.onAsk, resolved.presentedInput, askWaitMs, resolved);
554
+ }
555
+ if (decision.updatedInput === undefined)
556
+ return resolved;
557
+ const approvedWithRewrite = { ...resolved, updatedInput: decision.updatedInput };
558
+ carryEngineAttestations(resolved, approvedWithRewrite);
559
+ return approvedWithRewrite;
560
+ }));
561
561
  const rewriteCapableLayers = policy !== undefined || parentConstraintWrappers.length > 0;
562
562
  const rewriteEmitterIndex = new Map();
563
563
  const rewriteCapableChain = [
@@ -570,16 +570,14 @@ export async function preparePolicyChain(input) {
570
570
  if (policy !== undefined && pc.policy === policy)
571
571
  sameInstanceAdapterIndices.add(j + callerSlotOffset);
572
572
  });
573
- const instrumentedChain = rewriteCapableChain.map((layer, idx) => ({
574
- check: async (creq, csignal) => {
575
- const d = await layer.check(creq, csignal);
576
- if (d.updatedInput !== undefined)
577
- rewriteEmitterIndex.set(creq.toolCallId, idx);
578
- if (d.action === "ask") {
579
- recordFoldAskClass(creq.toolCallId, idx < callerSlotOffset ? policyAskClassOf(policy) : "mandate", idx < callerSlotOffset ? "caller_policy" : "ancestor_constraint");
580
- }
581
- return d;
582
- },
573
+ const instrumentedChain = rewriteCapableChain.map((layer, idx) => wrapToolPolicy(layer, async (creq, csignal) => {
574
+ const d = await layer.check(creq, csignal);
575
+ if (d.updatedInput !== undefined)
576
+ rewriteEmitterIndex.set(creq.toolCallId, idx);
577
+ if (d.action === "ask") {
578
+ recordFoldAskClass(creq.toolCallId, idx < callerSlotOffset ? policyAskClassOf(policy) : "mandate", idx < callerSlotOffset ? "caller_policy" : "ancestor_constraint");
579
+ }
580
+ return d;
583
581
  }));
584
582
  const modeGateLayer = createModeGatePolicy(permissionMode, { env: executionEnv, rootPath: taskRootFinal, acceptDirs: [taskRootFinal, ...additionalRootsCanonical], roster: toolRosterDeltas, readOnlyDeclared: (name) => mountedToolDeclaresReadOnly(tools, name), ...(scratchpadDir !== undefined ? { exemptDirs: [scratchpadDir] } : {}) });
585
583
  const policyLayers = [
@@ -593,70 +591,68 @@ export async function preparePolicyChain(input) {
593
591
  : [...integrityGuardLayers, ...policyLayers, modeGateLayer];
594
592
  const foldedPolicy = guardedPolicyLayers.length === 1 ? guardedPolicyLayers[0] : combinePolicies(...guardedPolicyLayers);
595
593
  const effectivePolicy = foldedPolicy !== undefined && rewriteCapableLayers
596
- ? {
597
- check: async (req, signal) => {
598
- let decision;
599
- try {
600
- decision = await foldedPolicy.check(req, signal);
601
- }
602
- catch (err) {
603
- rewriteEmitterIndex.delete(req.toolCallId);
604
- throw err;
605
- }
606
- const emitterIdx = rewriteEmitterIndex.get(req.toolCallId);
594
+ ? wrapToolPolicy(foldedPolicy, async (req, signal) => {
595
+ let decision;
596
+ try {
597
+ decision = await foldedPolicy.check(req, signal);
598
+ }
599
+ catch (err) {
607
600
  rewriteEmitterIndex.delete(req.toolCallId);
608
- if (decision.action === "deny" || decision.updatedInput === undefined)
609
- return decision;
610
- const recheckPrefix = rewriteCapableChain.slice(0, emitterIdx ?? rewriteCapableChain.length);
611
- const postEmitterSameInstanceAdapters = [];
612
- if (emitterIdx !== undefined && emitterIdx > 0) {
613
- for (let i = emitterIdx + 1; i < rewriteCapableChain.length; i++) {
614
- if (sameInstanceAdapterIndices.has(i))
615
- postEmitterSameInstanceAdapters.push(rewriteCapableChain[i]);
616
- }
601
+ throw err;
602
+ }
603
+ const emitterIdx = rewriteEmitterIndex.get(req.toolCallId);
604
+ rewriteEmitterIndex.delete(req.toolCallId);
605
+ if (decision.action === "deny" || decision.updatedInput === undefined)
606
+ return decision;
607
+ const recheckPrefix = rewriteCapableChain.slice(0, emitterIdx ?? rewriteCapableChain.length);
608
+ const postEmitterSameInstanceAdapters = [];
609
+ if (emitterIdx !== undefined && emitterIdx > 0) {
610
+ for (let i = emitterIdx + 1; i < rewriteCapableChain.length; i++) {
611
+ if (sameInstanceAdapterIndices.has(i))
612
+ postEmitterSameInstanceAdapters.push(rewriteCapableChain[i]);
617
613
  }
618
- const recheckLayers = [...recheckPrefix, ...postEmitterSameInstanceAdapters];
619
- const finalReq = { ...req, args: decision.updatedInput };
620
- let asked;
621
- const revalidated = {};
622
- try {
623
- for (const layer of recheckLayers) {
624
- const d = await layer.check(finalReq, signal);
625
- if (d.action === "deny") {
626
- const carried = { ...d, updatedInput: decision.updatedInput };
627
- carryEngineAttestations(d, carried);
628
- carryPrecedingSettlement(decision, carried);
629
- return carried;
630
- }
631
- if (d.updatedInput !== undefined) {
632
- return {
633
- action: "deny",
634
- message: "policy required a rewrite during final-form re-check — the arguments that would execute were not accepted as-is (fail-closed; rewrite chains are not iterated to a fixpoint)",
635
- updatedInput: decision.updatedInput,
636
- };
637
- }
638
- if (d.action === "ask" && asked === undefined)
639
- asked = d;
640
- carryEngineAttestations(d, revalidated);
614
+ }
615
+ const recheckLayers = [...recheckPrefix, ...postEmitterSameInstanceAdapters];
616
+ const finalReq = { ...req, args: decision.updatedInput };
617
+ let asked;
618
+ const revalidated = {};
619
+ try {
620
+ for (const layer of recheckLayers) {
621
+ const d = await layer.check(finalReq, signal);
622
+ if (d.action === "deny") {
623
+ const carried = { ...d, updatedInput: decision.updatedInput };
624
+ carryEngineAttestations(d, carried);
625
+ carryPrecedingSettlement(decision, carried);
626
+ return carried;
641
627
  }
628
+ if (d.updatedInput !== undefined) {
629
+ return {
630
+ action: "deny",
631
+ message: "policy required a rewrite during final-form re-check — the arguments that would execute were not accepted as-is (fail-closed; rewrite chains are not iterated to a fixpoint)",
632
+ updatedInput: decision.updatedInput,
633
+ };
634
+ }
635
+ if (d.action === "ask" && asked === undefined)
636
+ asked = d;
637
+ carryEngineAttestations(d, revalidated);
642
638
  }
643
- finally {
644
- rewriteEmitterIndex.delete(req.toolCallId);
645
- }
646
- if (asked !== undefined && decision.action === "allow") {
647
- const surfaced = { ...asked, updatedInput: decision.updatedInput };
648
- carryEngineAttestations(asked, surfaced);
649
- return surfaced;
650
- }
651
- if (decision.action === "allow") {
652
- const owned = { ...decision };
653
- carryEngineAttestations(decision, owned);
654
- carryEngineAttestations(revalidated, owned);
655
- return owned;
656
- }
657
- return decision;
658
- },
659
- }
639
+ }
640
+ finally {
641
+ rewriteEmitterIndex.delete(req.toolCallId);
642
+ }
643
+ if (asked !== undefined && decision.action === "allow") {
644
+ const surfaced = { ...asked, updatedInput: decision.updatedInput };
645
+ carryEngineAttestations(asked, surfaced);
646
+ return surfaced;
647
+ }
648
+ if (decision.action === "allow") {
649
+ const owned = { ...decision };
650
+ carryEngineAttestations(decision, owned);
651
+ carryEngineAttestations(revalidated, owned);
652
+ return owned;
653
+ }
654
+ return decision;
655
+ })
660
656
  : foldedPolicy;
661
657
  return { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions, ancestorArbitrations };
662
658
  }
@@ -113,7 +113,9 @@ runnerSelf?: RunnerSelfSeat,
113
113
  * shape the Runner uses for the generated taskId/sessionId. */
114
114
  runIdSink?: {
115
115
  runId?: string;
116
- }): Promise<Prepared>;
116
+ },
117
+ /** The Runner's lock on this leg's session, handed only when the spec named none: taken the moment the store mints the id. */
118
+ holdSessionLock?: (sessionId: string) => Promise<void>): Promise<Prepared>;
117
119
  /** The entry IS the contract the notification lane's seat names ({@link PrepareTaskFn}, contracts.ts):
118
120
  * a signature change reds HERE, at the declaration, as `false` failing the `true` bound. IDENTITY, not assignability:
119
121
  * the parameter tuple (arity, optionality, each type) and the return type must be the SAME type in both directions —
@@ -44,6 +44,7 @@ import { parkContaminationMarker } from "./park-commit.js";
44
44
  export { gatedCallIdOf, USAGE_WINDOW_REAP_MARGIN_MS } from "./park-commit.js";
45
45
  import { prepareOffloadWrappers } from "./prepare-offload-wrappers.js";
46
46
  import { prepareFileHistory } from "./prepare-file-history.js";
47
+ import { resolveFileHistoryRequest } from "./file-history-request.js";
47
48
  import { prepareExecutionEnv } from "./prepare-execution-env.js";
48
49
  import { prepareRunRefs } from "./prepare-run-refs.js";
49
50
  import { prepareInheritedGate } from "./prepare-inherited-gate.js";
@@ -281,7 +282,7 @@ function refusePermissionModeDoors(spec) {
281
282
  }
282
283
  return mode;
283
284
  }
284
- export async function prepareTask(spec, deps, sessions, resume, internals, runnerSelf, runIdSink) {
285
+ export async function prepareTask(spec, deps, sessions, resume, internals, runnerSelf, runIdSink, holdSessionLock) {
285
286
  const runId = uuidv7();
286
287
  if (runIdSink !== undefined)
287
288
  runIdSink.runId = runId;
@@ -304,8 +305,10 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
304
305
  let shellGatedTools = new Set();
305
306
  refuseRequireExistingWithoutSession(spec);
306
307
  refuseRegistryScopeMismatch(spec, internals);
308
+ const fileHistoryRequest = resolveFileHistoryRequest(spec, deps);
307
309
  const { acquired, session, conflictRef, wakeRecovered } = await prepareAcquireReconcile({ sessions, spec, resume, toolEffects, ...(internals?.sessionPlacement !== undefined ? { placement: internals.sessionPlacement } : {}), ...(() => { const g = durableParkGapFor(deps, spec); return g !== undefined ? { durableParkGap: g } : {}; })() });
308
310
  const sessionId = acquired.sessionId;
311
+ await holdSessionLock?.(sessionId);
309
312
  const onceLedger = await loadAnnounceOnceLedger(session, resume?.seed.announcedListings, deps.onError);
310
313
  const hostTaskId = spec.taskId ?? sessionId;
311
314
  const delegation = effectiveDelegationFacts(internals, resume?.seed.isDelegatedChild);
@@ -403,7 +406,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
403
406
  const ownedEnv = ownedEnvSeat.current;
404
407
  const { taskRootFinal, rebaseRestoredPath } = await prepareWorkspaceRestore({ ownedEnv, deps, spec, internals, resume, taskRootInitial, abortController, sessionId });
405
408
  const { canonical: taskRootCanonical } = await resolveTaskRootCanonical({ env: executionEnv, given: taskRootFinal, onNotice: deps.onNotice, sessionId, runId });
406
- const { trackFileEdit, fileHistoryBoundary, restoredFilePaths, rewindNotes, fileHistoryEnabled, fileHistoryFact, historyScope, historyRoot, historyFs } = await prepareFileHistory({ deps, spec, internals, executionEnv, taskRootFinal, handsEnabled, sessionId, session, abortController });
409
+ const { trackFileEdit, fileHistoryBoundary, restoredFilePaths, rewindNotes, fileHistoryEnabled, fileHistoryFact, historyScope, historyRoot, historyFs } = await prepareFileHistory({ deps, spec, request: fileHistoryRequest, internals, executionEnv, taskRootFinal, handsEnabled, sessionId, session, abortController });
407
410
  const { noteFileEdited, editedFilesSnapshot, harnessRef, toolProgressRef, skillScope, forwardEvent, reviewRequestRef, requestReview, stopRequestedRef, requestStopAfterTurn, bindPermissionModeSeat, enterPlanMode, subagentRetain, worktreeIsolation } = prepareRunRefs({ spec, internals, executionEnv, taskRootFinal });
408
411
  const legContextOverlay = () => runnerSelf?.contextOverlayOf?.(harnessRef.current?.getModelRef() ?? resolvedRole.modelRef);
409
412
  let resolvedReadFace;
@@ -411,7 +414,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
411
414
  let readDenyAdditionsNormalized = [];
412
415
  let handsLessResolvedFace;
413
416
  let handsLessResolvedWriteFace;
414
- const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, permissionMode, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, checkpointSeat, toolFaceSnapshot, mountsCallerTool, promptProfile, resolvedInteractionPosture, permissionModeSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun: () => { abortController.abort(); void harness.abort(); }, runtimeCaps: () => runtimeCaps, hooks: () => hooks, preToolUseObservational: () => preToolUseObservational, hookTimeoutMs: () => hookTimeoutMs, hookEnvFace: () => hookEnvFace, autoModeDecider: () => autoModeDeciderRef.current, autoModeDenialTracking: () => autoModeDenialTrackingRef.current, autoModeArming: () => autoModeArmingRef.current, agentForkDenial: () => agentForkDenial, observersActive: () => observersActive, resolvedReadFace: () => resolvedReadFace, resolvedWriteFace: () => resolvedWriteFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, handsLessResolvedWriteFace: () => handsLessResolvedWriteFace, centerAdoption: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
417
+ const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, permissionMode, commitModeSeats, endModeCeiling, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, checkpointSeat, toolFaceSnapshot, mountsCallerTool, promptProfile, resolvedInteractionPosture, permissionModeSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun: () => { abortController.abort(); void harness.abort(); }, runtimeCaps: () => runtimeCaps, hooks: () => hooks, preToolUseObservational: () => preToolUseObservational, hookTimeoutMs: () => hookTimeoutMs, hookEnvFace: () => hookEnvFace, autoModeDecider: () => autoModeDeciderRef.current, autoModeDenialTracking: () => autoModeDenialTrackingRef.current, autoModeArming: () => autoModeArmingRef.current, agentForkDenial: () => agentForkDenial, observersActive: () => observersActive, resolvedReadFace: () => resolvedReadFace, resolvedWriteFace: () => resolvedWriteFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, handsLessResolvedWriteFace: () => handsLessResolvedWriteFace, centerAdoption: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
415
418
  bindPermissionModeSeat(permissionMode);
416
419
  const announceRuleStoreSkipped = ruleStoreUnreadableNotice(deps.onNotice, { sessionId, runId });
417
420
  const persistedPathRowsRead = persistedPathRowsOfTask({ deps, principal: spec.principal, sessionId, root: taskRootFinal, home: executionEnv.homeDir, taskId: hostTaskId, handsEnabled, signals: [abortController.signal, ...(spec.signal !== undefined ? [spec.signal] : [])], onUnread: announceRuleStoreSkipped });
@@ -750,15 +753,16 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
750
753
  const { askLane } = prepareAskLane({ gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking: autoModeDenialTrackingRef, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash, grantReadRoot: handsReadFace.grantReadRoot });
751
754
  const { saga } = prepareSuspendSaga({ gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, permissionMode, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter });
752
755
  const { suspendForResource, suspendForPlatformLimit, suspendForReview } = prepareBoundaryParks({ saga, spec, checkpointStore, abortController, harness, session, sessions, sessionId, deps, priorLedger, maxSlices, maxSuspends, resourceTotal, priorSuspendCount, suspendChainBase, humanReviewRef, liveSpendRef, now, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, pausedRef, remoteEnvFailures, resourceSuspendEligible, durableSuspendInfraReady, incompleteSuspendAdapter });
753
- const { parkAsk } = prepareParkAsk({ askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas: rosterSeat, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, pausedRef });
756
+ const { parkAsk } = prepareParkAsk({ askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas: rosterSeat, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, irreversibilityTier, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, pausedRef });
754
757
  prepareGateStations({ askLane, parkAsk, tools, toolRosterDeltas: rosterSeat, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, readDenyInForce: () => ({ tiers: handsReadFace.readDenyBuiltinTiersActive, rows: [...readDenyAdditionsNormalized.map((e) => e.pattern), ...handsReadFaceInput.pathDenyRules.filter((r) => r.faces.includes("read")).map((r) => r.rule)] }), hookEnvFace, writeProtectionCheck, shellReadCheck, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider: autoModeDeciderRef, autoModeDenialTracking: autoModeDenialTrackingRef, askRoute: wiringManifest.ask, permissionMode, toolMarkSources, ...(deps.a2a?.isolatePeers === true ? { a2aIsolatePeers: true } : {}), ancestorArbitrations, runId, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash, frameSettlement });
755
758
  const postureFrameRef = {};
756
759
  const { microCompact, charsPerToken } = prepareContextLane({ model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, postureFrameRef, requestLossyRef, trimPressureRef, contextOverlay: legContextOverlay, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}) });
757
760
  const preparedHolder = {};
758
761
  const { cacheBreakDetector, cacheFingerprint, promptOverheadTokens, readTaskFile, normalizeAttachmentPath, isDedupStubResult, recentlyReadFiles, onCompactionApplied, detectExternalChanges, listBackgroundTasks, centerCompactionCandidate, effectiveReadFaceObserved, effectiveWriteFaceObserved, effectiveReadDenyObserved } = prepareTurnWiring({ deps, systemPromptSeat, harnessTools, fpRef, model, epochArtifactDigestForSnapshot, promptProfile, fableMitigations, systemBlocks, thinking, spec, sessionId, turnSnapshotRef, promptManifest, charsPerToken, handsEnabled, executionEnv, attachmentRootCanonical, additionalRootsCanonical, additionalReadRootsCanonical, readDenyMatcher, resolvedReadFace, handsCwdRef, readFileStateForCheckpoint, denyNarrowingPolicy, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder });
759
762
  await onceLedger.settle(session, announcedListingsRef);
760
- const buildPrepared = () => ({ harness, session, sessionId, runId, registryAccess: { owner: hostTaskId, scope: taskScope }, reminderMark, reminderDisclosureCounts, fullShellReachable, approvalPosture: { get mode() { return permissionMode.current; }, askEffective: wiringManifest.ask.effective ?? "unresolved" }, permissionMode, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(mcpRedial !== undefined ? { mcpRedial } : {}), ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, gateOutcomes, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved, effectiveWriteFace: effectiveWriteFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(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, pausedRef, undeliveredContinuationRef, suspendProgressRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, workCutRef, brainCallGuardrailRef, gateStopRef, stopForDenialLimit, suspendForReview, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}), resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolRoster, toolRosterDeltas: rosterSeat, structuredProjector, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, microCompact, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), stopRequestedRef, announcedSnapshotRecovered: onceLedger.recovered, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, frameReceipts: { pending: [], listingSnapshotOwed: false }, frameSettlement, ...(peerLaneRefs.boundaryRound !== undefined ? { peerBoundaryRound: peerLaneRefs.boundaryRound } : {}), gitStatusRef, postureFrameRef, listBackgroundTasks, hookIdentity, ...(sessionBoxBound !== undefined ? { sessionBox: sessionBoxBound } : {}), hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(shellReadCheck !== undefined ? { shellReadCheck } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}) });
763
+ const buildPrepared = () => ({ harness, session, sessionId, runId, registryAccess: { owner: hostTaskId, scope: taskScope }, reminderMark, reminderDisclosureCounts, fullShellReachable, approvalPosture: { get mode() { return permissionMode.current; }, askEffective: wiringManifest.ask.effective ?? "unresolved" }, permissionMode, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(mcpRedial !== undefined ? { mcpRedial } : {}), ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, gateOutcomes, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved, effectiveWriteFace: effectiveWriteFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, endModeCeiling, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, pausedRef, undeliveredContinuationRef, suspendProgressRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, workCutRef, brainCallGuardrailRef, gateStopRef, stopForDenialLimit, suspendForReview, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}), resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolRoster, toolRosterDeltas: rosterSeat, structuredProjector, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, microCompact, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), stopRequestedRef, announcedSnapshotRecovered: onceLedger.recovered, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, frameReceipts: { pending: [], listingSnapshotOwed: false }, frameSettlement, ...(peerLaneRefs.boundaryRound !== undefined ? { peerBoundaryRound: peerLaneRefs.boundaryRound } : {}), gitStatusRef, postureFrameRef, listBackgroundTasks, hookIdentity, ...(sessionBoxBound !== undefined ? { sessionBox: sessionBoxBound } : {}), hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(shellReadCheck !== undefined ? { shellReadCheck } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}) });
761
764
  rollback.commit();
765
+ commitModeSeats(abortController.signal);
762
766
  const prepared = buildPrepared();
763
767
  preparedHolder.current = prepared;
764
768
  return prepared;
@@ -54,7 +54,9 @@ export function runBrainSinks(input) {
54
54
  ...(t.nextDelayMs !== undefined ? { nextDelayMs: t.nextDelayMs } : {}),
55
55
  ts: Date.now(),
56
56
  }
57
- : { kind: "vision.placeholder", version: 1, taskId: rs.telemetry.taskId, count: t.count, ts: Date.now() });
57
+ : t.kind === "echo_dropped"
58
+ ? { kind: "brain.echo_dropped", version: 1, taskId: rs.telemetry.taskId, field: t.field, ts: Date.now() }
59
+ : { kind: "vision.placeholder", version: 1, taskId: rs.telemetry.taskId, count: t.count, ts: Date.now() });
58
60
  };
59
61
  const withBrainSinks = (fn) => runWithStatusSink(statusEmit, () => runWithBrainTelemetry(telemetryEmit, () => runWithReasoningWireFacts(observeReasoningWireFacts, fn)));
60
62
  return { subagentName, withBrainSinks };