@sema-agent/core 7.33.8 → 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 (228) hide show
  1. package/CHANGELOG.md +462 -3
  2. package/dist/agents/cross-session-judge.d.ts +16 -7
  3. package/dist/agents/cross-session-judge.js +18 -15
  4. package/dist/agents/peer-session-drain.d.ts +4 -0
  5. package/dist/agents/peer-session-drain.js +5 -2
  6. package/dist/agents/repair-loop.d.ts +1 -1
  7. package/dist/agents/subagent.d.ts +3 -0
  8. package/dist/agents/subagent.js +12 -3
  9. package/dist/brain/anthropic.js +1 -0
  10. package/dist/brain/errors.d.ts +23 -1
  11. package/dist/brain/errors.js +19 -2
  12. package/dist/brain/open-responses.js +1 -0
  13. package/dist/brain/openai.js +1 -0
  14. package/dist/brain/status-sink.d.ts +7 -1
  15. package/dist/brain/stream-engine.d.ts +3 -0
  16. package/dist/brain/stream-engine.js +2 -1
  17. package/dist/brain/stream-shared.d.ts +3 -1
  18. package/dist/brain/stream-shared.js +16 -4
  19. package/dist/core/a2a.d.ts +3 -1
  20. package/dist/core/a2a.js +1 -1
  21. package/dist/core/ask-origin.d.ts +379 -154
  22. package/dist/core/ask-origin.js +224 -45
  23. package/dist/core/ask-question.js +2 -1
  24. package/dist/core/ask-unresolvable-notice.d.ts +5 -2
  25. package/dist/core/ask-unresolvable-notice.js +4 -3
  26. package/dist/core/auto-mode-arming.d.ts +17 -4
  27. package/dist/core/auto-mode-arming.js +31 -6
  28. package/dist/core/auto-mode-defaults.d.ts +16 -7
  29. package/dist/core/auto-mode-defaults.js +2 -1
  30. package/dist/core/auto-mode-directive.d.ts +18 -30
  31. package/dist/core/auto-mode-directive.js +9 -18
  32. package/dist/core/auto-mode-prompt.d.ts +62 -1
  33. package/dist/core/auto-mode-prompt.js +42 -4
  34. package/dist/core/auto-mode-rebuild.d.ts +2 -1
  35. package/dist/core/auto-mode-rebuild.js +1 -0
  36. package/dist/core/auto-mode.d.ts +32 -3
  37. package/dist/core/auto-mode.js +80 -41
  38. package/dist/core/checkpoint-store.d.ts +54 -35
  39. package/dist/core/checkpoint-store.js +58 -3
  40. package/dist/core/child-clamp-seats.js +1 -0
  41. package/dist/core/classifier-unavailable-arm.d.ts +57 -0
  42. package/dist/core/classifier-unavailable-arm.js +17 -0
  43. package/dist/core/effective-path-target.d.ts +12 -0
  44. package/dist/core/effective-path-target.js +18 -7
  45. package/dist/core/engine-notice.d.ts +53 -2
  46. package/dist/core/fs-write-gate-policy.d.ts +5 -1
  47. package/dist/core/fs-write-gate-policy.js +4 -1
  48. package/dist/core/gate-fold.js +19 -3
  49. package/dist/core/gate-lanes.js +116 -50
  50. package/dist/core/gate-outcome.d.ts +40 -7
  51. package/dist/core/gate-outcome.js +26 -4
  52. package/dist/core/governance-codes.d.ts +18 -2
  53. package/dist/core/governance-codes.js +23 -1
  54. package/dist/core/hooks.d.ts +53 -29
  55. package/dist/core/hooks.js +1 -3
  56. package/dist/core/mcp.js +1 -1
  57. package/dist/core/message-utils.d.ts +2 -1
  58. package/dist/core/message-utils.js +2 -1
  59. package/dist/core/mode-policy.d.ts +185 -0
  60. package/dist/core/mode-policy.js +184 -0
  61. package/dist/core/oracle-isolation.d.ts +3 -3
  62. package/dist/core/permission-rule-model.d.ts +20 -3
  63. package/dist/core/permission-rule-model.js +40 -3
  64. package/dist/core/permission-rule-provider.d.ts +22 -11
  65. package/dist/core/permission-rule-provider.js +17 -7
  66. package/dist/core/permission-rules.d.ts +23 -1
  67. package/dist/core/permission-rules.js +56 -9
  68. package/dist/core/persisted-rule-arms.d.ts +14 -10
  69. package/dist/core/persisted-rule-arms.js +13 -6
  70. package/dist/core/present-plan-tool.d.ts +1 -1
  71. package/dist/core/present-plan-tool.js +3 -3
  72. package/dist/core/read-only-shell.d.ts +19 -19
  73. package/dist/core/read-only-shell.js +38 -12
  74. package/dist/core/retired-keys.d.ts +9 -3
  75. package/dist/core/retired-keys.js +5 -1
  76. package/dist/core/runner/active-skill-scope.js +5 -4
  77. package/dist/core/runner/assemble-result.js +2 -2
  78. package/dist/core/runner/classifier-ladder.d.ts +43 -1
  79. package/dist/core/runner/classifier-ladder.js +29 -0
  80. package/dist/core/runner/contracts.d.ts +62 -38
  81. package/dist/core/runner/denial-limit-arms.d.ts +35 -7
  82. package/dist/core/runner/denial-limit-arms.js +15 -9
  83. package/dist/core/runner/env-capability-census.js +1 -1
  84. package/dist/core/runner/file-history-request.d.ts +26 -0
  85. package/dist/core/runner/file-history-request.js +90 -0
  86. package/dist/core/runner/gate-exit.d.ts +30 -4
  87. package/dist/core/runner/gate-exit.js +28 -7
  88. package/dist/core/runner/permission-rule-lanes.d.ts +11 -6
  89. package/dist/core/runner/permission-rule-lanes.js +55 -48
  90. package/dist/core/runner/persisted-path-rows.d.ts +64 -0
  91. package/dist/core/runner/persisted-path-rows.js +89 -0
  92. package/dist/core/runner/prepare-artifact.d.ts +5 -1
  93. package/dist/core/runner/prepare-artifact.js +9 -3
  94. package/dist/core/runner/prepare-ask-lane.d.ts +6 -4
  95. package/dist/core/runner/prepare-ask-lane.js +3 -3
  96. package/dist/core/runner/prepare-caps-and-workflow.d.ts +39 -13
  97. package/dist/core/runner/prepare-caps-and-workflow.js +58 -29
  98. package/dist/core/runner/prepare-file-history.d.ts +9 -3
  99. package/dist/core/runner/prepare-file-history.js +4 -90
  100. package/dist/core/runner/prepare-gate-stations.d.ts +37 -9
  101. package/dist/core/runner/prepare-gate-stations.js +46 -29
  102. package/dist/core/runner/prepare-hands-readface.d.ts +40 -17
  103. package/dist/core/runner/prepare-hands-readface.js +64 -53
  104. package/dist/core/runner/prepare-inherited-gate.d.ts +30 -23
  105. package/dist/core/runner/prepare-inherited-gate.js +45 -25
  106. package/dist/core/runner/prepare-park-ask.d.ts +6 -4
  107. package/dist/core/runner/prepare-park-ask.js +7 -5
  108. package/dist/core/runner/prepare-policy-chain.d.ts +25 -4
  109. package/dist/core/runner/prepare-policy-chain.js +203 -180
  110. package/dist/core/runner/prepare-protocol-tools.d.ts +28 -0
  111. package/dist/core/runner/prepare-protocol-tools.js +13 -3
  112. package/dist/core/runner/prepare-run-refs.d.ts +6 -6
  113. package/dist/core/runner/prepare-run-refs.js +9 -4
  114. package/dist/core/runner/prepare-safety-scan.d.ts +5 -0
  115. package/dist/core/runner/prepare-safety-scan.js +19 -3
  116. package/dist/core/runner/prepare-suspend-saga.d.ts +5 -5
  117. package/dist/core/runner/prepare-suspend-saga.js +6 -6
  118. package/dist/core/runner/prepare-task.js +56 -24
  119. package/dist/core/runner/prepare-wiring-manifest.d.ts +10 -6
  120. package/dist/core/runner/prepare-wiring-manifest.js +5 -3
  121. package/dist/core/runner/read-root-grant.d.ts +9 -0
  122. package/dist/core/runner/read-root-grant.js +12 -0
  123. package/dist/core/runner/retired-runner-deps.js +1 -0
  124. package/dist/core/runner/run-brain-sinks.js +3 -1
  125. package/dist/core/runner/run-identity-wiring.js +1 -1
  126. package/dist/core/runner/run-leg.js +29 -18
  127. package/dist/core/runner/run-notification-lane.d.ts +3 -0
  128. package/dist/core/runner/run-notification-lane.js +3 -1
  129. package/dist/core/runner/run-telemetry-and-budget-seats.d.ts +2 -2
  130. package/dist/core/runner/run-telemetry-and-budget-seats.js +1 -1
  131. package/dist/core/runner/run-turn-boundary.js +1 -1
  132. package/dist/core/runner/runtask.d.ts +1 -0
  133. package/dist/core/runner/runtask.js +8 -11
  134. package/dist/core/runner/session-mode-seats.d.ts +48 -0
  135. package/dist/core/runner/session-mode-seats.js +73 -0
  136. package/dist/core/runner/session-rule-policy.js +3 -2
  137. package/dist/core/runner/stream-mode-verb.d.ts +27 -0
  138. package/dist/core/runner/stream-mode-verb.js +19 -0
  139. package/dist/core/runner/turn-attachments.d.ts +3 -3
  140. package/dist/core/runner/turn-attachments.js +1 -1
  141. package/dist/core/runner/write-protection-station.d.ts +7 -1
  142. package/dist/core/runner/write-protection-station.js +6 -3
  143. package/dist/core/runner-deps.d.ts +31 -34
  144. package/dist/core/runtime-caps.d.ts +12 -3
  145. package/dist/core/safety-axis-vocab.d.ts +2 -2
  146. package/dist/core/safety-axis-vocab.js +1 -1
  147. package/dist/core/secret-env.d.ts +30 -58
  148. package/dist/core/secret-env.js +149 -586
  149. package/dist/core/sensitive-path-policy.d.ts +4 -2
  150. package/dist/core/sensitive-path-policy.js +41 -31
  151. package/dist/core/store-contracts/checkpoint-store-contract.js +2 -1
  152. package/dist/core/task-event.d.ts +1 -1
  153. package/dist/core/task-result.d.ts +1 -1
  154. package/dist/core/task-spec.d.ts +41 -89
  155. package/dist/core/task-stream.d.ts +16 -0
  156. package/dist/core/terminal-cause.js +1 -0
  157. package/dist/core/tighten-task-spec.d.ts +2 -2
  158. package/dist/core/tighten-task-spec.js +12 -14
  159. package/dist/core/tool-catalog-entries.js +4 -3
  160. package/dist/core/tool-policy.d.ts +84 -30
  161. package/dist/core/tool-policy.js +76 -16
  162. package/dist/core/tool-registry.d.ts +38 -0
  163. package/dist/core/tool-registry.js +25 -1
  164. package/dist/core/tool-roster.d.ts +17 -2
  165. package/dist/core/tool-roster.js +11 -1
  166. package/dist/core/tool-spec.d.ts +8 -9
  167. package/dist/core/trace.d.ts +18 -6
  168. package/dist/core/types.d.ts +2 -1
  169. package/dist/core/wiring-manifest.d.ts +31 -5
  170. package/dist/core/wiring-manifest.js +28 -1
  171. package/dist/core/write-protect.d.ts +11 -1
  172. package/dist/core/write-protect.js +26 -15
  173. package/dist/engine/execution-env/node-execution-env.d.ts +6 -1
  174. package/dist/engine/execution-env/node-execution-env.js +16 -3
  175. package/dist/engine/harness/agent-harness.js +2 -1
  176. package/dist/engine/harness/types.d.ts +6 -7
  177. package/dist/engine/llm/types.d.ts +2 -4
  178. package/dist/engine/loop/types.d.ts +7 -0
  179. package/dist/engine/lsp/node-lsp-manager.js +1 -1
  180. package/dist/index.d.ts +18 -8
  181. package/dist/index.js +15 -6
  182. package/dist/orchestration/workflow-governance.js +1 -1
  183. package/dist/orchestration/workflow-primitives.d.ts +1 -1
  184. package/dist/orchestration/workflow-size-guideline.js +2 -2
  185. package/dist/stores/file/index.d.ts +5 -3
  186. package/dist/stores/file/session-store.d.ts +8 -2
  187. package/dist/stores/file/session-store.js +5 -5
  188. package/dist/tools/artifact/artifact-tool.d.ts +17 -0
  189. package/dist/tools/artifact/artifact-tool.js +16 -1
  190. package/dist/tools/fs/bash-readonly-classifier.d.ts +28 -7
  191. package/dist/tools/fs/bash-readonly-classifier.js +88 -0
  192. package/dist/tools/fs/containment-face.d.ts +9 -3
  193. package/dist/tools/fs/containment-face.js +2 -0
  194. package/dist/tools/fs/file-read-check.d.ts +50 -0
  195. package/dist/tools/fs/file-read-check.js +79 -0
  196. package/dist/tools/fs/fs-bash.d.ts +3 -3
  197. package/dist/tools/fs/fs-read.d.ts +1 -0
  198. package/dist/tools/fs/fs-search-tools.d.ts +2 -2
  199. package/dist/tools/fs/fs-search-tools.js +4 -4
  200. package/dist/tools/fs/fs-shared.js +20 -6
  201. package/dist/tools/fs/fs-write.d.ts +4 -3
  202. package/dist/tools/fs/fs-write.js +9 -9
  203. package/dist/tools/fs/index.d.ts +39 -3
  204. package/dist/tools/fs/index.js +38 -15
  205. package/dist/tools/fs/read-deny-home.d.ts +93 -0
  206. package/dist/tools/fs/read-deny-home.js +225 -0
  207. package/dist/tools/fs/read-deny.d.ts +176 -34
  208. package/dist/tools/fs/read-deny.js +268 -61
  209. package/dist/tools/fs/safety.d.ts +6 -1
  210. package/dist/tools/fs/safety.js +18 -5
  211. package/dist/tools/fs/search.d.ts +3 -0
  212. package/dist/tools/fs/search.js +47 -9
  213. package/dist/tools/fs/shell-read-check.d.ts +52 -13
  214. package/dist/tools/fs/shell-read-check.js +199 -20
  215. package/dist/tools/fs/shell-read-establish.d.ts +7 -3
  216. package/dist/tools/fs/shell-read-establish.js +134 -21
  217. package/dist/tools/fs/shell-read-identity.d.ts +7 -0
  218. package/dist/tools/fs/shell-read-identity.js +21 -0
  219. package/dist/tools/fs/shell-read-judge.d.ts +92 -32
  220. package/dist/tools/fs/shell-read-judge.js +43 -18
  221. package/dist/tools/fs/shell-read-plan.d.ts +4 -0
  222. package/dist/tools/fs/shell-read-plan.js +26 -4
  223. package/dist/tools/fs/shell-read-write-targets.d.ts +73 -0
  224. package/dist/tools/fs/shell-read-write-targets.js +173 -0
  225. package/package.json +1 -1
  226. package/test/export-surface.snapshot.json +117 -11
  227. package/wire-changes/core@7.34.0.json +2621 -0
  228. package/wire-changes/core@7.35.0.json +584 -0
@@ -513,6 +513,7 @@ export async function runLeg(input) {
513
513
  }
514
514
  prepared.abortController.abort();
515
515
  prepared.releaseSignal();
516
+ prepared.endModeCeiling();
516
517
  strandedHumanAnswers = prepared.settleContentAskBindings();
517
518
  try {
518
519
  prepared.harness.recoverUndrainedEngineNotes();
@@ -633,7 +634,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
633
634
  }
634
635
  if (prepared.permissionRuleOrg !== undefined) {
635
636
  const orgVerdict = prepared.permissionRuleOrg
636
- .adjudicate({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId })
637
+ .adjudicate({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId, ...callFaceSeat(toolCallFaceOf(prepared.toolRosterDeltas.current, pendingAction.toolName)) })
637
638
  .catch(() => ({ status: "unavailable", disclosures: ["the org adjudication face threw on resume"] }));
638
639
  let orgWaitCancelled = false;
639
640
  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)`] }, {
@@ -683,22 +684,30 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
683
684
  }
684
685
  if (prepared.persistedRuleLane !== undefined) {
685
686
  const unreadable = { unreadable: true };
686
- const read = await settleOrgVerdictWithin(Promise.resolve()
687
- .then(() => prepared.persistedRuleLane.adjudicate({
688
- toolName: pendingAction.toolName,
689
- args: resolvedArgs,
690
- toolCallId: pendingAction.toolCallId,
691
- ...callFaceSeat(toolCallFaceOf(prepared.toolRosterDeltas.current, pendingAction.toolName)),
692
- budget: beltBudget,
693
- ...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
694
- ...advertisedWritableRootsSeat(prepared.advertisedWritableDirs),
695
- }))
696
- .then((answer) => normalizePersistedRuleHit(answer)), unreadable, { signal: prepared.abortController.signal, timeoutMs: ORG_ADJUDICATION_TIMEOUT_MS });
687
+ let laneWaitCancelled = false;
688
+ let laneRead;
689
+ try {
690
+ laneRead = Promise.resolve(prepared.persistedRuleLane.adjudicate({
691
+ toolName: pendingAction.toolName,
692
+ args: resolvedArgs,
693
+ toolCallId: pendingAction.toolCallId,
694
+ ...callFaceSeat(toolCallFaceOf(prepared.toolRosterDeltas.current, pendingAction.toolName)),
695
+ budget: beltBudget,
696
+ ...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
697
+ ...advertisedWritableRootsSeat(prepared.advertisedWritableDirs),
698
+ })).then((answer) => normalizePersistedRuleHit(answer));
699
+ }
700
+ catch {
701
+ laneRead = Promise.resolve(unreadable);
702
+ }
703
+ const read = await settleOrgVerdictWithin(laneRead, unreadable, { signal: prepared.abortController.signal, timeoutMs: ORG_ADJUDICATION_TIMEOUT_MS, onFallback: (cause) => void (laneWaitCancelled = cause === "aborted") });
697
704
  const tightened = applyPersistedTightening({ action: "allow" }, read);
698
- if (tightened.tightened === "deny" || tightened.tightened === "unreadable") {
699
- const why = tightened.tightened === "deny"
700
- ? `a standing deny rule of yours (${disclosedRuleSet(read.hit?.rules ?? [])}) now refuses it`
701
- : "your persisted permission rules could not be read on this leg";
705
+ if (laneWaitCancelled || tightened.tightened === "deny" || tightened.tightened === "unreadable") {
706
+ const why = laneWaitCancelled
707
+ ? "the task was cancelled before your persisted permission rules could be read"
708
+ : tightened.tightened === "deny"
709
+ ? `a standing deny rule of yours (${disclosedRuleSet(read.hit?.rules ?? [])}) now refuses it`
710
+ : `it could not be read against your standing deny/ask rules (${read.reason})`;
702
711
  const ruleDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: ${why}. This approval is spent — the call has to be re-issued and approved again.`, prepared.reminderMark);
703
712
  await settleEnd(true, { content: ruleDenial }, vetoed("persisted_rule"));
704
713
  const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, ruleDenial, true));
@@ -708,7 +717,8 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
708
717
  }
709
718
  if (prepared.shellReadCheck !== undefined) {
710
719
  const edited = outcome.updatedInput !== undefined && !deepJsonEqual(outcome.updatedInput, pendingAction.args);
711
- const judged = prepared.shellReadCheck.judge(pendingAction.toolName, resolvedArgs, prepared.cwdRef?.current, prepared.abortController.signal, {
720
+ const readCall = { toolName: pendingAction.toolName, ...callFaceSeat(toolCallFaceOf(prepared.toolRosterDeltas.current, pendingAction.toolName)) };
721
+ const judged = prepared.shellReadCheck.judge(readCall, resolvedArgs, prepared.cwdRef?.current, prepared.abortController.signal, {
712
722
  toolCallId: pendingAction.toolCallId,
713
723
  ...(edited ? { seat: "rejudge" } : {}),
714
724
  });
@@ -720,7 +730,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
720
730
  }
721
731
  catch {
722
732
  answered = false;
723
- verdict = prepared.shellReadCheck.unanswered(pendingAction.toolName, "the read station could not answer on the resumed leg");
733
+ verdict = prepared.shellReadCheck.unanswered(readCall, "the read station could not answer on the resumed leg");
724
734
  }
725
735
  if (!answered || verdict.disposition === "refuse" || (edited && verdict.disposition !== "vouch")) {
726
736
  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);
@@ -729,6 +739,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
729
739
  emitCommitted(eid, "toolResult", pendingAction.toolCallId);
730
740
  return;
731
741
  }
742
+ verdict.admit?.();
732
743
  }
733
744
  }
734
745
  const tool = prepared.tools.find((t) => t.name === pendingAction.toolName);
@@ -4,6 +4,7 @@ import type { SessionStore } from "../session.js";
4
4
  import { type NotificationInjectionOutcome, type PendingSessionNotifications, type SystemInjectionPriority, type TaskNotificationPayload } from "../task-notification.js";
5
5
  import type { RunnerDeps, TaskEvent, TaskSpec } from "../types.js";
6
6
  import type { SessionReadFileStates } from "./session-read-file-states.js";
7
+ import type { SessionModeSeats } from "./session-mode-seats.js";
7
8
  import type { NotificationLaneBindings, Prepared, PrepareTaskFn, ResumeRun, RunInternals, RunnerDepsSeat, RunnerSelfSeat, TaskIdRef } from "./contracts.js";
8
9
  export interface RunNotificationLaneInput<R> {
9
10
  /** borrowed-readonly — the task spec: prepare's input, and the principal the peer scope is minted from. */
@@ -26,6 +27,8 @@ export interface RunNotificationLaneInput<R> {
26
27
  sessions: SessionStore;
27
28
  /** borrowed-mutable — the Runner's own per-session read-state seat, ALWAYS this Runner's (a caller-supplied one is overridden). */
28
29
  sessionReadStates: SessionReadFileStates;
30
+ /** borrowed-mutable — the Runner's per-session permission-mode seats, ALWAYS this Runner's (a caller-supplied one is overridden). */
31
+ sessionModeSeats: SessionModeSeats;
29
32
  /** borrowed-mutable — the Runner's per-session parked-notification store: the torn-down lane and the escalation pend here. */
30
33
  pendingSessionNotifications: PendingSessionNotifications;
31
34
  /** borrowed-readonly — the orchestrator's entry (a lane may not name the orchestrator; the driver hands it in), as the ONE
@@ -8,12 +8,13 @@ export function runNotificationLane(input) {
8
8
  return obligations.run(() => runNotificationLaneAs(input, obligations));
9
9
  }
10
10
  async function runNotificationLaneAs(input, obligations) {
11
- const { spec, queue, resume, internals, taskIdRef, runner, self, sessions, sessionReadStates, pendingSessionNotifications, prepareTask, next } = input;
11
+ const { spec, queue, resume, internals, taskIdRef, runner, self, sessions, sessionReadStates, sessionModeSeats, pendingSessionNotifications, prepareTask, next } = input;
12
12
  const prepareResume = resume
13
13
  ? {
14
14
  leafId: resume.cp.leafId,
15
15
  suspendedBatch: new Set(resume.cp.pendingAction.kind === "tool_approval" ? resume.cp.pendingAction.batchToolCallIds : []),
16
16
  seed: resume.cp.state,
17
+ outcome: resume.outcome,
17
18
  priorSuspendCount: resume.cp.suspendCount,
18
19
  priorLedger: resume.cp.resourceLedger,
19
20
  priorHumanReview: resume.cp.humanReview,
@@ -117,6 +118,7 @@ async function runNotificationLaneAs(input, obligations) {
117
118
  peerInboundChainRef,
118
119
  detachHub,
119
120
  sessionReadStates,
121
+ sessionModeSeats,
120
122
  onTaskNotification: (notification, opts) => {
121
123
  try {
122
124
  upstreamTaskNotification?.(notification, opts);
@@ -6,8 +6,8 @@ import type { Prepared, ResumeRun, RunnerDepsSeat, RunState, TaskIdRef } from ".
6
6
  export interface RunTelemetryAndBudgetSeatsInput {
7
7
  /** borrowed-readonly — the task spec: limits, degrade, tracer, output retries, the resource-suspend totals. */
8
8
  spec: TaskSpec;
9
- /** borrowed-mutable — the leg's prepared seat: `liveSpendRef.get` is installed, `humanReviewRef` and `planModeRef`
10
- * are written on a resume; the model, the ledger, the session id and the usage governance are read. */
9
+ /** borrowed-mutable — the leg's prepared seat: `liveSpendRef.get` is installed, `humanReviewRef` is written and the
10
+ * mode seat steered on a resume; the model, the ledger, the session id and the usage governance are read. */
11
11
  prepared: Prepared;
12
12
  /** borrowed-readonly — the resume plan when this leg resumes a checkpoint (the human-review latency, the plan re-arm). */
13
13
  resume: ResumeRun | undefined;
@@ -33,7 +33,7 @@ export function runTelemetryAndBudgetSeats(input) {
33
33
  });
34
34
  }
35
35
  if (resume !== undefined && resume.outcome.gate === "plan_review" && resume.outcome.decision === "reject") {
36
- prepared.planModeRef.active = true;
36
+ void prepared.permissionMode.steer("plan");
37
37
  }
38
38
  rs.telemetry.cacheFamily = cacheFamilyOf(prepared.model);
39
39
  rs.telemetry.pricing = runner.deps.pricing?.[prepared.model.id] ?? modelCostToPricing(prepared.model.cost);
@@ -283,7 +283,7 @@ export function createTurnBoundary(input) {
283
283
  cadenceTurn: rs.counters.cadenceTurns,
284
284
  todoMounted: todoToolMounted,
285
285
  taskListMounted: taskToolsMounted,
286
- planActive: prepared.planModeRef.active,
286
+ planActive: prepared.permissionMode.current === "plan",
287
287
  config: {
288
288
  todoReminder: rs.attach.attachmentsCfg?.todoReminder === true,
289
289
  ...(rs.attach.attachmentsCfg?.todoReminderMode !== undefined ? { todoReminderMode: rs.attach.attachmentsCfg.todoReminderMode } : {}),
@@ -22,6 +22,7 @@ export declare class Runner implements RunnerSelfSeat {
22
22
  * authority: a durable resume's checkpoint seed wins on its leg, and a miss (cross-process,
23
23
  * evicted) falls back to the transcript-replay rung. See {@link SessionReadFileStates}. */
24
24
  private readonly sessionReadStates;
25
+ private readonly sessionModeSeats;
25
26
  /** task notifications born BETWEEN turns (run torn down / harness already idle), parked
26
27
  * per session and drained into the session's next run at its first boundary. Runner-lived (outlives any
27
28
  * single run, like the registry handles that produce into it); bounded + drop-disclosing, see
@@ -16,9 +16,10 @@ import { TtlSessionStore } from "../session-store.js";
16
16
  import { errorCodeOf } from "./assemble-result.js";
17
17
  import { effectiveDelegationFacts, prepareTask } from "./prepare-task.js";
18
18
  import { SessionReadFileStates } from "./session-read-file-states.js";
19
+ import { SessionModeSeats } from "./session-mode-seats.js";
19
20
  import { RunnerSharedToolResultStore } from "../tool-result-store.js";
20
21
  import { PAUSE_REGISTRY } from "../pause-registry.js";
21
- import { toolPolicyNameSets } from "../tool-policy.js";
22
+ import { wrapToolPolicy } from "../tool-policy.js";
22
23
  import { PendingSessionNotifications } from "../task-notification.js";
23
24
  import { ToolDetachHub } from "../tool-detach.js";
24
25
  import { assertContextSettingsValue, assertContextSettingsWords, contextOverlayFor } from "../context-settings.js";
@@ -40,6 +41,7 @@ import { streamReap } from "./stream-reap.js";
40
41
  import { streamSteerVerb } from "./stream-steer-verb.js";
41
42
  import { streamLifecycleVerbs } from "./stream-lifecycle-verbs.js";
42
43
  import { streamHaltVerbs } from "./stream-halt-verbs.js";
44
+ import { streamModeVerb } from "./stream-mode-verb.js";
43
45
  import { runNotificationLane } from "./run-notification-lane.js";
44
46
  import { assertNoRetiredRunnerDeps } from "./retired-runner-deps.js";
45
47
  import { runLeg } from "./run-leg.js";
@@ -64,6 +66,7 @@ export class Runner {
64
66
  sessions;
65
67
  sessionLocks = new Map();
66
68
  sessionReadStates = new SessionReadFileStates();
69
+ sessionModeSeats = new SessionModeSeats();
67
70
  pendingSessionNotifications = new PendingSessionNotifications();
68
71
  parentConstraintRegistry = new Map();
69
72
  suspendedEnvReaps = new Map();
@@ -173,15 +176,7 @@ export class Runner {
173
176
  return {
174
177
  ...(deps.toolPolicy !== undefined
175
178
  ? {
176
- toolPolicy: (() => {
177
- const sets = toolPolicyNameSets(deps.toolPolicy);
178
- const wrapped = {
179
- check: (req, signal) => deps.toolPolicy.check(req, signal),
180
- };
181
- if (sets.length > 0)
182
- wrapped.nameSets = sets;
183
- return wrapped;
184
- })(),
179
+ toolPolicy: wrapToolPolicy(deps.toolPolicy, (req, signal) => deps.toolPolicy.check(req, signal)),
185
180
  }
186
181
  : {}),
187
182
  ...(hooks !== undefined ? { hooks } : {}),
@@ -417,6 +412,7 @@ export class Runner {
417
412
  const { steer } = streamSteerVerb({ spec, internals, queue, acceptedSteerInputs, live, ready, orTimeout, readyTimeoutMs: READY_TIMEOUT_MS, steeringError, runner: this.depsSeat });
418
413
  const { notify, optOutMemoryCapture, reconnectMcpServer, compact, detach, interrupt } = streamLifecycleVerbs({ live, ready, orTimeout, steeringError, notifyRef, captureOptOutRef, mcpRedialRef, manualCompactRef, detachHub });
419
414
  const { halt, destroy } = streamHaltVerbs({ spec, live, ready, orTimeout, readyTimeoutMs: READY_TIMEOUT_MS, run, reapSuspended, runner: this.depsSeat });
415
+ const { setPermissionMode } = streamModeVerb({ live, ready, orTimeout, steeringError });
420
416
  return {
421
417
  [Symbol.asyncIterator]: () => queue[Symbol.asyncIterator](),
422
418
  result: async () => {
@@ -430,6 +426,7 @@ export class Runner {
430
426
  steer,
431
427
  notify,
432
428
  optOutMemoryCapture,
429
+ setPermissionMode,
433
430
  reconnectMcpServer,
434
431
  compact,
435
432
  detach,
@@ -442,7 +439,7 @@ export class Runner {
442
439
  const { spec, queue, setResult, onSuggestions, onReady, onSuspend, manualCompactRef, taskIdRef, resume, internals, notifyRef, captureOptOutRef, mcpRedialRef, entryActor, entryTracer } = input;
443
440
  return runNotificationLane({
444
441
  spec, queue, resume, internals, taskIdRef,
445
- runner: this.depsSeat, self: this, sessions: this.sessions, sessionReadStates: this.sessionReadStates, pendingSessionNotifications: this.pendingSessionNotifications,
442
+ runner: this.depsSeat, self: this, sessions: this.sessions, sessionReadStates: this.sessionReadStates, sessionModeSeats: this.sessionModeSeats, pendingSessionNotifications: this.pendingSessionNotifications,
446
443
  prepareTask,
447
444
  next: ({ prepared, injectTaskNotification, deliveredAtTurnOpen, peerInboundChainRef, modelCatalog, notificationLane, unsubscribeTaskNotifications }) => {
448
445
  const { runSourceTaskId, parentToolCallId, ident, emitDelegationLifecycle, loopLatch, stats, internalsNotifier, undrainedUserAtEnd, userInputLostAtEnd } = runIdentityWiring({
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The Runner's per-session PERMISSION-MODE seats: the mode a session's CURRENT leg runs under, as the object a
3
+ * child's ceiling falls back to once the leg that spawned it has ended. A child's ceiling used to be the controller
4
+ * of the leg that spawned it for the child's whole life, so a session-scoped background child kept that leg's mode
5
+ * after the leg ended — the next leg of the same session, started in `plan`, did not reach it. The chain now carries
6
+ * a LEG ceiling: the spawning leg's live mode while that leg runs, and the session's seat after it. Each leg binds
7
+ * itself to its session's seat when its prepare commits, so the seat answers the most recently committed leg; a leg
8
+ * that is still running keeps answering its own children, whatever another leg of the session commits meanwhile.
9
+ */
10
+ import type { PermissionMode } from "../ask-origin.js";
11
+ import type { PermissionModeReceipt } from "../mode-policy.js";
12
+ /** A child leg whose ceiling is a leg ceiling: its receipt is read before a ceiling move and re-judged after it. */
13
+ export interface ModeCeilingHolder {
14
+ receipt(): PermissionModeReceipt;
15
+ /** The ceiling's source moved; `before` is this holder's receipt under the previous source. */
16
+ ceilingRebound(before: PermissionModeReceipt): void;
17
+ }
18
+ /** One leg's ceiling, as the chain carries it (`InheritedGate.permissionModeCeiling`). */
19
+ export interface LegModeCeiling {
20
+ /** The spawning leg's live mode while it runs; the session's current leg's after it (`default` before any leg of
21
+ * the session committed — no child can read it then). */
22
+ readonly current: PermissionMode;
23
+ readonly sessionId: string;
24
+ /** A child leg holds the ceiling until `signal` aborts (its own leg's end): each move of the ceiling re-judges it. */
25
+ hold(holder: ModeCeilingHolder, signal: AbortSignal): void;
26
+ }
27
+ /**
28
+ * The seats of one Runner, keyed by session id and held WEAKLY: a seat lives while a leg ceiling holds it, and the
29
+ * next leg of the session finds the same object as long as any child still reads it. In-process only — a session
30
+ * continued on another Runner instance or in another process binds that Runner's seat.
31
+ */
32
+ export declare class SessionModeSeats {
33
+ private readonly seats;
34
+ private readonly reaper;
35
+ private seatOf;
36
+ /**
37
+ * A leg's ceiling over its session's seat, the leg's commit (once its prepare succeeded: the leg becomes the
38
+ * session's current leg) and its end (once the leg has STOPPED — its loop settled, not merely cancelled: a cancelled
39
+ * leg whose tool is still pending keeps answering its own children).
40
+ */
41
+ legCeiling(sessionId: string, leg: {
42
+ readonly current: PermissionMode;
43
+ }): {
44
+ ceiling: LegModeCeiling;
45
+ commit: () => void;
46
+ end: () => void;
47
+ };
48
+ }
@@ -0,0 +1,73 @@
1
+ class Seat {
2
+ sessionId;
3
+ leg;
4
+ holders = new Set();
5
+ constructor(sessionId) {
6
+ this.sessionId = sessionId;
7
+ }
8
+ get current() {
9
+ return this.leg?.current ?? "default";
10
+ }
11
+ addHolder(holder, via, signal) {
12
+ if (signal.aborted)
13
+ return;
14
+ const entry = { holder, via };
15
+ this.holders.add(entry);
16
+ signal.addEventListener("abort", () => this.holders.delete(entry), { once: true });
17
+ }
18
+ moveCeiling(affected, move) {
19
+ const before = [...this.holders].filter((e) => affected(e.via)).map((e) => [e.holder, e.holder.receipt()]);
20
+ move();
21
+ for (const [holder, receipt] of before)
22
+ holder.ceilingRebound(receipt);
23
+ }
24
+ }
25
+ class LegCeiling {
26
+ leg;
27
+ seat;
28
+ live = true;
29
+ constructor(leg, seat) {
30
+ this.leg = leg;
31
+ this.seat = seat;
32
+ }
33
+ get sessionId() {
34
+ return this.seat.sessionId;
35
+ }
36
+ get current() {
37
+ return this.live ? this.leg.current : this.seat.current;
38
+ }
39
+ hold(holder, signal) {
40
+ this.seat.addHolder(holder, this, signal);
41
+ }
42
+ commit() {
43
+ if (this.seat.leg === this.leg)
44
+ return;
45
+ this.seat.moveCeiling((via) => !via.live, () => (this.seat.leg = this.leg));
46
+ }
47
+ end() {
48
+ if (!this.live)
49
+ return;
50
+ this.seat.moveCeiling((via) => via === this, () => (this.live = false));
51
+ }
52
+ }
53
+ export class SessionModeSeats {
54
+ seats = new Map();
55
+ reaper = new FinalizationRegistry(({ sessionId, ref }) => {
56
+ if (this.seats.get(sessionId) === ref)
57
+ this.seats.delete(sessionId);
58
+ });
59
+ seatOf(sessionId) {
60
+ const live = this.seats.get(sessionId)?.deref();
61
+ if (live !== undefined)
62
+ return live;
63
+ const seat = new Seat(sessionId);
64
+ const ref = new WeakRef(seat);
65
+ this.seats.set(sessionId, ref);
66
+ this.reaper.register(seat, { sessionId, ref });
67
+ return seat;
68
+ }
69
+ legCeiling(sessionId, leg) {
70
+ const ceiling = new LegCeiling(leg, this.seatOf(sessionId));
71
+ return { ceiling, commit: () => ceiling.commit(), end: () => ceiling.end() };
72
+ }
73
+ }
@@ -2,6 +2,7 @@ import { canonicalizeTarget, isWinFormPath, pathFamilyOf, writeTargetPath } from
2
2
  import { createCoarseCommandNamePolicy, namespacedCoveringEntries, namespacedCoveringHit } from "../tool-policy.js";
3
3
  import { continuesLineage } from "../session-policy-store.js";
4
4
  import { thrownText } from "../thrown-value.js";
5
+ import { toolNameSetHas } from "../tool-roster.js";
5
6
  import { ruleDenialSentence } from "../rule-denial-sentence.js";
6
7
  import { DEFAULT_GATE_AWAIT_TIMEOUT_MS, gateWaitFailureOf, withGateTimeout } from "../gate-bounded-await.js";
7
8
  import { declaredPathTargetOf, isProtectedWrite, pathConfinableWriteToolNames, skillScopeWriteToolNames } from "../tool-registry.js";
@@ -136,9 +137,9 @@ export function createSessionRulePolicy(rules, opts) {
136
137
  async check(req, signal) {
137
138
  const { toolDeny, toolAllow, toolDenyCovering, toolAllowCovering, cmdPolicy, allowDirs } = compile(await readRules(req.toolCallId, signal));
138
139
  const toolName = req.toolName;
139
- if (toolDeny.has(toolName) || namespacedCoveringHit(toolDenyCovering, toolName))
140
+ if (toolNameSetHas(toolDeny, req) || namespacedCoveringHit(toolDenyCovering, toolName))
140
141
  return deny(req.toolName);
141
- if (toolAllow && !toolAllow.has(toolName) && !namespacedCoveringHit(toolAllowCovering, toolName)) {
142
+ if (toolAllow && !toolNameSetHas(toolAllow, req) && !namespacedCoveringHit(toolAllowCovering, toolName)) {
142
143
  return deny(req.toolName, "it is not in the session-rule allowlist");
143
144
  }
144
145
  if (cmdPolicy) {
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The TaskStream façade's PERMISSION-MODE verb: `setPermissionMode` — the one entrance a running leg's mode changes
3
+ * through (a shell's mode toggle, a serving layer's `permission_mode` control frame). ONE read of the argument at the
4
+ * wire boundary, the steer lane's liveness gate, then the leg's live mode seat: the requested word is rewritten, the
5
+ * fold against the deployment's deny bits and the classifier face is re-judged (a fold is announced by the seat), a
6
+ * leg that became `auto` arms its classifier, and the same three-member receipt the wiring manifest carries comes
7
+ * back — EVERY call answers one, whether or not the effective mode moved, so a caller never has to render
8
+ * optimistically. The next tool call reads the new mode at the gate's arbitration point; a child leg derives its own
9
+ * effective mode from this seat on its next read (no forwarding frame).
10
+ */
11
+ import type { TaskStream } from "../types.js";
12
+ import type { LiveHandle, TaskStreamLiveSeat } from "./contracts.js";
13
+ export interface StreamModeVerbInput {
14
+ /** borrowed-readonly — the stream's live state: `resultValue` (the finished-task refusal) and `handle` (the mode seat rides it). */
15
+ live: TaskStreamLiveSeat;
16
+ /** borrowed-readonly — the readiness promise the verb awaits before it touches the handle. */
17
+ ready: Promise<LiveHandle | undefined>;
18
+ /** borrowed-readonly — the driver's timeout race: a wedged prepare cannot hang the verb on `ready`. */
19
+ orTimeout: <T>(p: Promise<T>) => Promise<T | undefined>;
20
+ /** borrowed-readonly — the driver's refusal factory, the steer verb's own `steeringError` seat. */
21
+ steeringError: (msg: string, code?: string) => Error;
22
+ }
23
+ export interface StreamModeVerbResult {
24
+ /** The stream's `setPermissionMode` verb. */
25
+ setPermissionMode: TaskStream["setPermissionMode"];
26
+ }
27
+ export declare function streamModeVerb(input: StreamModeVerbInput): StreamModeVerbResult;
@@ -0,0 +1,19 @@
1
+ import { isPermissionMode, PERMISSION_MODES } from "../ask-origin.js";
2
+ export function streamModeVerb(input) {
3
+ const { live, ready, orTimeout, steeringError } = input;
4
+ const setPermissionMode = async (mode) => {
5
+ const modeIn = mode;
6
+ if (!isPermissionMode(modeIn)) {
7
+ const e = new Error(`cannot set the permission mode: it must be one of ${PERMISSION_MODES.join("/")} (got ${JSON.stringify(modeIn)})`);
8
+ e.code = "steering.invalid_content";
9
+ throw e;
10
+ }
11
+ if (live.resultValue)
12
+ throw steeringError("the task has already finished");
13
+ const h = live.handle ?? (await orTimeout(ready));
14
+ if (!h)
15
+ throw steeringError("the task is not running");
16
+ return h.permissionMode.steer(modeIn);
17
+ };
18
+ return { setPermissionMode };
19
+ }
@@ -419,9 +419,9 @@ export interface AttachmentInputs {
419
419
  todoMounted: boolean;
420
420
  /** Whether the task-family tools are mounted. Both mounted ⇒ task_reminder wins (CC isTodoV2 branch). */
421
421
  taskListMounted: boolean;
422
- /** Live plan-mode flag (planModeRef.active). One-way in sema: exit re-prepares the run, so a false
423
- * here simply stops the producer — no re-entry cycle reset is needed (documented deviation from CC's
424
- * countPlanModeAttachmentsSinceLastExit, which sema's lifecycle makes unreachable). */
422
+ /** Whether the leg's effective permission mode is `plan` right now (the one mode seat, whichever entrance set it:
423
+ * the spec's word, a steer, the model's EnterPlanMode). A false here simply stops the producer — no re-entry cycle
424
+ * reset (documented deviation from CC's countPlanModeAttachmentsSinceLastExit). */
425
425
  planActive: boolean;
426
426
  /** SR-3 budget_usd lane (CC 198 `QFm` producer :480424-480428 + renderer :600225; 220 `_Fy` @609096
427
427
  * with the mount at @607474): the run's cost progress against its $ ceiling, in USD. Only passed
@@ -761,7 +761,7 @@ function mcpDroppedToolsBound(inp) {
761
761
  const single = first === undefined ? 0 : MCP_DROPPED_FIXED_MAX_BYTES + bytes(first.tool) + bytes(first.server) + bytes(first.reason) + 32;
762
762
  return Math.max(MCP_DROPPED_BATCH_MAX_BYTES, single);
763
763
  }
764
- const PLAN_MODE_FULL_BODY = `Plan mode is active. The user indicated that they do not want you to execute yet -- you MUST NOT make any edits, run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system. This supercedes any other instructions you have received. Instead, you should: research and analyze with read-only tools, and when you have formed a concrete plan, present it with the ${PRESENT_PLAN_TOOL_NAME} tool for approval. That tool inherently requests plan approval — never ask for approval via plain text.`;
764
+ const PLAN_MODE_FULL_BODY = `Plan mode is active. The user indicated that they do not want you to execute yet -- you MUST NOT make any edits, run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system — every such call asks the user first, and is refused where nobody can answer. This supercedes any other instructions you have received. Instead, you should: research and analyze with read-only tools, and when you have formed a concrete plan, present it with the ${PRESENT_PLAN_TOOL_NAME} tool for approval. That tool inherently requests plan approval — never ask for approval via plain text.`;
765
765
  const PLAN_MODE_SPARSE_BODY = `Plan mode still active (see full instructions earlier in conversation). Read-only: no edits, commits, or config changes. When your plan is ready, present it with the ${PRESENT_PLAN_TOOL_NAME} tool for approval. Never ask about plan approval via plain text.`;
766
766
  const TRUNCATION_MARKER = "\n…[truncated]";
767
767
  const MIN_KEEP_BYTES = 160;
@@ -24,6 +24,12 @@ import type { ExecutionEnv } from "../../internal/harness.js";
24
24
  import type { RunnerDeps } from "../runner-deps.js";
25
25
  import type { ToolRoster } from "../tool-roster.js";
26
26
  import { type WriteProtectionMount } from "../write-protect.js";
27
+ import type { ShellReadBoundary } from "../../tools/fs/shell-read-judge.js";
28
+ /** The leg's mount plus the same judge over a bare ABSOLUTE target — what the shell station asks about each write target
29
+ * a command names (`ShellReadBoundary.writeProtectionJudge`): `true` ⇔ a row covers it. */
30
+ export interface LegWriteProtection extends WriteProtectionMount {
31
+ readonly judgeTarget: NonNullable<ShellReadBoundary["writeProtectionJudge"]>;
32
+ }
27
33
  export declare function mountWriteProtection(input: {
28
34
  /** borrowed-readonly — the deployment seats: the table (`writeProtectedPaths`; absent ⇒ the default
29
35
  * table, `[]` ⇒ opted out), the data root the memory library is materialized under
@@ -38,4 +44,4 @@ export declare function mountWriteProtection(input: {
38
44
  roster: ToolRoster;
39
45
  sessionId: string;
40
46
  runId: string;
41
- }): WriteProtectionMount | undefined;
47
+ }): LegWriteProtection | undefined;
@@ -1,10 +1,10 @@
1
1
  import { deliverEngineNotice } from "../engine-notice.js";
2
2
  import { protectivePathTargetOf } from "../tool-registry.js";
3
- import { createWriteProtectionCheck } from "../write-protect.js";
3
+ import { compileWriteProtection, writeProtectionMountOver } from "../write-protect.js";
4
4
  const ABSENCE_REASON = { symlink: "unresolved-symlink", target: "unresolvable-target" };
5
5
  export function mountWriteProtection(input) {
6
6
  const announced = new Set();
7
- const mount = createWriteProtectionCheck(input.deps.writeProtectedPaths, {
7
+ const matcher = compileWriteProtection(input.deps.writeProtectedPaths, {
8
8
  ...(input.deps.memoryEngineDir !== undefined ? { dataRoot: input.deps.memoryEngineDir } : {}),
9
9
  ...(input.env !== undefined ? { env: input.env } : {}),
10
10
  rootPath: input.rootPath,
@@ -25,7 +25,10 @@ export function mountWriteProtection(input) {
25
25
  });
26
26
  },
27
27
  });
28
- if (mount === undefined || mount.targetView !== "spelling-only")
28
+ if (matcher === undefined)
29
+ return undefined;
30
+ const mount = { ...writeProtectionMountOver(matcher, input.rootPath), judgeTarget: async (target, opts) => (await matcher.judge(target, { ...(opts?.signal !== undefined ? { signal: opts.signal } : {}), ...(opts?.canonical === true ? { canonical: true } : {}) })).kind === "hit" };
31
+ if (mount.targetView !== "spelling-only")
29
32
  return mount;
30
33
  const covered = input.roster.entries
31
34
  .filter((e) => [e.name, ...e.aliases].some((name) => {