@sema-agent/core 7.34.0 → 7.35.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 (83) hide show
  1. package/CHANGELOG.md +123 -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 +25 -3
  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.js +7 -4
  48. package/dist/core/runner/run-brain-sinks.js +3 -1
  49. package/dist/core/runner/run-leg.js +5 -3
  50. package/dist/core/runner/run-notification-lane.d.ts +3 -0
  51. package/dist/core/runner/run-notification-lane.js +2 -1
  52. package/dist/core/runner/runtask.d.ts +1 -0
  53. package/dist/core/runner/runtask.js +5 -11
  54. package/dist/core/runner/session-mode-seats.d.ts +48 -0
  55. package/dist/core/runner/session-mode-seats.js +73 -0
  56. package/dist/core/runner/session-rule-policy.js +3 -2
  57. package/dist/core/secret-env.d.ts +30 -58
  58. package/dist/core/secret-env.js +149 -586
  59. package/dist/core/sensitive-path-policy.js +2 -1
  60. package/dist/core/store-contracts/checkpoint-store-contract.js +2 -1
  61. package/dist/core/tool-policy.d.ts +31 -6
  62. package/dist/core/tool-policy.js +35 -16
  63. package/dist/core/tool-registry.d.ts +36 -0
  64. package/dist/core/tool-registry.js +22 -1
  65. package/dist/core/tool-roster.d.ts +17 -2
  66. package/dist/core/tool-roster.js +11 -1
  67. package/dist/core/trace.d.ts +11 -0
  68. package/dist/engine/execution-env/node-execution-env.d.ts +3 -1
  69. package/dist/engine/execution-env/node-execution-env.js +5 -2
  70. package/dist/engine/harness/agent-harness.js +2 -1
  71. package/dist/engine/lsp/node-lsp-manager.js +1 -1
  72. package/dist/index.d.ts +4 -4
  73. package/dist/index.js +4 -4
  74. package/dist/stores/file/index.d.ts +5 -3
  75. package/dist/stores/file/session-store.d.ts +8 -2
  76. package/dist/stores/file/session-store.js +5 -5
  77. package/dist/tools/fs/file-read-check.d.ts +2 -1
  78. package/dist/tools/fs/file-read-check.js +15 -7
  79. package/dist/tools/fs/shell-read-check.d.ts +10 -2
  80. package/dist/tools/fs/shell-read-check.js +12 -8
  81. package/package.json +1 -1
  82. package/test/export-surface.snapshot.json +15 -1
  83. package/wire-changes/core@7.35.0.json +584 -0
@@ -35,6 +35,7 @@ import type { InheritedGate, IrreversibilityTier, PrepareResume, RunInternals, S
35
35
  import { type Arbitration } from "../ask-origin.js";
36
36
  import { type ModeGateSeats, type PermissionModeController } from "../mode-policy.js";
37
37
  import { createRuleOffersOf, type PermissionRuleLanes } from "./permission-rule-lanes.js";
38
+ import type { ToolCallLike } from "../tool-registry.js";
38
39
  export interface PreparePolicyChainInput {
39
40
  /** borrowed-readonly — the REBOUND spec. Read: `sessionId` / `principal` (the session-rule read and the lanes' identity),
40
41
  * `hooks` (spec over deps), `durableApproval` / `checkpointStore` (the mandate-park facility test and the sandbox arming's park prerequisites), `taskId` (the late-ask observer's detail).
@@ -199,7 +200,7 @@ export interface PreparePolicyChainResult {
199
200
  isDelegatedChild?: true;
200
201
  };
201
202
  /** owned — the engine-judged risk axes for an ask, from the same resolved sources the safety tightens use. */
202
- riskAxesOf: (toolName: string) => {
203
+ riskAxesOf: (call: Pick<ToolCallLike, "toolName" | "face">) => {
203
204
  riskAxes?: {
204
205
  irreversible?: boolean;
205
206
  egress?: boolean;
@@ -214,7 +215,7 @@ export interface PreparePolicyChainResult {
214
215
  /** owned — whether the sandbox-admission mode armed (declared isolation with its park prerequisites). */
215
216
  sandboxAdmissionArmed: boolean;
216
217
  /** owned — the NOT-CONTAINED classification: a call the declared sandbox does not contain. */
217
- sandboxBoundaryCapable: (toolName: string) => boolean;
218
+ sandboxBoundaryCapable: (call: Pick<ToolCallLike, "toolName" | "face">) => boolean;
218
219
  /** owned — the admission disclosure's trace emitter (the gate station emits it once the fold settles). */
219
220
  emitSandboxAdmitted: (info: {
220
221
  toolCallId: string;
@@ -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
  }
@@ -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";
@@ -304,6 +305,7 @@ 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;
309
311
  const onceLedger = await loadAnnounceOnceLedger(session, resume?.seed.announcedListings, deps.onError);
@@ -403,7 +405,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
403
405
  const ownedEnv = ownedEnvSeat.current;
404
406
  const { taskRootFinal, rebaseRestoredPath } = await prepareWorkspaceRestore({ ownedEnv, deps, spec, internals, resume, taskRootInitial, abortController, sessionId });
405
407
  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 });
408
+ const { trackFileEdit, fileHistoryBoundary, restoredFilePaths, rewindNotes, fileHistoryEnabled, fileHistoryFact, historyScope, historyRoot, historyFs } = await prepareFileHistory({ deps, spec, request: fileHistoryRequest, internals, executionEnv, taskRootFinal, handsEnabled, sessionId, session, abortController });
407
409
  const { noteFileEdited, editedFilesSnapshot, harnessRef, toolProgressRef, skillScope, forwardEvent, reviewRequestRef, requestReview, stopRequestedRef, requestStopAfterTurn, bindPermissionModeSeat, enterPlanMode, subagentRetain, worktreeIsolation } = prepareRunRefs({ spec, internals, executionEnv, taskRootFinal });
408
410
  const legContextOverlay = () => runnerSelf?.contextOverlayOf?.(harnessRef.current?.getModelRef() ?? resolvedRole.modelRef);
409
411
  let resolvedReadFace;
@@ -411,7 +413,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
411
413
  let readDenyAdditionsNormalized = [];
412
414
  let handsLessResolvedFace;
413
415
  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 });
416
+ 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
417
  bindPermissionModeSeat(permissionMode);
416
418
  const announceRuleStoreSkipped = ruleStoreUnreadableNotice(deps.onNotice, { sessionId, runId });
417
419
  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 +752,16 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
750
752
  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
753
  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
754
  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 });
755
+ 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
756
  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
757
  const postureFrameRef = {};
756
758
  const { microCompact, charsPerToken } = prepareContextLane({ model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, postureFrameRef, requestLossyRef, trimPressureRef, contextOverlay: legContextOverlay, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}) });
757
759
  const preparedHolder = {};
758
760
  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
761
  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 } : {}) });
762
+ 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
763
  rollback.commit();
764
+ commitModeSeats(abortController.signal);
762
765
  const prepared = buildPrepared();
763
766
  preparedHolder.current = prepared;
764
767
  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 };