@sema-agent/core 7.40.1 → 7.41.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 (134) hide show
  1. package/CHANGELOG.md +188 -0
  2. package/dist/agents/agent-transcript-tool.d.ts +10 -2
  3. package/dist/agents/agent-transcript-tool.js +52 -26
  4. package/dist/agents/list-agents-tool.d.ts +17 -2
  5. package/dist/agents/list-agents-tool.js +28 -16
  6. package/dist/agents/peer-session-drain.d.ts +3 -1
  7. package/dist/agents/peer-session-drain.js +1 -0
  8. package/dist/agents/subagent-steps.d.ts +12 -2
  9. package/dist/agents/subagent-steps.js +8 -4
  10. package/dist/agents/subagent.d.ts +1 -17
  11. package/dist/agents/subagent.js +12 -24
  12. package/dist/core/agent-definition.d.ts +19 -40
  13. package/dist/core/agent-state.d.ts +91 -0
  14. package/dist/core/agent-state.js +79 -0
  15. package/dist/core/ask-origin.d.ts +28 -6
  16. package/dist/core/ask-origin.js +9 -5
  17. package/dist/core/ask-unresolvable-notice.d.ts +27 -4
  18. package/dist/core/ask-unresolvable-notice.js +8 -0
  19. package/dist/core/auto-promote.d.ts +14 -27
  20. package/dist/core/auto-promote.js +5 -5
  21. package/dist/core/checkpoint-store.d.ts +11 -12
  22. package/dist/core/decision-receipt.d.ts +5 -2
  23. package/dist/core/decision-receipt.js +1 -1
  24. package/dist/core/engine-notice.d.ts +7 -1
  25. package/dist/core/gate-crash-trace.d.ts +1 -1
  26. package/dist/core/gate-fold.js +7 -3
  27. package/dist/core/gate-lanes.js +22 -3
  28. package/dist/core/governance-codes.d.ts +3 -1
  29. package/dist/core/governance-codes.js +3 -0
  30. package/dist/core/hooks.d.ts +20 -6
  31. package/dist/core/hooks.js +28 -28
  32. package/dist/core/mcp-tool-meta.d.ts +24 -0
  33. package/dist/core/mcp-tool-meta.js +17 -0
  34. package/dist/core/mcp.d.ts +1 -6
  35. package/dist/core/mcp.js +6 -7
  36. package/dist/core/mode-policy.d.ts +25 -0
  37. package/dist/core/mode-policy.js +8 -0
  38. package/dist/core/protocol-table.d.ts +3 -0
  39. package/dist/core/protocol-table.js +3 -0
  40. package/dist/core/reminder-mint.d.ts +21 -1
  41. package/dist/core/reminder-mint.js +20 -0
  42. package/dist/core/retired-keys.d.ts +2 -1
  43. package/dist/core/retired-keys.js +13 -2
  44. package/dist/core/rule-denial-sentence.d.ts +2 -2
  45. package/dist/core/runner/call-hook-stations.d.ts +106 -0
  46. package/dist/core/runner/call-hook-stations.js +118 -0
  47. package/dist/core/runner/contracts.d.ts +35 -25
  48. package/dist/core/runner/gate-exit.d.ts +5 -1
  49. package/dist/core/runner/gate-exit.js +10 -6
  50. package/dist/core/runner/prepare-delegation-surface.d.ts +1 -0
  51. package/dist/core/runner/prepare-delegation-surface.js +4 -4
  52. package/dist/core/runner/prepare-gate-stations.d.ts +8 -3
  53. package/dist/core/runner/prepare-gate-stations.js +13 -70
  54. package/dist/core/runner/prepare-inherited-gate.d.ts +6 -3
  55. package/dist/core/runner/prepare-inherited-gate.js +5 -3
  56. package/dist/core/runner/prepare-park-ask.d.ts +4 -0
  57. package/dist/core/runner/prepare-park-ask.js +4 -1
  58. package/dist/core/runner/prepare-policy-chain.d.ts +6 -5
  59. package/dist/core/runner/prepare-policy-chain.js +14 -25
  60. package/dist/core/runner/prepare-project-context.d.ts +15 -16
  61. package/dist/core/runner/prepare-project-context.js +15 -11
  62. package/dist/core/runner/prepare-run-refs.d.ts +8 -8
  63. package/dist/core/runner/prepare-run-refs.js +3 -3
  64. package/dist/core/runner/prepare-safety-scan.js +3 -3
  65. package/dist/core/runner/prepare-suspend-saga.d.ts +2 -1
  66. package/dist/core/runner/prepare-suspend-saga.js +2 -1
  67. package/dist/core/runner/prepare-task.js +14 -12
  68. package/dist/core/runner/prepare-turn-wiring.d.ts +3 -1
  69. package/dist/core/runner/prepare-turn-wiring.js +16 -13
  70. package/dist/core/runner/resume-admission.js +2 -1
  71. package/dist/core/runner/resume-apply.js +12 -23
  72. package/dist/core/runner/resume-checkpoint-screen.js +3 -0
  73. package/dist/core/runner/run-leg.js +61 -47
  74. package/dist/core/runner/runtask.d.ts +1 -1
  75. package/dist/core/runner/session-rule-policy.d.ts +7 -19
  76. package/dist/core/runner/session-rule-policy.js +1 -2
  77. package/dist/core/runner/skill-base-dir.d.ts +10 -0
  78. package/dist/core/runner/skill-base-dir.js +4 -0
  79. package/dist/core/runner/synthetic-tools.d.ts +11 -17
  80. package/dist/core/runner/synthetic-tools.js +7 -8
  81. package/dist/core/safety-axis-vocab.d.ts +2 -2
  82. package/dist/core/safety-axis-vocab.js +2 -0
  83. package/dist/core/session-policy-store.d.ts +63 -11
  84. package/dist/core/session-policy-store.js +52 -6
  85. package/dist/core/skill-grant.d.ts +239 -0
  86. package/dist/core/skill-grant.js +313 -0
  87. package/dist/core/skills-directory.d.ts +5 -34
  88. package/dist/core/skills-directory.js +9 -58
  89. package/dist/core/task-registry-agent.d.ts +14 -2
  90. package/dist/core/task-registry-agent.js +23 -8
  91. package/dist/core/task-registry-shared.d.ts +6 -0
  92. package/dist/core/task-registry.d.ts +16 -0
  93. package/dist/core/task-registry.js +19 -6
  94. package/dist/core/task-spec.d.ts +10 -0
  95. package/dist/core/tool-face.d.ts +3 -2
  96. package/dist/core/tool-name-aliases.d.ts +38 -12
  97. package/dist/core/tool-name-aliases.js +16 -0
  98. package/dist/core/tool-name-readings.d.ts +6 -2
  99. package/dist/core/tool-name-readings.js +6 -4
  100. package/dist/core/tool-policy.d.ts +2 -2
  101. package/dist/core/tool-registry.d.ts +1 -4
  102. package/dist/core/tool-registry.js +0 -3
  103. package/dist/core/tool-spec.d.ts +11 -13
  104. package/dist/core/tools.d.ts +1 -1
  105. package/dist/core/trace.d.ts +15 -0
  106. package/dist/core/types.d.ts +1 -1
  107. package/dist/engine/harness/agent-harness.d.ts +12 -1
  108. package/dist/engine/harness/agent-harness.js +17 -13
  109. package/dist/engine/loop/agent-loop.d.ts +16 -1
  110. package/dist/engine/loop/agent-loop.js +9 -15
  111. package/dist/engine/loop/types.d.ts +2 -0
  112. package/dist/index.d.ts +9 -4
  113. package/dist/index.js +4 -2
  114. package/dist/stores/file/adoption/adopt.js +23 -16
  115. package/dist/stores/file/index.d.ts +8 -2
  116. package/dist/stores/file/index.js +2 -2
  117. package/dist/stores/file/mailbox-store.js +2 -2
  118. package/dist/stores/file/permission-rule-adopt.js +10 -1
  119. package/dist/stores/file/permission-rule-store.d.ts +6 -4
  120. package/dist/stores/file/permission-rule-store.js +127 -59
  121. package/dist/stores/file/session-policy-store.d.ts +86 -14
  122. package/dist/stores/file/session-policy-store.js +135 -118
  123. package/dist/stores/file/task-list-store.d.ts +5 -0
  124. package/dist/stores/file/task-list-store.js +18 -4
  125. package/dist/stores/file/usage-window-store.d.ts +9 -1
  126. package/dist/stores/file/usage-window-store.js +47 -33
  127. package/dist/tools/fs/safety.d.ts +1 -1
  128. package/package.json +1 -1
  129. package/test/export-surface.snapshot.json +45 -5
  130. package/wire-changes/core@7.41.0.json +1865 -0
  131. package/dist/core/runner/active-skill-scope.d.ts +0 -58
  132. package/dist/core/runner/active-skill-scope.js +0 -133
  133. package/dist/core/skill-tool-specifier.d.ts +0 -80
  134. package/dist/core/skill-tool-specifier.js +0 -57
@@ -79,7 +79,7 @@ function stampPlacementRootSessionId(placementRootResolved) {
79
79
  return placementValueOrAbsent(placementRootResolved);
80
80
  }
81
81
  export function prepareSuspendSaga(input) {
82
- const { 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 } = input;
82
+ const { gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, skillGrantCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, permissionMode, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter } = input;
83
83
  if (!gateMachineryActive)
84
84
  return { saga: undefined };
85
85
  const inFlightSpendMicroUsd = () => {
@@ -110,6 +110,7 @@ export function prepareSuspendSaga(input) {
110
110
  readFileState: readFileStateForCheckpoint ? [...readFileStateForCheckpoint.entries()] : undefined,
111
111
  readFace: faceCheckpointSection(),
112
112
  writeFace: writeFaceCheckpointSection(),
113
+ skillGrants: skillGrantCheckpointSection(),
113
114
  reminderMark,
114
115
  repairBundle: repairBundleForCheckpoint(parkedSpendMicroUsd),
115
116
  workspaceHandle,
@@ -20,7 +20,8 @@ import { persistedPathRowsOfTask, ruleStoreUnreadableNotice } from "./persisted-
20
20
  import { reservedCollisions, reservedFor } from "../../brain/request-params.js";
21
21
  import { STALL_FIRST_BYTE_MS, STALL_FIRST_TOKEN_MS, STALL_IDLE_MS, withBrainCallGuardrail } from "../../brain/timeout.js";
22
22
  import { OFFLOAD_TOOL_NAME } from "../tool-result-store.js";
23
- import { OUTPUT_TOOL_NAME, createOutputTool } from "./synthetic-tools.js";
23
+ import { OUTPUT_TOOL_NAME, SKILL_TOOL_NAME, createOutputTool } from "./synthetic-tools.js";
24
+ import { skillGateWiring } from "../skill-grant.js";
24
25
  import { compileOutputSchema } from "./strict-output-schema.js";
25
26
  import { MEMORY_ENGINE_TOOL_NAMES } from "../memory-engine/tools.js";
26
27
  import { foldAdmissionFreeze } from "../memory-admission.js";
@@ -396,14 +397,14 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
396
397
  const { taskRootFinal, rebaseRestoredPath } = await prepareWorkspaceRestore({ ownedEnv, deps, spec, internals, resume, taskRootInitial, abortController, sessionId });
397
398
  const { canonical: taskRootCanonical } = await resolveTaskRootCanonical({ env: executionEnv, given: taskRootFinal, onNotice: deps.onNotice, sessionId, runId });
398
399
  const { trackFileEdit, fileHistoryBoundary, restoredFilePaths, rewindNotes, fileHistoryEnabled, fileHistoryFact, historyScope, historyRoot, historyFs } = await prepareFileHistory({ deps, spec, request: fileHistoryRequest, internals, executionEnv, taskRootFinal, handsEnabled, sessionId, session, abortController });
399
- const { noteFileEdited, editedFilesSnapshot, harnessRef, toolProgressRef, skillScope, forwardEvent, reviewRequestRef, requestReview, stopRequestedRef, requestStopAfterTurn, bindPermissionModeSeat, enterPlanMode, subagentRetain, worktreeIsolation } = prepareRunRefs({ spec, internals, executionEnv, taskRootFinal });
400
+ const { noteFileEdited, editedFilesSnapshot, harnessRef, toolProgressRef, skillGrants, forwardEvent, reviewRequestRef, requestReview, stopRequestedRef, requestStopAfterTurn, bindPermissionModeSeat, enterPlanMode, subagentRetain, worktreeIsolation } = prepareRunRefs({ spec, internals, executionEnv, taskRootFinal });
400
401
  const legContextOverlay = () => runnerSelf?.contextOverlayOf?.(harnessRef.current?.getModelRef() ?? resolvedRole.modelRef);
401
402
  let resolvedReadFace;
402
403
  let resolvedWriteFace;
403
404
  let readDenyAdditionsNormalized = [];
404
405
  let handsLessResolvedFace;
405
406
  let handsLessResolvedWriteFace;
406
- 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 });
407
+ const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, permissionMode, commitModeSeats, endModeCeiling, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, skillGrantCheckpointSection, 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, skillGrants, 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 });
407
408
  bindPermissionModeSeat(permissionMode);
408
409
  const announceRuleStoreSkipped = ruleStoreUnreadableNotice(deps.onNotice, { sessionId, runId });
409
410
  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 });
@@ -558,7 +559,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
558
559
  handsLessResolvedWriteFace = handsReadFace.handsLessResolvedWriteFace;
559
560
  shellGatedTools = handsReadFace.shellGatedTools;
560
561
  await armAutoMode();
561
- const { worktreeSessionRef } = prepareDelegationSurface({ spec, deps, internals, resume, sessionId, hostTaskId, taskScope, taskRootFinal, taskRootCanonical, executionEnv, runnerSelf, backgroundTaskToolsActive, shellReadAdmission, workflowToolsActive, peerLaneActive, peerSendMessageBuiltIn, peerLaneRefs, sessionBox, roster, toolEffects, axisExplicitNegatives, envHandToolNames, toolFaceSnapshot, firstPartyOffload, offloadStore, subagentRetain, enrichSpecToolCtx, handsCwdRef, wsSnapshot, rebaseWsPath, rebaseRestoredPath, workspaceStateSettle });
562
+ const { worktreeSessionRef } = prepareDelegationSurface({ spec, deps, internals, resume, sessionId, hostTaskId, taskScope, taskRootFinal, taskRootCanonical, executionEnv, runnerSelf, backgroundTaskToolsActive, shellReadAdmission, workflowToolsActive, peerLaneActive, peerSendMessageBuiltIn, peerLaneRefs, sessionBox, roster, toolEffects, axisExplicitNegatives, envHandToolNames, toolFaceSnapshot, firstPartyOffload, offloadStore, subagentRetain, enrichSpecToolCtx, checkpointSeat, handsCwdRef, wsSnapshot, rebaseWsPath, rebaseRestoredPath, workspaceStateSettle });
562
563
  const { liveQuestionFace, contentAskBindings, contentAskRoutable, lateStrandedAnswers, discloseStrandedAnswers, settleContentAskBindings, durableQuestionFace, questionToolMounted, mountedQuestionTool } = prepareQuestionFace({ frozenOnQuestion, spec, deps, checkpointStore, runtimeCaps, resolvedInteractionPosture, resume, sessionId, roster, inheritedUnavailableAsks: () => inheritedUnavailableAsks });
563
564
  if (spec.handsReadOnly !== true) {
564
565
  const sessionScope = sessionId ?? spec.principal ?? spec.taskId ?? "default";
@@ -615,8 +616,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
615
616
  deps.onError?.(asThrownError(err), { phase: "config", sessionId });
616
617
  }
617
618
  }
618
- const projectContext = await prepareProjectContext({ deps, spec, internals, session, sessionId, hostTaskId, taskRootFinal, handsEnabled, delegation, seedContextFiles, memoryBlockFromEngine, skillScope, roster, toolFaceSnapshot, onceLedger, memoryTools, memoryEngineSession, memorySearchToolsPlanned, memoryPairExcludedName, memoryPairOccupiedName, resume, skillListingBudget: doors.skillListingBudget });
619
- const { memoryBlock: memoryBlockWithProject, instructionSources, projectInstructionContent, hasSkillManifest, skillsListing, sharedMemoryPairMounted, memoryEnginePairMounted } = projectContext;
619
+ const projectContext = await prepareProjectContext({ deps, spec, internals, session, sessionId, hostTaskId, taskRootFinal, handsEnabled, delegation, seedContextFiles, memoryBlockFromEngine, skillGrants, roster, toolFaceSnapshot, onceLedger, memoryTools, memoryEngineSession, memorySearchToolsPlanned, memoryPairExcludedName, memoryPairOccupiedName, resume, skillListingBudget: doors.skillListingBudget });
620
+ const { memoryBlock: memoryBlockWithProject, instructionSources, projectInstructionContent, skillSpecs, skillSeatOf, skillsListing, sharedMemoryPairMounted, memoryEnginePairMounted } = projectContext;
620
621
  const { provider, featureFlags, userSystemPrompt, userAppendSystemPrompt, mcpInstructionsBlock, envFacts, tzValid, userTz, dateAnchor, gitStatusRef } = await preparePromptInputs({ spec, deps, contextOverlay: legContextOverlay(), failClosedReason: selfOrchestrationFailClosedReason(spec, deps), lockedPreflight, selfOrchestrationActive, executionEnv, reminderMark, resolvedReadFace, resolvedWriteFace: resolvedWriteFace?.inForce(), toolFaceSnapshot, promptProfile, fableMitigations, internals, thinking, ownedEnv, delegation, resolvedRole, mcp, model, externalContentTargetActive, resume, handsEnabled, taskRootFinal, additionalRootsCanonical, additionalReadRootsCanonical, sessionId, session, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}) });
621
622
  const { deferred, memoryBlock, toolSearch: toolSearchPolicy } = prepareDeferClassify({ spec, deps, roster, toolFaceSnapshot, handsEnabled, fullShellReachable, promptProfile, model, mcp, a2a, sharedMemoryPairMounted, memoryEnginePairMounted, memoryBlock: memoryBlockWithProject, memoryRecallSegment, onceLedger, sessionId });
622
623
  prepareMemoryEngineSession({ memoryEngineSession, internals, memoryDelegationEvidence, externalContentTargetActive, lockedPreflight, mcp, sessionId, runId, deps, provenanceForChildrenRef, delegationSettlementRef, spec, envHandToolNames, roster, contentOriginWrapRef });
@@ -729,7 +730,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
729
730
  releaseSignal = () => sig.removeEventListener("abort", onExternalAbort);
730
731
  }
731
732
  }
732
- const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions, ancestorArbitrations } = await preparePolicyChain({ spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, toolMarkSources, envHandToolNames, handsCwdRef, readDenyMatcher, announceRuleStoreSkipped, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, hasSkillManifest, skillScope, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider: () => autoModeDeciderRef.current, permissionMode, scratchpadDir: spec.envFacts?.scratchpadDir, toolRosterDeltas: rosterSeat, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
733
+ const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, skillGrantLane, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions, ancestorArbitrations } = await preparePolicyChain({ spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, toolMarkSources, envHandToolNames, handsCwdRef, readDenyMatcher, announceRuleStoreSkipped, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, skillGrants, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider: () => autoModeDeciderRef.current, permissionMode, scratchpadDir: spec.envFacts?.scratchpadDir, toolRosterDeltas: rosterSeat, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
733
734
  const { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, durableApproval, wiringManifest, writeProtection, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace, sessionBoxBound } = prepareWiringManifest({ spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, shellReadStationArmed: handsReadFace.shellReadStationArmed, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, permissionMode, lockedPreflight, peerLaneRefs, frameSettlement, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive: handsReadFace.readDenyBuiltinTiersActive, lspManagerWired, lspDiagnosticsLive: lspDiagnostics !== undefined, fileHistoryFact, sessionBox });
734
735
  const writeProtectionCheck = writeProtection?.check;
735
736
  writeProtectionJudgeRef.current = writeProtection?.judgeTarget;
@@ -740,16 +741,17 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
740
741
  });
741
742
  }
742
743
  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 });
743
- 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 });
744
+ const { saga } = prepareSuspendSaga({ gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, skillGrantCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, permissionMode, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter });
744
745
  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 });
745
- 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, toolMarkSources, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, pausedRef });
746
- 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, effective: askSeatEffective(wiringManifest) }, permissionMode, toolMarkSources, ...(deps.a2a?.isolatePeers === true ? { a2aIsolatePeers: true } : {}), ancestorArbitrations, runId, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash, frameSettlement });
746
+ 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, toolMarkSources, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, skillSpecs, now, humanReviewRef, abortController, harness, pausedRef });
747
+ const seatInputsFor = skillGateWiring({ tools, skillToolName: SKILL_TOOL_NAME, seatOf: skillSeatOf, mode: () => permissionMode.current, lane: skillGrantLane, onAllowed: (info) => emitTrace(deps.tracer, () => ({ kind: "permission.skill_allowed", version: 1, taskId: hostTaskId, ...info, entries: [...info.entries], ts: Date.now() })) });
748
+ const { approvedCallScreen } = 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, effective: askSeatEffective(wiringManifest) }, permissionMode, toolMarkSources, ...(deps.a2a?.isolatePeers === true ? { a2aIsolatePeers: true } : {}), ancestorArbitrations, runId, stopForDenialLimit, permissionRuleLane, seatInputsFor, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash, frameSettlement });
747
749
  const postureFrameRef = { report: (err) => deps.onError?.(err, { phase: "degraded", sessionId }) };
748
750
  const { microCompact, charsPerToken } = prepareContextLane({ model, deps, sessionId, runId, hostTaskId, offloadStore, toolRosterDeltas: rosterSeat, microCompactKnob, harness, batchHaltRef, gitStatusRef, postureFrameRef, requestLossyRef, trimPressureRef, contextOverlay: legContextOverlay, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}) });
749
751
  const preparedHolder = {};
750
- 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 });
752
+ 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, skillGrants: skillGrantLane, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder });
751
753
  await onceLedger.settle(session, announcedListingsRef);
752
- const buildPrepared = () => ({ harness, session, sessionId, runId, registryAccess: { owner: hostTaskId, scope: taskScope }, reminderMark, reminderDisclosureCounts, fullShellReachable, approvalPosture: { get mode() { return permissionMode.current; }, askEffective: askSeatEffective(wiringManifest) }, ...(outputStyleFrame !== undefined ? { outputStyleFrame } : {}), 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 } : {}) });
754
+ const buildPrepared = () => ({ harness, session, sessionId, runId, registryAccess: { owner: hostTaskId, scope: taskScope }, reminderMark, reminderDisclosureCounts, fullShellReachable, approvalPosture: { get mode() { return permissionMode.current; }, askEffective: askSeatEffective(wiringManifest) }, ...(outputStyleFrame !== undefined ? { outputStyleFrame } : {}), 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, skillGrants: skillGrantLane, skillSpecs, ...(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 } : {}), ...(approvedCallScreen !== undefined ? { approvedCallScreen } : {}) });
753
755
  rollback.commit();
754
756
  commitModeSeats(abortController.signal);
755
757
  const prepared = buildPrepared();
@@ -78,8 +78,10 @@ export interface PrepareTurnWiringInput {
78
78
  /** borrowed-mutable — the live read-file state, or undefined. Writers here, at RUN time: `detectExternalChanges` evicts a
79
79
  * not-found entry; `onCompactionApplied` applies a landed compaction to it. The hands phase mints it. */
80
80
  readFileStateForCheckpoint: ReadFileState | undefined;
81
- /** borrowed-readonly — the deny-narrowing fold, or undefined (the changed-files deny door's authority). */
81
+ /** borrowed-readonly — the deny-narrowing fold, or undefined (one of the changed-files deny door's two authorities). */
82
82
  denyNarrowingPolicy: ToolPolicy | undefined;
83
+ /** borrowed-readonly — the skill lane: the run's skill deny rows (the deny door's other authority; only `denying` is read). */
84
+ skillGrants: Prepared["skillGrants"];
83
85
  /** borrowed-readonly — the run's abort controller; only `signal` (the deny door's and the stat's signal). */
84
86
  abortController: Pick<AbortController, "signal">;
85
87
  /** borrowed-readonly — the per-task identity (the background listing's owner). */
@@ -12,7 +12,7 @@ import { PROMPT_HASH_SALT } from "./prompt-hash-salt.js";
12
12
  import { CHANGED_FILES_MTIME_EPS_MS } from "./turn-attachments.js";
13
13
  import { thrownText } from "../thrown-value.js";
14
14
  export function prepareTurnWiring(input) {
15
- const { 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 } = input;
15
+ const { 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, skillGrants, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder } = input;
16
16
  const cacheBreakDetector = deps.cacheBreakDetection === false ? undefined : new CacheBreakDetector();
17
17
  const cacheFingerprint = cacheBreakDetector
18
18
  ? { systemPrompt: systemPromptSeat.current, tools: toolsToFingerprintInputs(harnessTools) }
@@ -101,17 +101,20 @@ export function prepareTurnWiring(input) {
101
101
  const onCompactionApplied = handsEnabled && readFileStateForCheckpoint
102
102
  ? (attachedComplete, preserveReadState) => applyContextShrinkToReadFileState(readFileStateForCheckpoint, { kind: "summarized", attachedComplete, ...(preserveReadState !== undefined ? { preserveKeys: preserveReadState } : {}) })
103
103
  : undefined;
104
- const changedFilesReadDenied = denyNarrowingPolicy === undefined
105
- ? undefined
106
- : async (path) => {
107
- try {
108
- const d = refuseOutOfContractDecision(await denyNarrowingPolicy.check({ toolName: "Read", args: { file_path: path }, toolCallId: "changed-files-visibility-probe" }, abortController.signal));
109
- return d.action === "deny";
110
- }
111
- catch {
112
- return false;
113
- }
114
- };
104
+ const changedFilesReadDenied = async (path) => {
105
+ const probe = { toolName: "Read", args: { file_path: path }, toolCallId: "changed-files-visibility-probe" };
106
+ if (skillGrants.denying(probe) !== undefined)
107
+ return true;
108
+ if (denyNarrowingPolicy === undefined)
109
+ return false;
110
+ try {
111
+ const d = refuseOutOfContractDecision(await denyNarrowingPolicy.check(probe, abortController.signal));
112
+ return d.action === "deny";
113
+ }
114
+ catch {
115
+ return false;
116
+ }
117
+ };
115
118
  const detectExternalChanges = handsEnabled && readFileStateForCheckpoint
116
119
  ? async (maxFiles) => {
117
120
  const candidates = [...readFileStateForCheckpoint.entries()]
@@ -122,7 +125,7 @@ export function prepareTurnWiring(input) {
122
125
  const changed = [];
123
126
  const evicted = [];
124
127
  for (const [path, entry] of candidates) {
125
- if (changedFilesReadDenied !== undefined && (await changedFilesReadDenied(path)))
128
+ if (await changedFilesReadDenied(path))
126
129
  continue;
127
130
  try {
128
131
  const info = await executionEnv.fileInfo(path, abortController.signal);
@@ -1,13 +1,14 @@
1
1
  import { CheckpointError, readPendingSteerQueue, refuseRetiredModeSeed, resolveCheckpointStore, validatePendingSteer } from "../checkpoint-store.js";
2
2
  import { refuseResumeDoors } from "../mode-policy.js";
3
3
  import { describeSuppliedValue } from "./decide-continuation.js";
4
+ import { detachSkillLists } from "../skill-grant.js";
4
5
  export async function resumeAdmission(input) {
5
6
  const { token, outcome, taskConfig, runner, parentConstraintRegistry } = input;
6
7
  let internals = input.internals;
7
8
  if (outcome === null || typeof outcome !== "object") {
8
9
  throw new CheckpointError("checkpoint.invalid_outcome", `resume outcome must be an object naming its gate (got ${outcome === null ? "null" : typeof outcome})`);
9
10
  }
10
- const config = typeof taskConfig === "object" && taskConfig !== null ? { ...taskConfig } : taskConfig;
11
+ const config = typeof taskConfig === "object" && taskConfig !== null ? { ...taskConfig, ...(Array.isArray(taskConfig.skills) ? { skills: detachSkillLists(taskConfig.skills) } : {}) } : taskConfig;
11
12
  internals = internals !== undefined ? { ...internals } : undefined;
12
13
  const store = resolveCheckpointStore(config, runner.deps);
13
14
  if (!store) {
@@ -3,15 +3,19 @@ import { isAskOrigin } from "../ask-origin.js";
3
3
  import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
4
4
  import { CheckpointError, remainingBudgetMicroUsd } from "../checkpoint-store.js";
5
5
  import { settledByDecider } from "../gate-outcome.js";
6
- import { cloneObserverInput, mintHookInvocationIdentity, resolveHookTimeoutMs, runHookSeat } from "../hooks.js";
6
+ import { mintHookInvocationIdentity, resolveHookTimeoutMs, runHookSeat } from "../hooks.js";
7
7
  import { checkToolPolicyProjection, policyCannotAnswer, refuseOutOfContractDecision } from "../tool-policy.js";
8
8
  import { callFaceSeat, parkedCallFaceOf } from "../tool-roster.js";
9
9
  import { inlineUntrusted } from "../untrusted-text.js";
10
10
  import { deepJsonEqual, describeSuppliedValue } from "./decide-continuation.js";
11
11
  import { orgVerdictBeforeClaim } from "./permission-rule-lanes.js";
12
12
  import { mintGateOutcome } from "./gate-exit.js";
13
+ import { chainCannotJudge } from "./call-hook-stations.js";
13
14
  import { asThrownError, thrownText } from "../thrown-value.js";
14
15
  import { wireOf } from "../tool-name-readings.js";
16
+ import { parkedSkillApprovalRefusal } from "../skill-grant.js";
17
+ import { SKILL_CONTENT_MAX_CHARS, SKILL_TOOL_NAME, normalizeSkills } from "./synthetic-tools.js";
18
+ import { skillLoadReading } from "./skill-base-dir.js";
15
19
  export async function resumeApply(input) {
16
20
  const { cp, config, internals, outcomeGate, wakeMessage, resumeSignal, recheckGovernanceWindow, plainReviewOutcome, plainParkOutcome, runner, effectiveDelegationFacts } = input;
17
21
  let plainPolicyOutcome = input.plainPolicyOutcome;
@@ -89,6 +93,12 @@ export async function resumeApply(input) {
89
93
  p.then((v) => { editSignal.removeEventListener("abort", onAbort); resolve(v); }, (e) => { editSignal.removeEventListener("abort", onAbort); reject(e); });
90
94
  });
91
95
  };
96
+ if (plainPolicyOutcome?.decision === "allow" && cp.pendingAction.kind === "tool_approval") {
97
+ const servedSkills = normalizeSkills(config.skills).filter((s) => skillLoadReading(s, SKILL_CONTENT_MAX_CHARS).loads);
98
+ const skillRefusal = parkedSkillApprovalRefusal(cp.pendingAction, plainPolicyOutcome.updatedInput ?? cp.pendingAction.args, servedSkills, SKILL_TOOL_NAME);
99
+ if (skillRefusal !== undefined)
100
+ throw new CheckpointError("checkpoint.invalid_outcome", skillRefusal, { field: "boundGrantDigest" });
101
+ }
92
102
  if (plainPolicyOutcome !== undefined &&
93
103
  plainPolicyOutcome.decision === "allow" &&
94
104
  plainPolicyOutcome.updatedInput !== undefined &&
@@ -111,13 +121,7 @@ export async function resumeApply(input) {
111
121
  const ig = cp.state.inheritedGate;
112
122
  if (ig?.requiresParentConstraint === true) {
113
123
  const chain = ig.constraintChain;
114
- const unprojectable = chain === undefined
115
- ? "the checkpoint predates the frozen projection chain (no constraintChain recorded)"
116
- : chain.some((e) => "opaque" in e)
117
- ? "the inherited chain carries an OPAQUE layer (no projection exported)"
118
- : chain.some((e) => !("opaque" in e) && e.requiresLiveRemainder)
119
- ? "an inherited chain layer declares a live remainder beyond its projection"
120
- : undefined;
124
+ const unprojectable = chainCannotJudge(ig);
121
125
  if (unprojectable !== undefined) {
122
126
  throw new CheckpointError("resume.constraint_unprojectable", `the approver EDITED this call's input, but the inherited ancestor chain cannot re-adjudicate the edit statically: ${unprojectable}. ` +
123
127
  "Refusing the edited resume pre-CAS (the checkpoint stays pending and remains decidable) — approve the shown input as-is, deny it, " +
@@ -156,21 +160,6 @@ export async function resumeApply(input) {
156
160
  `refused pre-CAS (the checkpoint stays pending); re-submit the edited action so the rewrite is adjudicated and approved synchronously`);
157
161
  }
158
162
  }
159
- const resumeHooks = config.hooks ?? runner.deps.hooks;
160
- if (resumeHooks?.preToolUse !== undefined && resumeHooks.preToolUseObservational === true) {
161
- try {
162
- await raceEditAbort(Promise.resolve(resumeHooks.preToolUse(cp.pendingAction.toolName, cloneObserverInput(editedArgs), {
163
- toolCallId: cp.pendingAction.toolCallId,
164
- toolName: cp.pendingAction.toolName,
165
- })));
166
- }
167
- catch (err) {
168
- if (err instanceof CheckpointError)
169
- throw err;
170
- throw new CheckpointError("resume.constraint_rejected", `the declared-observational PreToolUse face threw while observing the approver's edited args (${thrownText(err)}) — ` +
171
- "refused fail-closed pre-CAS (the checkpoint stays pending and remains decidable)");
172
- }
173
- }
174
163
  }
175
164
  const orgProvider = runner.deps.permissionRuleStore;
176
165
  if (plainPolicyOutcome?.decision === "allow" && plainPolicyOutcome.hostDecision.decidedBy !== "person" && orgProvider?.partitions.org === true && cp.pendingAction.kind === "tool_approval") {
@@ -2,6 +2,7 @@ import { persistedReadDenyEntryProblem } from "../../tools/fs/read-deny.js";
2
2
  import { isAskOrigin } from "../ask-origin.js";
3
3
  import { CHECKPOINT_FEATURES, CheckpointError, checkpointVersionOf, MAX_SUPPORTED_CHECKPOINT_VERSION, realApprovalOrgFact } from "../checkpoint-store.js";
4
4
  import { constraintChainDigest, constraintChainEntryOfSuppliedLayer } from "../tool-policy.js";
5
+ import { readFrozenSkillGrants } from "../skill-grant.js";
5
6
  export function resumeCheckpointScreen(input) {
6
7
  const { cp, resumeSignal, internals, runner } = input;
7
8
  const { realApproval: realApprovalRow, face: faceRow, f012: f012Row } = CHECKPOINT_FEATURES;
@@ -46,6 +47,8 @@ export function resumeCheckpointScreen(input) {
46
47
  throw new CheckpointError("checkpoint.invalid_outcome", `this checkpoint carries a write-face section (state.writeFace) that is not well-formed — refusing to resume a damaged write-face row (corruption guard), the checkpoint stays pending`, { reason: "write_face_damaged" });
47
48
  }
48
49
  }
50
+ if (readFrozenSkillGrants(cp.state.skillGrants) === undefined)
51
+ throw new CheckpointError("checkpoint.invalid_outcome", "this checkpoint carries a skill-grant section (state.skillGrants) that does not read as grant rows — refusing to resume without the grant rows and refusals it carries (corruption guard), the checkpoint stays pending", { reason: "skill_grants_damaged" });
49
52
  }
50
53
  if (realApprovalOrgFact(preCasGateBit) !== undefined && runner.deps.permissionRuleStore?.partitions.org !== true) {
51
54
  throw new CheckpointError("checkpoint.unsupported_version", `this checkpoint's approval was minted under organization governance (org ${String(realApprovalOrgFact(preCasGateBit))}, routed as ${String(preCasGateBit?.origin)}) and this worker has no org adjudication wiring (its permission-rule store carries no org partition) — a governed approval may only be redeemed where governance can be enforced; the checkpoint stays pending, resume it on an org-wired worker`, { reason: "governed_unwired" });
@@ -8,6 +8,8 @@ import { formatHookFeedback, hookSeatExpiredError, runHookSeat } from "../hooks.
8
8
  import { buildAppliedHumanInputEvent, buildHumanInputEvent, projectHumanInput } from "../human-input-projection.js";
9
9
  import { ORG_ADJUDICATION_TIMEOUT_MS, settleOrgVerdictWithin } from "../permission-rule-org.js";
10
10
  import { applyPersistedTightening, disclosedRuleSet } from "../persisted-rule-arms.js";
11
+ import { parkedSkillApprovalRefusal, skillRowRefusal } from "../skill-grant.js";
12
+ import { SKILL_TOOL_NAME } from "./synthetic-tools.js";
11
13
  import { computeCostMicroUsd } from "../pricing.js";
12
14
  import { mintSystemReminder } from "../reminder-mint.js";
13
15
  import { hasDestroy } from "../remote-env.js";
@@ -19,6 +21,7 @@ import { errorCodeOf } from "./assemble-result.js";
19
21
  import { DEFAULT_PRECALL_OUTPUT_TOKENS, platformLimitTerminal } from "./clock-and-limits.js";
20
22
  import { mintOutputStyleReminder, owedPostureText } from "./compaction-restate.js";
21
23
  import { deepJsonEqual, resumeContinuation } from "./decide-continuation.js";
24
+ import { chainCannotJudge } from "./call-hook-stations.js";
22
25
  import { settleExecutionRecord } from "./execution-record.js";
23
26
  import { holdPrompt } from "./prompt-block.js";
24
27
  import { mintGateOutcome } from "./gate-exit.js";
@@ -565,7 +568,8 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
565
568
  if (decidedGate === undefined) {
566
569
  throw new CheckpointError("checkpoint.invalid_outcome", "pending action reached the resolver with no settlement record on its decision (gateOutcome) — refusing to execute or settle a decision that carries no record");
567
570
  }
568
- const resolvedArgs = wireOf("resume.questionArgsShown", pendingAction) === ASK_USER_QUESTION_TOOL_NAME ? pendingAction.args : (outcome.updatedInput !== undefined ? outcome.updatedInput : pendingAction.args);
571
+ const humanRewrite = wireOf("resume.questionArgsShown", pendingAction) === ASK_USER_QUESTION_TOOL_NAME ? undefined : outcome.updatedInput;
572
+ const resolvedArgs = humanRewrite !== undefined ? humanRewrite : pendingAction.args;
569
573
  const pendingLabel = (() => { const l = prepared.tools.find((t) => t.name === pendingAction.toolName)?.label; return l !== undefined && l !== pendingAction.toolName ? { label: l } : {}; })();
570
574
  const spelledName = pendingAction.names?.[0] ?? pendingAction.toolName;
571
575
  emit({ type: "tool_start", toolCallId: pendingAction.toolCallId, toolName: spelledName, ...pendingLabel, args: resolvedArgs });
@@ -601,32 +605,41 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
601
605
  if (outcome.decision !== "allow") {
602
606
  throw new CheckpointError("checkpoint.invalid_outcome", `pending action reached the resolver with decision "${String(outcome.decision)}" — only "allow" executes and only "deny" injects a denial; refusing to execute`);
603
607
  }
608
+ const screened = await prepared.approvedCallScreen?.({ toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, args: structuredClone(resolvedArgs), decidedBy: outcome.hostDecision.decidedBy });
609
+ if (screened !== undefined && "refused" in screened) {
610
+ const hookDenial = formatHookFeedback(screened.refused, prepared.reminderMark);
611
+ await settleEnd(true, { content: hookDenial }, vetoed("hook"));
612
+ emitCommitted(await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, hookDenial, true)), "toolResult", pendingAction.toolCallId);
613
+ return;
614
+ }
615
+ const executedArgs = humanRewrite !== undefined ? resolvedArgs : screened !== undefined ? screened.args : resolvedArgs;
616
+ const unseen = !deepJsonEqual(executedArgs, pendingAction.args);
617
+ const beltReq = () => ({ toolName: pendingAction.toolName, args: executedArgs, toolCallId: pendingAction.toolCallId, ...callFaceSeat(parkedRowLiveFace(prepared.toolRosterDeltas.current, pendingAction)), budget: beltBudget, ...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}), ...advertisedWritableRootsSeat(prepared.advertisedWritableDirs) });
618
+ const unjudgeable = unseen ? chainCannotJudge(resume.cp.state.inheritedGate) : undefined;
619
+ if (unjudgeable !== undefined) {
620
+ const chainDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: its input changed after the approval and the inherited ancestor chain cannot judge the changed input (${unjudgeable}). This approval is spent — the call has to be re-issued.`, prepared.reminderMark);
621
+ await settleEnd(true, { content: chainDenial }, vetoed("policy"));
622
+ emitCommitted(await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, chainDenial, true)), "toolResult", pendingAction.toolCallId);
623
+ return;
624
+ }
604
625
  const beltBudget = {
605
626
  resourceRemainingMicroUsd: remainingBudgetMicroUsd(prepared.resourceLedger),
606
627
  resourceSpentMicroUsd: prepared.resourceLedger?.spentMicroUsd ?? 0,
607
628
  suspendCount: resume.cp.suspendCount ?? 0,
608
629
  };
609
- if (outcome.decision === "allow" && outcome.updatedInput !== undefined && prepared.basePolicyForResumeEdit) {
610
- const rechecked = refuseOutOfContractDecision(await prepared.basePolicyForResumeEdit.check({
611
- toolName: pendingAction.toolName,
612
- args: resolvedArgs,
613
- toolCallId: pendingAction.toolCallId,
614
- budget: beltBudget,
615
- ...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
616
- ...advertisedWritableRootsSeat(prepared.advertisedWritableDirs),
617
- ...callFaceSeat(parkedRowLiveFace(prepared.toolRosterDeltas.current, pendingAction)),
618
- }, prepared.abortController.signal));
630
+ if (unseen && prepared.basePolicyForResumeEdit) {
631
+ const rechecked = refuseOutOfContractDecision(await prepared.basePolicyForResumeEdit.check(beltReq(), prepared.abortController.signal));
619
632
  const personUnanswered = policyCannotAnswer(rechecked, outcome.hostDecision.decidedBy);
620
633
  const beltUnsafe = rechecked.action === "deny" ||
621
- (rechecked.updatedInput !== undefined && !deepJsonEqual(rechecked.updatedInput, resolvedArgs)) ||
634
+ (rechecked.updatedInput !== undefined && !deepJsonEqual(rechecked.updatedInput, executedArgs)) ||
622
635
  personUnanswered;
623
636
  if (beltUnsafe) {
624
637
  const why = rechecked.action === "deny"
625
638
  ? (rechecked.message ? `: ${rechecked.message}` : "")
626
639
  : personUnanswered
627
- ? " (the edited call requires a person's approval, and this decision was the deployment's policy)"
628
- : " (the policy rewrote the edit to a value that was not approved)";
629
- const editedDenial = formatHookFeedback(`The approver EDITED this call's input; the edited call is refused by the deployment's tool policy and was not executed${why}.`, prepared.reminderMark);
640
+ ? " (the call as it would run requires a person's approval, and this decision was the deployment's policy)"
641
+ : " (the policy rewrote these bytes to a value that was not approved)";
642
+ const editedDenial = formatHookFeedback(`${outcome.updatedInput !== undefined ? "The approver EDITED this call's input; the edited call" : "A PreToolUse hook REWROTE this approved call's input; the rewritten call"} is refused by the deployment's tool policy and was not executed${why}.`, prepared.reminderMark);
630
643
  await settleEnd(true, { content: editedDenial }, vetoed("policy"));
631
644
  const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, editedDenial, true));
632
645
  emitCommitted(eid, "toolResult", pendingAction.toolCallId);
@@ -644,7 +657,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
644
657
  }
645
658
  if (prepared.permissionRuleOrg !== undefined) {
646
659
  const orgVerdict = prepared.permissionRuleOrg
647
- .adjudicate({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId, ...callFaceSeat(parkedRowLiveFace(prepared.toolRosterDeltas.current, pendingAction)) })
660
+ .adjudicate({ toolName: pendingAction.toolName, args: executedArgs, toolCallId: pendingAction.toolCallId, ...callFaceSeat(parkedRowLiveFace(prepared.toolRosterDeltas.current, pendingAction)) })
648
661
  .catch(() => ({ status: "unavailable", disclosures: ["the org adjudication face threw on resume"] }));
649
662
  let orgWaitCancelled = false;
650
663
  const org = await settleOrgVerdictWithin(orgVerdict, { status: "unavailable", disclosures: [`the org adjudication face did not answer within ${ORG_ADJUDICATION_TIMEOUT_MS}ms (or the task ended first)`] }, {
@@ -678,15 +691,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
678
691
  }
679
692
  }
680
693
  if (prepared.denyNarrowingPolicy) {
681
- const narrowed = refuseOutOfContractDecision(await prepared.denyNarrowingPolicy.check({
682
- toolName: pendingAction.toolName,
683
- args: resolvedArgs,
684
- toolCallId: pendingAction.toolCallId,
685
- ...callFaceSeat(parkedRowLiveFace(prepared.toolRosterDeltas.current, pendingAction)),
686
- budget: beltBudget,
687
- ...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
688
- ...advertisedWritableRootsSeat(prepared.advertisedWritableDirs),
689
- }, prepared.abortController.signal));
694
+ const narrowed = refuseOutOfContractDecision(await prepared.denyNarrowingPolicy.check(beltReq(), prepared.abortController.signal));
690
695
  if (narrowed.action === "deny") {
691
696
  const narrowedDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" is now denied by ${ruleSourcePhrase("narrowing")} and was not executed${narrowed.message ? `: ${narrowed.message}` : ""}.`, prepared.reminderMark);
692
697
  await settleEnd(true, { content: narrowedDenial }, vetoed("policy"));
@@ -695,20 +700,19 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
695
700
  return;
696
701
  }
697
702
  }
703
+ const skillRefusal = skillRowRefusal(prepared.skillGrants, beltReq());
704
+ if (skillRefusal !== undefined) {
705
+ const skillDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: ${skillRefusal.message}. This approval is spent — the call has to be re-issued.`, prepared.reminderMark);
706
+ await settleEnd(true, { content: skillDenial }, vetoed("policy"));
707
+ emitCommitted(await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, skillDenial, true)), "toolResult", pendingAction.toolCallId);
708
+ return;
709
+ }
698
710
  if (prepared.persistedRuleLane !== undefined) {
699
711
  const unreadable = { unreadable: true };
700
712
  let laneWaitCancelled = false;
701
713
  let laneRead;
702
714
  try {
703
- laneRead = Promise.resolve(prepared.persistedRuleLane.adjudicate({
704
- toolName: pendingAction.toolName,
705
- args: resolvedArgs,
706
- toolCallId: pendingAction.toolCallId,
707
- ...callFaceSeat(parkedRowLiveFace(prepared.toolRosterDeltas.current, pendingAction)),
708
- budget: beltBudget,
709
- ...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
710
- ...advertisedWritableRootsSeat(prepared.advertisedWritableDirs),
711
- })).then((answer) => normalizePersistedRuleHit(answer));
715
+ laneRead = Promise.resolve(prepared.persistedRuleLane.adjudicate(beltReq())).then((answer) => normalizePersistedRuleHit(answer));
712
716
  }
713
717
  catch {
714
718
  laneRead = Promise.resolve(unreadable);
@@ -729,11 +733,10 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
729
733
  }
730
734
  }
731
735
  if (prepared.shellReadCheck !== undefined) {
732
- const edited = outcome.updatedInput !== undefined && !deepJsonEqual(outcome.updatedInput, pendingAction.args);
733
736
  const readCall = { toolName: pendingAction.toolName, ...callFaceSeat(parkedRowLiveFace(prepared.toolRosterDeltas.current, pendingAction)) };
734
- const judged = prepared.shellReadCheck.judge(readCall, resolvedArgs, prepared.cwdRef?.current, prepared.abortController.signal, {
737
+ const judged = prepared.shellReadCheck.judge(readCall, executedArgs, prepared.cwdRef?.current, prepared.abortController.signal, {
735
738
  toolCallId: pendingAction.toolCallId,
736
- ...(edited ? { seat: "rejudge" } : {}),
739
+ ...(unseen ? { seat: "rejudge" } : {}),
737
740
  });
738
741
  if (judged !== null) {
739
742
  let verdict;
@@ -745,7 +748,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
745
748
  answered = false;
746
749
  verdict = prepared.shellReadCheck.unanswered(readCall, "the read station could not answer on the resumed leg");
747
750
  }
748
- if (!answered || verdict.disposition === "refuse" || (edited && verdict.disposition !== "vouch")) {
751
+ if (!answered || verdict.disposition === "refuse" || (unseen && verdict.disposition !== "vouch")) {
749
752
  const readDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: ${verdict.sentence ?? "the read boundary does not vouch for what it reads on this leg"} This approval is spent — the call has to be re-issued and approved again.`, prepared.reminderMark);
750
753
  await settleEnd(true, { content: readDenial, ...(verdict.details !== undefined ? { details: verdict.details } : {}) }, vetoed("read_boundary"));
751
754
  const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, readDenial, true));
@@ -755,16 +758,25 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
755
758
  verdict.admit?.();
756
759
  }
757
760
  }
761
+ const grantRefusal = parkedSkillApprovalRefusal(pendingAction, executedArgs, prepared.skillSpecs, SKILL_TOOL_NAME, "execution");
762
+ if (grantRefusal !== undefined) {
763
+ const grantDenial = formatHookFeedback(grantRefusal, prepared.reminderMark);
764
+ await settleEnd(true, { content: grantDenial }, vetoed("policy"));
765
+ emitCommitted(await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, grantDenial, true)), "toolResult", pendingAction.toolCallId);
766
+ return;
767
+ }
758
768
  const tool = prepared.tools.find((t) => t.name === pendingAction.toolName);
759
769
  if (!tool) {
760
770
  const e = new Error(`the approved tool "${pendingAction.toolName}" is no longer available on resume`);
761
771
  e.code = "resume.tool_unavailable";
762
772
  throw e;
763
773
  }
764
- const args = resolvedArgs;
774
+ const args = executedArgs;
775
+ screened?.admit();
765
776
  onExecuteStart?.(pendingAction.toolCallId);
766
777
  prepared.toolProgressRef.current?.noteStart(pendingAction.toolCallId, pendingAction.toolName);
767
778
  prepared.suspendProgressRef.executedApproved = true;
779
+ const finalize = (executed) => prepared.harness.finalizeToolCall({ type: "toolCall", id: pendingAction.toolCallId, name: spelledName, arguments: executedArgs }, executedArgs, executed);
768
780
  let res;
769
781
  try {
770
782
  res = await tool.execute(pendingAction.toolCallId, args, prepared.abortController.signal);
@@ -772,21 +784,23 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
772
784
  catch (err) {
773
785
  const execError = `Error: ${thrownText(err)}`;
774
786
  const marks = thrownMarksOf(err);
775
- await settleEnd(true, { content: execError, ...(marks !== undefined ? { details: marks } : {}) });
776
- const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, execError, true));
787
+ const failed = await finalize({ result: { content: [{ type: "text", text: execError }], details: marks ?? {} }, isError: true });
788
+ const failedText = failed.result.content.flatMap((c) => (c.type === "text" ? [c.text] : [])).join("\n");
789
+ await settleEnd(true, { content: failedText, ...(marks !== undefined ? { details: marks } : {}) });
790
+ const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, failedText, true));
777
791
  emitCommitted(eid, "toolResult", pendingAction.toolCallId);
778
792
  return;
779
793
  }
780
- const executedIsError = res.isError === true;
781
- await settleEnd(executedIsError, res);
782
- onResolvedToolSuccess?.(pendingAction.toolName, executedIsError ? undefined : res.details);
794
+ const ran = await finalize({ result: res, isError: res.isError === true });
795
+ await settleEnd(ran.isError, ran.result);
796
+ onResolvedToolSuccess?.(pendingAction.toolName, ran.isError ? undefined : ran.result.details);
783
797
  const eid = await prepared.session.appendMessage({
784
798
  role: "toolResult",
785
799
  toolCallId: pendingAction.toolCallId,
786
800
  toolName: pendingAction.toolName,
787
- content: res.content,
788
- details: res.details,
789
- isError: executedIsError,
801
+ content: ran.result.content,
802
+ details: ran.result.details,
803
+ isError: ran.isError,
790
804
  timestamp: Date.now(),
791
805
  });
792
806
  emitCommitted(eid, "toolResult", pendingAction.toolCallId);
@@ -241,7 +241,7 @@ export declare class Runner implements RunnerSelfSeat {
241
241
  * re-supply; on the resumed leg the CHECKPOINT snapshot is the trust floor — live internals may only
242
242
  * TIGHTEN it (per-entry rev-monotonic ancestorRules replacement, max-rank shellGate fold), never
243
243
  * displace it wholesale. TRUST BOUNDARY: `internals` is the DEPLOYMENT trust channel (the same tier as
244
- * `inheritedManifestScope` and every other RunInternals field) — the library caller is trusted to
244
+ * `inheritedSkillGrants` and every other RunInternals field) — the library caller is trusted to
245
245
  * re-supply the genuine chain; a caller able to invoke resumeStream already holds full Runner
246
246
  * authority, so constraint substitution here is outside the enforced threat model (the count check
247
247
  * catches shape MISTAKES, not a untrusted trusted caller). */