@sema-agent/core 7.33.8 → 7.34.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 (197) hide show
  1. package/CHANGELOG.md +339 -0
  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/open-responses.js +1 -0
  11. package/dist/brain/openai.js +1 -0
  12. package/dist/brain/stream-engine.d.ts +3 -0
  13. package/dist/brain/stream-engine.js +2 -1
  14. package/dist/core/a2a.d.ts +3 -1
  15. package/dist/core/a2a.js +1 -1
  16. package/dist/core/ask-origin.d.ts +351 -154
  17. package/dist/core/ask-origin.js +223 -45
  18. package/dist/core/ask-unresolvable-notice.d.ts +5 -2
  19. package/dist/core/ask-unresolvable-notice.js +4 -3
  20. package/dist/core/auto-mode-arming.d.ts +17 -4
  21. package/dist/core/auto-mode-arming.js +31 -6
  22. package/dist/core/auto-mode-defaults.d.ts +16 -7
  23. package/dist/core/auto-mode-defaults.js +2 -1
  24. package/dist/core/auto-mode-directive.d.ts +18 -30
  25. package/dist/core/auto-mode-directive.js +9 -18
  26. package/dist/core/auto-mode-prompt.d.ts +62 -1
  27. package/dist/core/auto-mode-prompt.js +42 -4
  28. package/dist/core/auto-mode-rebuild.d.ts +2 -1
  29. package/dist/core/auto-mode-rebuild.js +1 -0
  30. package/dist/core/auto-mode.d.ts +32 -3
  31. package/dist/core/auto-mode.js +80 -41
  32. package/dist/core/checkpoint-store.d.ts +50 -35
  33. package/dist/core/checkpoint-store.js +58 -3
  34. package/dist/core/child-clamp-seats.js +1 -0
  35. package/dist/core/classifier-unavailable-arm.d.ts +57 -0
  36. package/dist/core/classifier-unavailable-arm.js +17 -0
  37. package/dist/core/effective-path-target.d.ts +12 -0
  38. package/dist/core/effective-path-target.js +18 -7
  39. package/dist/core/engine-notice.d.ts +53 -2
  40. package/dist/core/fs-write-gate-policy.d.ts +5 -1
  41. package/dist/core/fs-write-gate-policy.js +4 -1
  42. package/dist/core/gate-fold.js +17 -1
  43. package/dist/core/gate-lanes.js +111 -46
  44. package/dist/core/gate-outcome.d.ts +40 -7
  45. package/dist/core/gate-outcome.js +26 -4
  46. package/dist/core/governance-codes.d.ts +1 -1
  47. package/dist/core/governance-codes.js +12 -0
  48. package/dist/core/hooks.d.ts +50 -29
  49. package/dist/core/hooks.js +1 -3
  50. package/dist/core/mcp.js +1 -1
  51. package/dist/core/mode-policy.d.ts +182 -0
  52. package/dist/core/mode-policy.js +179 -0
  53. package/dist/core/oracle-isolation.d.ts +3 -3
  54. package/dist/core/permission-rule-model.d.ts +16 -3
  55. package/dist/core/permission-rule-model.js +36 -3
  56. package/dist/core/permission-rule-provider.d.ts +22 -11
  57. package/dist/core/permission-rule-provider.js +17 -7
  58. package/dist/core/permission-rules.d.ts +23 -1
  59. package/dist/core/permission-rules.js +35 -8
  60. package/dist/core/persisted-rule-arms.d.ts +10 -9
  61. package/dist/core/persisted-rule-arms.js +9 -6
  62. package/dist/core/present-plan-tool.d.ts +1 -1
  63. package/dist/core/present-plan-tool.js +3 -3
  64. package/dist/core/read-only-shell.d.ts +19 -19
  65. package/dist/core/read-only-shell.js +38 -12
  66. package/dist/core/retired-keys.d.ts +9 -3
  67. package/dist/core/retired-keys.js +5 -1
  68. package/dist/core/runner/classifier-ladder.d.ts +43 -1
  69. package/dist/core/runner/classifier-ladder.js +29 -0
  70. package/dist/core/runner/contracts.d.ts +37 -35
  71. package/dist/core/runner/denial-limit-arms.d.ts +30 -7
  72. package/dist/core/runner/denial-limit-arms.js +15 -9
  73. package/dist/core/runner/env-capability-census.js +1 -1
  74. package/dist/core/runner/gate-exit.d.ts +30 -4
  75. package/dist/core/runner/gate-exit.js +28 -7
  76. package/dist/core/runner/permission-rule-lanes.d.ts +9 -5
  77. package/dist/core/runner/permission-rule-lanes.js +44 -40
  78. package/dist/core/runner/persisted-path-rows.d.ts +64 -0
  79. package/dist/core/runner/persisted-path-rows.js +89 -0
  80. package/dist/core/runner/prepare-artifact.d.ts +5 -1
  81. package/dist/core/runner/prepare-artifact.js +9 -3
  82. package/dist/core/runner/prepare-ask-lane.d.ts +5 -3
  83. package/dist/core/runner/prepare-ask-lane.js +1 -1
  84. package/dist/core/runner/prepare-caps-and-workflow.d.ts +39 -13
  85. package/dist/core/runner/prepare-caps-and-workflow.js +58 -29
  86. package/dist/core/runner/prepare-gate-stations.d.ts +33 -6
  87. package/dist/core/runner/prepare-gate-stations.js +32 -19
  88. package/dist/core/runner/prepare-hands-readface.d.ts +40 -17
  89. package/dist/core/runner/prepare-hands-readface.js +64 -53
  90. package/dist/core/runner/prepare-inherited-gate.d.ts +22 -22
  91. package/dist/core/runner/prepare-inherited-gate.js +37 -23
  92. package/dist/core/runner/prepare-park-ask.d.ts +2 -2
  93. package/dist/core/runner/prepare-park-ask.js +2 -1
  94. package/dist/core/runner/prepare-policy-chain.d.ts +22 -2
  95. package/dist/core/runner/prepare-policy-chain.js +39 -12
  96. package/dist/core/runner/prepare-protocol-tools.d.ts +28 -0
  97. package/dist/core/runner/prepare-protocol-tools.js +13 -3
  98. package/dist/core/runner/prepare-run-refs.d.ts +6 -6
  99. package/dist/core/runner/prepare-run-refs.js +9 -4
  100. package/dist/core/runner/prepare-safety-scan.d.ts +5 -0
  101. package/dist/core/runner/prepare-safety-scan.js +19 -3
  102. package/dist/core/runner/prepare-suspend-saga.d.ts +5 -5
  103. package/dist/core/runner/prepare-suspend-saga.js +6 -6
  104. package/dist/core/runner/prepare-task.js +51 -22
  105. package/dist/core/runner/prepare-wiring-manifest.d.ts +10 -6
  106. package/dist/core/runner/prepare-wiring-manifest.js +5 -3
  107. package/dist/core/runner/read-root-grant.d.ts +9 -0
  108. package/dist/core/runner/read-root-grant.js +12 -0
  109. package/dist/core/runner/retired-runner-deps.js +1 -0
  110. package/dist/core/runner/run-identity-wiring.js +1 -1
  111. package/dist/core/runner/run-leg.js +24 -15
  112. package/dist/core/runner/run-notification-lane.js +1 -0
  113. package/dist/core/runner/run-telemetry-and-budget-seats.d.ts +2 -2
  114. package/dist/core/runner/run-telemetry-and-budget-seats.js +1 -1
  115. package/dist/core/runner/run-turn-boundary.js +1 -1
  116. package/dist/core/runner/runtask.js +3 -0
  117. package/dist/core/runner/stream-mode-verb.d.ts +27 -0
  118. package/dist/core/runner/stream-mode-verb.js +19 -0
  119. package/dist/core/runner/turn-attachments.d.ts +3 -3
  120. package/dist/core/runner/turn-attachments.js +1 -1
  121. package/dist/core/runner/write-protection-station.d.ts +7 -1
  122. package/dist/core/runner/write-protection-station.js +6 -3
  123. package/dist/core/runner-deps.d.ts +31 -34
  124. package/dist/core/runtime-caps.d.ts +12 -3
  125. package/dist/core/safety-axis-vocab.d.ts +2 -2
  126. package/dist/core/safety-axis-vocab.js +1 -1
  127. package/dist/core/sensitive-path-policy.d.ts +4 -2
  128. package/dist/core/sensitive-path-policy.js +39 -30
  129. package/dist/core/task-event.d.ts +1 -1
  130. package/dist/core/task-result.d.ts +1 -1
  131. package/dist/core/task-spec.d.ts +41 -89
  132. package/dist/core/task-stream.d.ts +16 -0
  133. package/dist/core/terminal-cause.js +1 -0
  134. package/dist/core/tighten-task-spec.d.ts +2 -2
  135. package/dist/core/tighten-task-spec.js +12 -14
  136. package/dist/core/tool-catalog-entries.js +4 -3
  137. package/dist/core/tool-policy.d.ts +54 -25
  138. package/dist/core/tool-policy.js +43 -2
  139. package/dist/core/tool-registry.d.ts +2 -0
  140. package/dist/core/tool-registry.js +3 -0
  141. package/dist/core/tool-spec.d.ts +8 -9
  142. package/dist/core/trace.d.ts +7 -6
  143. package/dist/core/types.d.ts +2 -1
  144. package/dist/core/wiring-manifest.d.ts +31 -5
  145. package/dist/core/wiring-manifest.js +28 -1
  146. package/dist/core/write-protect.d.ts +11 -1
  147. package/dist/core/write-protect.js +26 -15
  148. package/dist/engine/execution-env/node-execution-env.d.ts +3 -0
  149. package/dist/engine/execution-env/node-execution-env.js +11 -1
  150. package/dist/engine/harness/types.d.ts +6 -7
  151. package/dist/engine/llm/types.d.ts +2 -4
  152. package/dist/engine/loop/types.d.ts +7 -0
  153. package/dist/index.d.ts +15 -5
  154. package/dist/index.js +12 -3
  155. package/dist/orchestration/workflow-governance.js +1 -1
  156. package/dist/orchestration/workflow-primitives.d.ts +1 -1
  157. package/dist/orchestration/workflow-size-guideline.js +2 -2
  158. package/dist/tools/artifact/artifact-tool.d.ts +17 -0
  159. package/dist/tools/artifact/artifact-tool.js +16 -1
  160. package/dist/tools/fs/bash-readonly-classifier.d.ts +28 -7
  161. package/dist/tools/fs/bash-readonly-classifier.js +88 -0
  162. package/dist/tools/fs/containment-face.d.ts +9 -3
  163. package/dist/tools/fs/containment-face.js +2 -0
  164. package/dist/tools/fs/file-read-check.d.ts +49 -0
  165. package/dist/tools/fs/file-read-check.js +71 -0
  166. package/dist/tools/fs/fs-bash.d.ts +3 -3
  167. package/dist/tools/fs/fs-read.d.ts +1 -0
  168. package/dist/tools/fs/fs-search-tools.d.ts +2 -2
  169. package/dist/tools/fs/fs-search-tools.js +4 -4
  170. package/dist/tools/fs/fs-shared.js +20 -6
  171. package/dist/tools/fs/fs-write.d.ts +4 -3
  172. package/dist/tools/fs/fs-write.js +9 -9
  173. package/dist/tools/fs/index.d.ts +39 -3
  174. package/dist/tools/fs/index.js +38 -15
  175. package/dist/tools/fs/read-deny-home.d.ts +93 -0
  176. package/dist/tools/fs/read-deny-home.js +225 -0
  177. package/dist/tools/fs/read-deny.d.ts +176 -34
  178. package/dist/tools/fs/read-deny.js +268 -61
  179. package/dist/tools/fs/safety.d.ts +6 -1
  180. package/dist/tools/fs/safety.js +18 -5
  181. package/dist/tools/fs/search.d.ts +3 -0
  182. package/dist/tools/fs/search.js +47 -9
  183. package/dist/tools/fs/shell-read-check.d.ts +42 -11
  184. package/dist/tools/fs/shell-read-check.js +188 -13
  185. package/dist/tools/fs/shell-read-establish.d.ts +7 -3
  186. package/dist/tools/fs/shell-read-establish.js +134 -21
  187. package/dist/tools/fs/shell-read-identity.d.ts +7 -0
  188. package/dist/tools/fs/shell-read-identity.js +21 -0
  189. package/dist/tools/fs/shell-read-judge.d.ts +92 -32
  190. package/dist/tools/fs/shell-read-judge.js +43 -18
  191. package/dist/tools/fs/shell-read-plan.d.ts +4 -0
  192. package/dist/tools/fs/shell-read-plan.js +26 -4
  193. package/dist/tools/fs/shell-read-write-targets.d.ts +73 -0
  194. package/dist/tools/fs/shell-read-write-targets.js +173 -0
  195. package/package.json +1 -1
  196. package/test/export-surface.snapshot.json +103 -11
  197. package/wire-changes/core@7.34.0.json +2621 -0
@@ -1,14 +1,17 @@
1
1
  import { attestClassifierDenyCause, classifierDenyCauseOf, decisionText, describeThrown } from "./tool-policy.js";
2
- import { classifierMayAnswer, classifierMayAnswerMandate, mandateDisclosureOf, persistedRuleMandateOf, ruleClearanceOf, safetyAxisOf, sandboxMayAdmitMandate, stampAskOrigin } from "./ask-origin.js";
3
- import { classifierParseFailureDenyMessage, classifierUnavailableDenyMessage, denialLimitFallbackMessage, unarmedWindow } from "./auto-mode.js";
2
+ import { arbitrateByMode, classifyAsk, fileCommandOptionsSentence, mandateDisclosureOf, persistedRuleMandateOf, ruleClearanceOf, safetyAxisOf, stampAskOrigin } from "./ask-origin.js";
3
+ import { classifierParseFailureDenyMessage, denialLimitFallbackMessage, unarmedWindow } from "./auto-mode.js";
4
+ import { classifierUnavailableArm } from "./classifier-unavailable-arm.js";
4
5
  import { SETTLEMENT_NOBODY_JUDGED } from "./gate-outcome.js";
5
6
  import { inlineUntrusted } from "./untrusted-text.js";
6
7
  import { isRuleBehavior } from "./permission-rule-model.js";
7
8
  import { ASK_USER_QUESTION_TOOL_NAME } from "./ask-question.js";
8
9
  import { applyPersistedTightening, disclosedRuleSet } from "./persisted-rule-arms.js";
9
- import { readOnlyShellVerdict } from "./read-only-shell.js";
10
+ import { CD_GIT_COMPOUND_SENTENCE, changesDirectoryBeforeGit, readOnlyShellVerdict } from "./read-only-shell.js";
11
+ import { readShellCommand } from "./shell-lexer.js";
10
12
  import { COMMAND_RULE_TOOL } from "./permission-rule-model.js";
11
13
  import { catalogRuleFaceOf } from "./tool-registry.js";
14
+ import { modeAcceptsFileCommand } from "./mode-policy.js";
12
15
  import { ORG_ADJUDICATION_TIMEOUT_MS, ORG_RULE_DECISION_REASON, ORG_UNAVAILABLE_DECISION_REASON, settleOrgVerdictWithin } from "./permission-rule-org.js";
13
16
  import { exitGate } from "./runner/gate-exit.js";
14
17
  import { traceHookCrash } from "./gate-crash-trace.js";
@@ -165,14 +168,19 @@ export async function runGateLanes(pass) {
165
168
  writeProtected: pass.writeProtected === true,
166
169
  writeProtectionUnresolved: pass.writeProtectionUnresolved === true,
167
170
  });
168
- const askOriginFacts = (org, ruleStore) => ({
171
+ const askOriginFacts = (org, ruleCallUnreadable) => ({
169
172
  contentQuestion: toolName === ASK_USER_QUESTION_TOOL_NAME,
170
173
  markedUnresolvable: markedUnresolvable(),
171
174
  org,
172
- ...(ruleStore !== undefined ? { ruleStore } : {}),
175
+ ...(ruleCallUnreadable ? { ruleCallUnreadable } : {}),
173
176
  tightened: pass.tightenedBy,
174
177
  mandateMarks: mandateMarksNow(),
178
+ ...(input.toolMarkSources !== undefined ? { toolMarkSources: input.toolMarkSources } : {}),
179
+ ...(input.a2aIsolatePeers === true ? { a2aIsolatePeers: true } : {}),
175
180
  });
181
+ const classifyStanding = (unstamped, facts) => {
182
+ pass.askClassification = classifyAsk(unstamped, facts);
183
+ };
176
184
  let orgTightenCount = 0;
177
185
  let orgEvidence = (() => {
178
186
  const w = input.orgRules === undefined ? "not_wired" : "not_adjudicated";
@@ -208,18 +216,14 @@ export async function runGateLanes(pass) {
208
216
  orgEvidence = { revisionAbsent: "unavailable", ruleAbsent: "unavailable" };
209
217
  orgRealApprovalRequired = true;
210
218
  await notifier.notifyAsync(() => input.orgRules?.onUnavailable?.({ toolName, toolCallId, message: answer.disclosures.join("; ") }), "toolGate.orgSnapshotUnavailable");
219
+ const orgUnavailableMessage = `tool "${toolName}" needs approval: this deployment is org-governed and cannot currently adjudicate ` +
220
+ `against an organization policy snapshot — every allow tightens to a real approval until it can`;
211
221
  if (decided.action === "allow") {
212
- decided = {
213
- action: "ask",
214
- message: `tool "${toolName}" needs approval: this deployment is org-governed and cannot currently adjudicate ` +
215
- `against an organization policy snapshot — every allow tightens to a real approval until it can`,
216
- decisionReason: ORG_UNAVAILABLE_DECISION_REASON,
217
- requiresRealApproval: true,
218
- };
222
+ decided = { action: "ask", message: orgUnavailableMessage, decisionReason: ORG_UNAVAILABLE_DECISION_REASON, requiresRealApproval: true };
219
223
  orgTightenCount += 1;
220
224
  }
221
225
  else if (decided.action === "ask" && decided.requiresRealApproval !== true) {
222
- decided = { ...decided, requiresRealApproval: true };
226
+ decided = { ...decided, requiresRealApproval: true, ...(pass.tightenedBy !== undefined ? { message: orgUnavailableMessage } : {}) };
223
227
  orgTightenCount += 1;
224
228
  }
225
229
  return { decision: decided, org: "unavailable" };
@@ -241,23 +245,20 @@ export async function runGateLanes(pass) {
241
245
  return { decision: decided, org: undefined };
242
246
  orgTightenCount += 1;
243
247
  orgRealApprovalRequired = true;
248
+ const orgRuleMessage = unreadable !== undefined
249
+ ? `an organization policy rule (${rule}) may reach this call, which could not be read against it (${unreadable}) — a person must decide`
250
+ : `an organization policy rule (${rule}) requires approval for this call`;
244
251
  return {
245
252
  decision: decided.action === "ask"
246
- ? { ...decided, requiresRealApproval: true }
247
- : {
248
- action: "ask",
249
- message: unreadable !== undefined
250
- ? `an organization policy rule (${rule}) may reach this call, which could not be read against it (${unreadable}) — a person must decide`
251
- : `an organization policy rule (${rule}) requires approval for this call`,
252
- decisionReason: ORG_RULE_DECISION_REASON,
253
- requiresRealApproval: true,
254
- },
253
+ ?
254
+ { ...decided, requiresRealApproval: true, ...(decided.requiresRealApproval !== true && pass.tightenedBy !== undefined ? { message: orgRuleMessage } : {}) }
255
+ : { action: "ask", message: orgRuleMessage, decisionReason: ORG_RULE_DECISION_REASON, requiresRealApproval: true },
255
256
  org: "rule",
256
257
  };
257
258
  };
258
259
  const orgAnswer = await applyOrgLayer(pass.decision, pass.policyRewrite !== undefined ? pass.policyRewrite : pass.currentInput);
259
260
  pass.decision = orgAnswer.decision;
260
- const ruleStoreUnreadableOf = (lane) => lane?.unreadable === true ? (lane.reason !== undefined ? "call" : "store") : undefined;
261
+ const ruleCallUnreadableOf = (lane) => lane?.unreadable === true && lane.reason !== undefined;
261
262
  const readPersistedRules = async (req) => {
262
263
  let pendingHit;
263
264
  try {
@@ -348,10 +349,10 @@ export async function runGateLanes(pass) {
348
349
  };
349
350
  }
350
351
  }
351
- const originFacts = askOriginFacts(orgAnswer.org, ruleStoreUnreadableOf(laneAnswer));
352
+ const originFacts = askOriginFacts(orgAnswer.org, ruleCallUnreadableOf(laneAnswer));
352
353
  if (pass.decision.action === "ask") {
353
- const { ruleStoreUnreadable: _selfDeclared, ...unstamped } = pass.decision;
354
- const stamped = { ...unstamped, ruleEvidence: mintRuleEvidence(personalEvidence), ...(laneCoverage !== undefined ? { segmentCoverage: laneCoverage } : {}), ...(originFacts.ruleStore !== undefined ? { ruleStoreUnreadable: originFacts.ruleStore } : {}) };
354
+ const stamped = { ...pass.decision, ruleEvidence: mintRuleEvidence(personalEvidence), ...(laneCoverage !== undefined ? { segmentCoverage: laneCoverage } : {}) };
355
+ classifyStanding(stamped, originFacts);
355
356
  pass.decision = stampAskOrigin(stamped, originFacts);
356
357
  }
357
358
  if (allowLayerMayClear() && pass.decision.action === "ask" && ruleMayClear() && pass.probeBoundaryDeclined !== true && pass.req.toolName === COMMAND_RULE_TOOL) {
@@ -359,7 +360,8 @@ export async function runGateLanes(pass) {
359
360
  const args = pass.req.args;
360
361
  const command = typeof args === "object" && args !== null && !Array.isArray(args) ? args[commandParam] : undefined;
361
362
  const backgrounded = typeof args === "object" && args !== null && args.run_in_background === true;
362
- if (typeof command === "string" && !backgrounded && readOnlyShellVerdict(command).readOnly) {
363
+ const shape = typeof command === "string" ? readShellCommand(command) : undefined;
364
+ if (typeof command === "string" && shape !== undefined && !backgrounded && readOnlyShellVerdict(command, shape, input.trackedCwd?.()).readOnly) {
363
365
  pass.decision = {
364
366
  action: "allow",
365
367
  message: "the command is read-only (upstream's read-only command tables) — allowed without asking",
@@ -368,15 +370,21 @@ export async function runGateLanes(pass) {
368
370
  };
369
371
  await notifier.notifyAsync(() => input.onReadOnlyAllowed?.({ toolName: pass.req.toolName, toolCallId, command }), "toolGate.readOnlyAllowed");
370
372
  }
373
+ else if (shape !== undefined && pass.tightenedBy === "shell_gate" && changesDirectoryBeforeGit(shape, input.trackedCwd?.())) {
374
+ pass.decision = { ...pass.decision, message: CD_GIT_COMPOUND_SENTENCE };
375
+ }
371
376
  }
372
377
  if (pass.decision.action === "ask")
373
378
  ledger.supersede();
374
- if (input.autoMode && pass.decision.action === "ask" && pass.decision.origin !== undefined && classifierMayAnswer(pass.decision.origin) && classifierMayAnswerMandate(persistedRuleMandate)) {
375
- const verdict = await input.autoMode.decider
376
- .decide({ req: pass.req, askMessage: decisionText(pass.decision) }, input.abortSignal)
379
+ if (input.autoMode && pass.decision.action === "ask" && pass.decision.origin !== undefined && pass.askClassification?.row.classifierEligible === true && (input.permissionMode === undefined || input.permissionMode() === "auto")) {
380
+ const classifySignal = callSignal ?? input.abortSignal;
381
+ const heard = await input.autoMode.decider
382
+ .decide({ req: pass.req, askMessage: decisionText(pass.decision) }, classifySignal)
377
383
  .catch(() => ({ kind: "unavailable", cause: "error" }));
378
- pass.classifierRound = verdict.classifier;
379
- if (verdict.kind === "allow") {
384
+ const verdict = heard.kind === "allow" && input.permissionMode !== undefined && input.permissionMode() !== "auto" ? undefined : heard;
385
+ if (verdict !== undefined)
386
+ pass.classifierRound = verdict.classifier;
387
+ if (verdict?.kind === "allow") {
380
388
  input.autoMode.denialTracking?.recordAllow();
381
389
  pass.decision = {
382
390
  action: "allow",
@@ -385,14 +393,17 @@ export async function runGateLanes(pass) {
385
393
  ...(pass.policyRewrite !== undefined ? { updatedInput: pass.policyRewrite } : {}),
386
394
  };
387
395
  }
388
- else if (verdict.kind === "unavailable") {
389
- pass.decision = attestClassifierDenyCause({
390
- action: "deny",
391
- message: classifierUnavailableDenyMessage(toolName, verdict.cause, verdict.classifier),
392
- decisionReason: "classifier",
393
- }, "unavailable", pass.req);
396
+ else if (verdict?.kind === "unavailable") {
397
+ const arm = classifierUnavailableArm({ ask: pass.decision, req: pass.req, cause: verdict.cause, attribution: verdict.classifier, seat: input.autoMode.unavailable, aborted: classifySignal?.aborted === true });
398
+ if (arm.kind === "asked") {
399
+ classifyStanding(arm.ask, originFacts);
400
+ pass.decision = stampAskOrigin(arm.ask, originFacts);
401
+ pass.classifierRound = undefined;
402
+ }
403
+ else
404
+ pass.decision = arm.deny;
394
405
  }
395
- else {
406
+ else if (verdict !== undefined) {
396
407
  const reason = verdict.kind === "block" ? (verdict.reason ? inlineUntrusted(verdict.reason) : "") : classifierParseFailureDenyMessage(verdict.classifier);
397
408
  const category = verdict.kind === "block" && verdict.category ? inlineUntrusted(verdict.category) : "";
398
409
  const tracked = input.autoMode.denialTracking?.recordBlock();
@@ -404,6 +415,7 @@ export async function runGateLanes(pass) {
404
415
  requiresRealApproval: true,
405
416
  denialLimitFallback: tracked.fallback,
406
417
  };
418
+ classifyStanding(fallbackAsk, originFacts);
407
419
  pass.decision = stampAskOrigin(fallbackAsk, originFacts);
408
420
  }
409
421
  else {
@@ -430,7 +442,7 @@ export async function runGateLanes(pass) {
430
442
  pass.hookAsk === undefined &&
431
443
  input.egress !== true &&
432
444
  input.peerMessage !== true &&
433
- sandboxMayAdmitMandate(persistedRuleMandate) &&
445
+ pass.askClassification?.row.sandboxAdmission === true &&
434
446
  pass.probeBoundaryDeclined !== true &&
435
447
  input.sandboxAdmission.boundaryCapable !== true) {
436
448
  const recorded = input.sandboxAdmission.askClassesOf(toolCallId);
@@ -447,6 +459,42 @@ export async function runGateLanes(pass) {
447
459
  };
448
460
  }
449
461
  }
462
+ if (pass.decision.action === "ask" && input.permissionMode !== undefined && pass.askClassification !== undefined) {
463
+ const standing = pass.decision;
464
+ const mode = input.permissionMode();
465
+ const fileCommandByWord = modeAcceptsFileCommand(mode, toolName, pass.policyRewrite ?? pass.currentInput);
466
+ const verdict = arbitrateByMode(mode, pass.askClassification, { ...(standing.origin !== undefined ? { origin: standing.origin } : {}), ...(standing.mandate !== undefined ? { mandate: standing.mandate } : {}) }, { fileCommand: pass.shellWritesPlaced === true && fileCommandByWord });
467
+ if (verdict.kind === "ask" && fileCommandByWord && pass.shellWritesPlaced !== true && pass.shellUnplacedOptions !== undefined && pass.askClassification.classes.size === 1 && pass.askClassification.classes.has("ordinary")) {
468
+ pass.decision = { ...standing, message: fileCommandOptionsSentence(pass.shellUnplacedOptions.program) };
469
+ }
470
+ if (verdict.kind === "accepted") {
471
+ ledger.supersede();
472
+ pass.decision = { action: "allow", message: `accepted by the ${verdict.mode} permission mode (one of its file commands)`, decisionReason: "mode", ...(pass.policyRewrite !== undefined ? { updatedInput: pass.policyRewrite } : {}) };
473
+ }
474
+ else if (verdict.kind === "released") {
475
+ pass.arbitration = verdict;
476
+ ledger.supersede();
477
+ await notifier.notifyAsync(() => input.onModeReleased?.({ toolName, toolCallId, release: { mode: verdict.mode, askClass: verdict.askClass, classes: verdict.classes, ...(verdict.origin !== undefined ? { origin: verdict.origin } : {}), ...(verdict.mandate !== undefined ? { mandate: verdict.mandate } : {}), ...(verdict.reason !== undefined ? { reason: verdict.reason } : {}) }, ...(pass.shellReadUnestablished === true ? { readUnestablished: true } : {}) }), "toolGate.onModeReleased");
478
+ pass.decision = {
479
+ action: "allow",
480
+ message: `released by the ${verdict.mode} permission mode (the ${verdict.askClass} ask was not put to anyone)`,
481
+ decisionReason: "mode",
482
+ ...(pass.policyRewrite !== undefined ? { updatedInput: pass.policyRewrite } : {}),
483
+ };
484
+ }
485
+ else if (verdict.kind === "denied_by_mode") {
486
+ pass.arbitration = verdict;
487
+ pass.deniedBy = "mode";
488
+ pass.denyCause = undefined;
489
+ pass.decision = {
490
+ action: "deny",
491
+ message: verdict.mode === "dontAsk"
492
+ ? `refused by the dontAsk permission mode: this call needed approval (${verdict.askClass}) and the mode answers no ask; run under a mode with an approver, or change the call`
493
+ : `refused by the bypassPermissions permission mode: the write-protection judge could not resolve this write's target, and with nobody to ask the mode refuses rather than releases it`,
494
+ decisionReason: "mode",
495
+ };
496
+ }
497
+ }
450
498
  const safety = safetyAxisOf({
451
499
  egress: input.egress,
452
500
  shellGated: input.shellGated,
@@ -466,8 +514,8 @@ export async function runGateLanes(pass) {
466
514
  ...(callSignal !== undefined ? { signal: callSignal } : {}),
467
515
  ...(d.action === "ask" && d.denialLimitFallback !== undefined ? { denialLimitFallback: unarmedWindow(d.denialLimitFallback) } : {}),
468
516
  ...(d.action === "ask" && d.origin !== undefined ? { origin: d.origin } : {}),
469
- ...(d.action === "ask" && d.ruleStoreUnreadable !== undefined ? { ruleStoreUnreadable: d.ruleStoreUnreadable } : {}),
470
517
  ...(d.action === "ask" && d.mandated === true ? { mandated: true, ...(d.mandate !== undefined ? { mandate: d.mandate } : {}) } : {}),
518
+ ...(d.action === "ask" && d.authoredBy === "deployment" ? { authoredBy: "deployment" } : {}),
471
519
  });
472
520
  if (suspendAsk && pass.decision.action === "ask") {
473
521
  const parkArgs = [pass.req, pass.currentInput, safety, undefined, realApprovalOf(pass.decision), pass.decision.action === "ask" ? pass.decision.persistedRuleShadowed : undefined, pass.decision.action === "ask" ? pass.decision.decisionReason : undefined, pass.decision.action === "ask" ? pass.decision.probeReason : undefined, pass.decision.action === "ask" ? pass.decision.probeCause : undefined, pass.decision.action === "ask" ? pass.decision.segmentCoverage : undefined, pass.decision.action === "ask" ? pass.decision.matchedAskRule : undefined, pass.decision.action === "ask" ? pass.decision.probeMandated : undefined];
@@ -642,10 +690,10 @@ export async function runGateLanes(pass) {
642
690
  editDenied = recheck;
643
691
  break;
644
692
  }
645
- let editRuleStore;
693
+ let editRuleCallUnreadable = false;
646
694
  if (input.persistedRules !== undefined && toolName !== ASK_USER_QUESTION_TOOL_NAME) {
647
695
  const editLane = await readPersistedRules({ toolName, args: editArgs, toolCallId, ...(pass.req.face !== undefined ? { face: pass.req.face } : {}) });
648
- editRuleStore = ruleStoreUnreadableOf(editLane);
696
+ editRuleCallUnreadable = ruleCallUnreadableOf(editLane);
649
697
  const editTightened = applyPersistedTightening(recheck, editLane);
650
698
  recheck = editTightened.decision;
651
699
  if (editTightened.tightened === "deny")
@@ -666,10 +714,27 @@ export async function runGateLanes(pass) {
666
714
  editArgs = recheck.updatedInput;
667
715
  editRewrittenSinceHuman = true;
668
716
  }
669
- const { ruleStoreUnreadable: _selfDeclaredOnEdit, ...recheckUnstamped } = recheck;
670
- const editAskSnapshot = { ...recheckUnstamped, ruleEvidence: mintRuleEvidence({ dotsAbsent: "not_adjudicated" }) };
717
+ const editAskSnapshot = { ...recheck, ruleEvidence: mintRuleEvidence({ dotsAbsent: "not_adjudicated" }) };
671
718
  markedThisRound = undefined;
672
- const editAskDecision = stampAskOrigin({ ...editAskSnapshot, ...(editRuleStore !== undefined ? { ruleStoreUnreadable: editRuleStore } : {}) }, askOriginFacts(editOrg.org, editRuleStore));
719
+ const editFacts = askOriginFacts(editOrg.org, editRuleCallUnreadable);
720
+ classifyStanding(editAskSnapshot, editFacts);
721
+ const editAskDecision = stampAskOrigin(editAskSnapshot, editFacts);
722
+ if (input.permissionMode !== undefined && pass.askClassification !== undefined) {
723
+ const editVerdict = arbitrateByMode(input.permissionMode(), pass.askClassification, { ...(editAskDecision.origin !== undefined ? { origin: editAskDecision.origin } : {}), ...(editAskDecision.mandate !== undefined ? { mandate: editAskDecision.mandate } : {}) });
724
+ if (editVerdict.kind === "released") {
725
+ ledger.supersede();
726
+ pass.arbitration = editVerdict;
727
+ await notifier.notifyAsync(() => input.onModeReleased?.({ toolName, toolCallId, release: { mode: editVerdict.mode, askClass: editVerdict.askClass, classes: editVerdict.classes, ...(editVerdict.origin !== undefined ? { origin: editVerdict.origin } : {}), ...(editVerdict.mandate !== undefined ? { mandate: editVerdict.mandate } : {}), ...(editVerdict.reason !== undefined ? { reason: editVerdict.reason } : {}) }, ...(pass.shellReadUnestablished === true ? { readUnestablished: true } : {}) }), "toolGate.onModeReleased");
728
+ break;
729
+ }
730
+ if (editVerdict.kind === "denied_by_mode") {
731
+ ledger.supersede();
732
+ pass.arbitration = editVerdict;
733
+ editDenied = { action: "deny", message: `the approved edit for "${toolName}" raised a new approval requirement that the ${editVerdict.mode} permission mode refuses`, decisionReason: "mode" };
734
+ pass.deniedBy = "mode";
735
+ break;
736
+ }
737
+ }
673
738
  const editAskReq = { toolName, args: editArgs, toolCallId, ...(pass.req.face !== undefined ? { face: pass.req.face } : {}) };
674
739
  const rr = await (callSignal !== undefined ? resolveAsk(editAskDecision, editAskReq, callSignal) : resolveAsk(editAskDecision, editAskReq));
675
740
  ledger.fromResolved(rr, editAskDecision, toolName);
@@ -7,7 +7,7 @@
7
7
  * policy composes: a policy says allow / ask / deny; this file says how the pass that consumed that
8
8
  * verdict ended.
9
9
  */
10
- import { type AskOrigin, type AssertAllKeysHandled } from "./ask-origin.js";
10
+ import { type AskClass, type AskOrigin, type AssertAllKeysHandled, type PermissionMode, type PersistedRuleMandate } from "./ask-origin.js";
11
11
  import { type AutoModeClassifierRound } from "./auto-mode.js";
12
12
  /**
13
13
  * How the wait an ask was in ENDED — the closed set. One word per way a wait can end; the `who` on the
@@ -197,7 +197,8 @@ export type NobodyJudgedTableCoversEveryKind = AssertAllKeysHandled<Exclude<Sett
197
197
  * - `org` — an organization policy rule denied.
198
198
  * - `classifier` — the auto-mode classifier denied directly (its ASK-side role is `origin:
199
199
  * "denial_limit_fallback"`).
200
- * - `plan_mode` — plan mode's read-only block on a write tool.
200
+ * - `persisted_rule` — the person's persisted-rule lane refused: a deny row (live), or, on a durable resume, a deny
201
+ * row, a call unreadable against the rows, or a wait for them the task's cancel ended.
201
202
  * - `compliance` — the compliance call-time lock.
202
203
  * - `write_protection` — an approved edit was rewritten by the restriction chain onto a write-protected
203
204
  * path that no approval covers.
@@ -206,15 +207,18 @@ export type NobodyJudgedTableCoversEveryKind = AssertAllKeysHandled<Exclude<Sett
206
207
  * - `read_boundary` — the shell read station refused: a deny-listed read (refused on every seat, with no
207
208
  * ask), a read the read-only shell face may not make, or a rewritten / re-established command whose reads
208
209
  * no longer are what a person approved.
210
+ * - `mode` — the permission MODE refused a surviving ask at the gate's arbitration point: `dontAsk` turns an
211
+ * ask nobody is present to answer into a refusal, and `bypassPermissions` refuses a write whose protection
212
+ * could not be judged. The disposition carries `mode`, `askClass` and `classes` beside the word.
209
213
  */
210
- export declare const DENIED_BY_VALUES: readonly ["policy", "hook", "org", "persisted_rule", "classifier", "plan_mode", "compliance", "write_protection", "ask_resolution", "read_boundary"];
214
+ export declare const DENIED_BY_VALUES: readonly ["policy", "hook", "org", "persisted_rule", "classifier", "compliance", "write_protection", "ask_resolution", "read_boundary", "mode"];
211
215
  export type DeniedBy = (typeof DENIED_BY_VALUES)[number];
212
216
  /** Membership test for {@link DeniedBy}. */
213
217
  export declare function isDeniedBy(v: unknown): v is DeniedBy;
214
218
  /** Which layers may VETO an approval a person already gave (invariant I3: a `human_allowed` settlement
215
219
  * beside a `denied` disposition names one of these). The approval-edit re-screen (hook), the policy, org
216
220
  * and persisted-rule re-checks, and the write-protection re-judge run AFTER the person's yes; the
217
- * classifier, plan mode and compliance run before any ask exists, and `ask_resolution` IS the
221
+ * classifier and compliance run before any ask exists, and `ask_resolution` IS the
218
222
  * settlement, not a veto of it. `persisted_rule` = a person's own standing DENY row (their settings'
219
223
  * deny list, imported) — the personal-store sibling of `org`, and like it re-judged on an edited
220
224
  * command. `read_boundary` vetoes too: the rewrite re-judge and the pre-spawn re-establishment run after
@@ -225,11 +229,11 @@ export declare const DENIED_BY_MAY_VETO: {
225
229
  readonly org: true;
226
230
  readonly persisted_rule: true;
227
231
  readonly classifier: false;
228
- readonly plan_mode: false;
229
232
  readonly compliance: false;
230
233
  readonly write_protection: true;
231
234
  readonly ask_resolution: false;
232
235
  readonly read_boundary: true;
236
+ readonly mode: false;
233
237
  };
234
238
  /** Compile-time fence: `never` while every deny layer has a veto row. */
235
239
  export type VetoTableCoversEveryDeniedBy = AssertAllKeysHandled<Exclude<DeniedBy, keyof typeof DENIED_BY_MAY_VETO>>;
@@ -259,11 +263,11 @@ export declare const DENIED_BY_MAY_CARRY_CAUSE: {
259
263
  readonly org: false;
260
264
  readonly persisted_rule: false;
261
265
  readonly classifier: true;
262
- readonly plan_mode: false;
263
266
  readonly compliance: false;
264
267
  readonly write_protection: false;
265
268
  readonly ask_resolution: false;
266
269
  readonly read_boundary: false;
270
+ readonly mode: false;
267
271
  };
268
272
  /** Compile-time fence: `never` while every deny layer has a cause row. */
269
273
  export type CauseTableCoversEveryDeniedBy = AssertAllKeysHandled<Exclude<DeniedBy, keyof typeof DENIED_BY_MAY_CARRY_CAUSE>>;
@@ -285,12 +289,37 @@ export type CauseTableCoversEveryDeniedBy = AssertAllKeysHandled<Exclude<DeniedB
285
289
  export type GateDisposition = {
286
290
  kind: "allowed";
287
291
  classifier?: AutoModeClassifierRound;
292
+ released?: ModeRelease;
288
293
  } | {
289
294
  kind: "denied";
290
295
  deniedBy: DeniedBy;
291
296
  cause?: ClassifierDenyCause;
292
297
  classifier?: AutoModeClassifierRound;
298
+ mode?: ModeRefusal["mode"];
299
+ askClass?: AskClass;
300
+ classes?: readonly AskClass[];
293
301
  };
302
+ /**
303
+ * A permission MODE released a surviving ask — the allowed disposition's account of the question it did not put
304
+ * to anyone: the mode, the disclosed class word (`askClass`, the first member of the folded set in `ASK_CLASSES`
305
+ * order — one of {@link RELEASABLE_ASK_CLASSES}), the whole set, and the origin / mandate words the card would
306
+ * have shown. `reason: "rule_unreadable"` marks a release over a rule the call could not be read against.
307
+ */
308
+ export interface ModeRelease {
309
+ mode: PermissionMode;
310
+ askClass: AskClass;
311
+ classes: readonly AskClass[];
312
+ origin?: AskOrigin;
313
+ mandate?: PersistedRuleMandate;
314
+ reason?: "rule_unreadable";
315
+ }
316
+ /** The two modes that refuse on their own account (`deniedBy: "mode"`): `dontAsk` every non-authority ask,
317
+ * `bypassPermissions` a write whose protection could not be judged. `plan` refuses nothing here — its questions ask. */
318
+ export interface ModeRefusal {
319
+ mode: "bypassPermissions" | "dontAsk";
320
+ askClass: AskClass;
321
+ classes: readonly AskClass[];
322
+ }
294
323
  /**
295
324
  * The WHOLE record of one tool-gate pass: the final disposition, plus — when this pass settled an ask —
296
325
  * the settlement it consumed and the ask's origin. Minted ONCE (the gate's exit; the decide lane for a
@@ -310,7 +339,11 @@ export type GateDisposition = {
310
339
  * I6 `disposition.classifier` present ⇒ both model ids are non-empty strings, and a `fallback` on it
311
340
  * names a `to` that IS `modelUsed` (the ladder landed where the record says) with a cause in
312
341
  * {@link import("./auto-mode.js").AUTO_MODE_FALLBACK_CAUSES} — a record whose `to` and `modelUsed`
313
- * disagree would let a reader answer "which model decided this call?" two ways.
342
+ * disagree would let a reader answer "which model decided this call?" two ways;
343
+ * I7 `disposition.released` present ⇒ `kind === "allowed"`, no settlement, `mode` is `bypassPermissions`,
344
+ * `askClass` is a releasable class and every member of `classes` is an ask class — a mode releases nothing
345
+ * a person answered and nothing the table calls immune;
346
+ * I8 `deniedBy === "mode"` ⇔ `mode` present (`bypassPermissions` | `dontAsk`) with `askClass` and `classes`.
314
347
  * Nothing here is read out of an ABSENCE: an ordinary allow with no ask is `{ disposition: { kind: "allowed" } }`,
315
348
  * a direct policy deny is `{ disposition: { kind: "denied", deniedBy: "policy" } }`, and neither carries a
316
349
  * settlement because neither settled one.
@@ -1,4 +1,4 @@
1
- import { isAskOrigin } from "./ask-origin.js";
1
+ import { isAskClass, isAskOrigin, isPermissionMode, RELEASABLE_ASK_CLASSES } from "./ask-origin.js";
2
2
  import { isAutoModeFallbackCause } from "./auto-mode.js";
3
3
  export const SETTLEMENT_KINDS = [
4
4
  "human_allowed",
@@ -56,7 +56,7 @@ export const SETTLEMENT_NOBODY_JUDGED = {
56
56
  policy_refused: true,
57
57
  task_aborted: false,
58
58
  };
59
- export const DENIED_BY_VALUES = ["policy", "hook", "org", "persisted_rule", "classifier", "plan_mode", "compliance", "write_protection", "ask_resolution", "read_boundary"];
59
+ export const DENIED_BY_VALUES = ["policy", "hook", "org", "persisted_rule", "classifier", "compliance", "write_protection", "ask_resolution", "read_boundary", "mode"];
60
60
  const DENIED_BY_SET = new Set(DENIED_BY_VALUES);
61
61
  export function isDeniedBy(v) {
62
62
  return DENIED_BY_SET.has(v);
@@ -67,11 +67,11 @@ export const DENIED_BY_MAY_VETO = {
67
67
  org: true,
68
68
  persisted_rule: true,
69
69
  classifier: false,
70
- plan_mode: false,
71
70
  compliance: false,
72
71
  write_protection: true,
73
72
  ask_resolution: false,
74
73
  read_boundary: true,
74
+ mode: false,
75
75
  };
76
76
  export const CLASSIFIER_DENY_CAUSES = ["unavailable", "parse_error"];
77
77
  const CLASSIFIER_DENY_CAUSE_SET = new Set(CLASSIFIER_DENY_CAUSES);
@@ -84,11 +84,11 @@ export const DENIED_BY_MAY_CARRY_CAUSE = {
84
84
  org: false,
85
85
  persisted_rule: false,
86
86
  classifier: true,
87
- plan_mode: false,
88
87
  compliance: false,
89
88
  write_protection: false,
90
89
  ask_resolution: false,
91
90
  read_boundary: false,
91
+ mode: false,
92
92
  };
93
93
  export function screenGateOutcome(g) {
94
94
  const defects = [];
@@ -116,6 +116,28 @@ export function screenGateOutcome(g) {
116
116
  else if (isDeniedBy(d.deniedBy) && !DENIED_BY_MAY_CARRY_CAUSE[d.deniedBy])
117
117
  defects.push(`I5: a classifier cause cannot stand beside deniedBy "${d.deniedBy}"`);
118
118
  }
119
+ if (d.kind === "allowed" && d.released !== undefined) {
120
+ const r = d.released;
121
+ if (s !== undefined)
122
+ defects.push("I7: a mode release carries no settlement — nobody answered the ask it released");
123
+ if (r.mode !== "bypassPermissions")
124
+ defects.push(`I7: a mode release names mode "${String(r.mode)}" — only bypassPermissions releases`);
125
+ if (!isAskClass(r.askClass) || !RELEASABLE_ASK_CLASSES.includes(r.askClass))
126
+ defects.push(`I7: released.askClass "${String(r.askClass)}" is not a releasable class`);
127
+ if (!Array.isArray(r.classes) || r.classes.length === 0 || !r.classes.every((c) => isAskClass(c)))
128
+ defects.push("I7: released.classes must be a non-empty list of ask classes");
129
+ if (r.origin !== undefined && !isAskOrigin(r.origin))
130
+ defects.push(`I7: released.origin "${String(r.origin)}" is outside the closed set`);
131
+ }
132
+ if (d.kind === "denied") {
133
+ const modeRefusal = d.deniedBy === "mode";
134
+ if (modeRefusal !== (d.mode !== undefined))
135
+ defects.push("I8: deniedBy mode and disposition.mode must be present together");
136
+ if (modeRefusal && (!isPermissionMode(d.mode) || (d.mode !== "bypassPermissions" && d.mode !== "dontAsk")))
137
+ defects.push(`I8: a mode refusal names mode "${String(d.mode)}" — only bypassPermissions, dontAsk and plan refuse`);
138
+ if (modeRefusal && (!isAskClass(d.askClass) || !Array.isArray(d.classes) || d.classes.length === 0 || !d.classes.every((c) => isAskClass(c))))
139
+ defects.push("I8: a mode refusal carries askClass and a non-empty classes list");
140
+ }
119
141
  const attribution = d.classifier;
120
142
  if (attribution !== undefined) {
121
143
  const named = (v) => typeof v === "string" && v !== "";
@@ -119,7 +119,7 @@ export type NoticeAudience = "user" | "operator";
119
119
  * the union's catalog ({@link EngineNotice.code}). The groups below say only which family a row joins,
120
120
  * so the catalog is not written a second time here and the two cannot drift.
121
121
  */
122
- export declare const ENGINE_NOTICE_CODES: readonly ["config.autocompact_window_clamped", "config.env_timeout_discarded", "config.materialize_env_discarded", "config.models_swapped", "config.context_settings_swapped", "config.read_face_swapped", "config.read_face_deployment_clamped", "config.write_face_swapped", "config.write_face_deployment_clamped", "config.tool_model_gate_removed", "config.tool_model_gate_unknown_class", "config.tool_model_gate_env_invalid", "config.artifact_host_invalid", "config.task_root_not_canonical", "config.write_protection_target_view_absent", "config.write_protection_unresolved", "config.execution_env_capability_invalid", "config.tool_card_undeclared", "config.tool_face_undeclared", "config.tool_face_invalid", "config.forward_sink_failed", "config.durable_gate_unavailable", "config.peer_admission_out_of_range", "config.peer_lane_unmounted", "config.secret_env_scrubbed", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "classifier.fallback", "classifier.role_defaulted", "classifier.contract_broken", "checkpoint.execution_outcome_unrecorded", "delegation.transcript_integrity", "delegation.ask_unresolvable", "mcp.injection_dropped", "mcp.revocation_probe_failed", "mcp.server_redialed", "workflow.governance_key_stripped", "workflow.agent_option_ignored", "memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.content_class_declared", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "memory.consolidation_recommended", "memory.consolidation_committed", "memory.consolidation_conflict", "memory.consolidation_incomplete", "memory.consolidation_refused", "memory.consolidation_withheld", "memory.restore_withheld", "memory.project_scope_absent", "route.fallback_to_primary", "route.classifier_unroutable", "route.base_url_changed_key_unchanged", "route.response_model_mismatch", "task.user_steer_undrained", "task.user_followup_undrained", "steering.parked_input_blocked", "task.turn_interrupted", "task.halt_unconsumed", "task.late_approval", "task.interrupt_unconsumed", "approval.read_root_granted", "approval.read_root_grant_rejected", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "skills.listing_truncated", "agents.listing_truncated", "tool_result.offload_put_failed"];
122
+ export declare const ENGINE_NOTICE_CODES: readonly ["config.autocompact_window_clamped", "config.env_timeout_discarded", "config.materialize_env_discarded", "config.models_swapped", "config.context_settings_swapped", "config.read_face_swapped", "config.read_face_deployment_clamped", "config.write_face_swapped", "config.write_face_deployment_clamped", "config.tool_model_gate_removed", "config.tool_model_gate_unknown_class", "config.tool_model_gate_env_invalid", "config.artifact_host_invalid", "config.task_root_not_canonical", "config.write_protection_target_view_absent", "config.write_protection_unresolved", "config.execution_env_capability_invalid", "config.tool_card_undeclared", "config.tool_face_undeclared", "config.tool_face_invalid", "config.forward_sink_failed", "config.durable_gate_unavailable", "config.peer_admission_out_of_range", "config.peer_lane_unmounted", "config.secret_env_scrubbed", "config.permission_mode_unavailable", "permission.ask_released_by_mode", "permission.read_deny_unapplied", "permission.rule_store_unavailable", "resume.legacy_mode_seed_migrated", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "classifier.fallback", "classifier.role_defaulted", "classifier.contract_broken", "classifier.unavailable", "checkpoint.execution_outcome_unrecorded", "delegation.transcript_integrity", "delegation.ask_unresolvable", "mcp.injection_dropped", "mcp.revocation_probe_failed", "mcp.server_redialed", "workflow.governance_key_stripped", "workflow.agent_option_ignored", "memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.content_class_declared", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "memory.consolidation_recommended", "memory.consolidation_committed", "memory.consolidation_conflict", "memory.consolidation_incomplete", "memory.consolidation_refused", "memory.consolidation_withheld", "memory.restore_withheld", "memory.project_scope_absent", "route.fallback_to_primary", "route.classifier_unroutable", "route.base_url_changed_key_unchanged", "route.response_model_mismatch", "task.user_steer_undrained", "task.user_followup_undrained", "steering.parked_input_blocked", "task.turn_interrupted", "task.halt_unconsumed", "task.late_approval", "task.interrupt_unconsumed", "approval.read_root_granted", "approval.read_root_grant_rejected", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "skills.listing_truncated", "agents.listing_truncated", "tool_result.offload_put_failed"];
123
123
  /** A code this engine mints (see {@link ENGINE_NOTICE_CODES}). NOT the type of
124
124
  * `EngineNotice.code`, which stays `string` — a host forwarding its own notices through the same
125
125
  * sink is a supported shape, and narrowing that field would break it. */
@@ -128,6 +128,11 @@ export const ENGINE_NOTICE_CODES = [
128
128
  "config.peer_admission_out_of_range",
129
129
  "config.peer_lane_unmounted",
130
130
  "config.secret_env_scrubbed",
131
+ "config.permission_mode_unavailable",
132
+ "permission.ask_released_by_mode",
133
+ "permission.read_deny_unapplied",
134
+ "permission.rule_store_unavailable",
135
+ "resume.legacy_mode_seed_migrated",
131
136
  "peer.inbound_disposition",
132
137
  "peer.held_settled",
133
138
  "peer.idle_subscription",
@@ -135,6 +140,7 @@ export const ENGINE_NOTICE_CODES = [
135
140
  "classifier.fallback",
136
141
  "classifier.role_defaulted",
137
142
  "classifier.contract_broken",
143
+ "classifier.unavailable",
138
144
  "checkpoint.execution_outcome_unrecorded",
139
145
  "delegation.transcript_integrity",
140
146
  "delegation.ask_unresolvable",
@@ -187,6 +193,7 @@ const NOTICE_AUDIENCE_TABLE = {
187
193
  "task.user_steer_undrained": "user",
188
194
  "task.user_followup_undrained": "user",
189
195
  "classifier.denial_limit": "user",
196
+ "classifier.unavailable": "user",
190
197
  "classifier.fallback": "operator",
191
198
  "classifier.role_defaulted": "operator",
192
199
  "classifier.contract_broken": "operator",
@@ -198,11 +205,16 @@ const NOTICE_AUDIENCE_TABLE = {
198
205
  "approval.read_root_granted": "user",
199
206
  "approval.read_root_grant_rejected": "user",
200
207
  "config.durable_gate_unavailable": "user",
208
+ "config.permission_mode_unavailable": "user",
209
+ "permission.ask_released_by_mode": "user",
210
+ "permission.read_deny_unapplied": "user",
211
+ "permission.rule_store_unavailable": "user",
201
212
  "memory.capture_opted_out": "user",
202
213
  "memory.capture_optout_unpersisted": "user",
203
214
  "memory.consolidation_withheld": "operator",
204
215
  "delegation.ask_unresolvable": "user",
205
216
  "config.autocompact_window_clamped": "operator",
217
+ "resume.legacy_mode_seed_migrated": "operator",
206
218
  "config.env_timeout_discarded": "operator",
207
219
  "config.materialize_env_discarded": "operator",
208
220
  "config.models_swapped": "operator",