@sema-agent/core 7.50.2 → 7.51.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/CHANGELOG.md +313 -0
  2. package/dist/agents/child-failure.d.ts +5 -16
  3. package/dist/agents/child-failure.js +4 -24
  4. package/dist/agents/subagent-steps.js +2 -2
  5. package/dist/agents/subagent.d.ts +4 -13
  6. package/dist/agents/subagent.js +94 -204
  7. package/dist/agents/tool-filter.d.ts +2 -2
  8. package/dist/brain/anthropic.d.ts +1 -1
  9. package/dist/brain/anthropic.js +7 -5
  10. package/dist/brain/open-responses.d.ts +1 -1
  11. package/dist/brain/open-responses.js +3 -2
  12. package/dist/brain/openai.d.ts +1 -1
  13. package/dist/brain/openai.js +3 -2
  14. package/dist/brain/request-route.d.ts +2 -2
  15. package/dist/brain/route-adjudicator.d.ts +16 -7
  16. package/dist/brain/route-adjudicator.js +23 -7
  17. package/dist/brain/route-conformance.js +23 -5
  18. package/dist/brain/stream-engine.js +9 -3
  19. package/dist/core/a2a.js +6 -8
  20. package/dist/core/allow-entry.d.ts +45 -26
  21. package/dist/core/allow-entry.js +48 -24
  22. package/dist/core/arg-summary.d.ts +8 -85
  23. package/dist/core/arg-summary.js +4 -386
  24. package/dist/core/ask-origin.d.ts +35 -33
  25. package/dist/core/ask-origin.js +9 -11
  26. package/dist/core/checkpoint-requirements.d.ts +6 -0
  27. package/dist/core/checkpoint-requirements.js +1 -0
  28. package/dist/core/checkpoint-store.d.ts +15 -34
  29. package/dist/core/checkpoint-store.js +5 -4
  30. package/dist/core/classifier-unavailable-arm.d.ts +12 -8
  31. package/dist/core/classifier-unavailable-arm.js +7 -2
  32. package/dist/core/credential-vocabulary.d.ts +3 -2
  33. package/dist/core/credential-vocabulary.js +27 -18
  34. package/dist/core/delegation-frames.d.ts +40 -15
  35. package/dist/core/delegation-frames.js +34 -0
  36. package/dist/core/engine-capabilities.d.ts +15 -0
  37. package/dist/core/engine-capabilities.js +1 -0
  38. package/dist/core/engine-notice.d.ts +41 -14
  39. package/dist/core/gate-fold.js +9 -7
  40. package/dist/core/gate-lanes.js +6 -4
  41. package/dist/core/gate-outcome.d.ts +81 -15
  42. package/dist/core/gate-outcome.js +168 -67
  43. package/dist/core/governance-codes.d.ts +7 -1
  44. package/dist/core/governance-codes.js +9 -0
  45. package/dist/core/hooks.d.ts +4 -4
  46. package/dist/core/hooks.js +4 -3
  47. package/dist/core/mcp-connection-pool.d.ts +2 -0
  48. package/dist/core/mcp-connection-pool.js +2 -1
  49. package/dist/core/mcp-failure.js +2 -1
  50. package/dist/core/mcp-server-spec.d.ts +27 -5
  51. package/dist/core/mcp.d.ts +13 -38
  52. package/dist/core/mcp.js +28 -23
  53. package/dist/core/media-byte-cap.d.ts +25 -0
  54. package/dist/core/media-byte-cap.js +18 -2
  55. package/dist/core/memory-engine/consolidation-driver.js +1 -1
  56. package/dist/core/mode-policy.d.ts +25 -1
  57. package/dist/core/mode-policy.js +28 -13
  58. package/dist/core/own-property-receipt.d.ts +18 -0
  59. package/dist/core/own-property-receipt.js +39 -0
  60. package/dist/core/permission-rule-model.d.ts +32 -11
  61. package/dist/core/permission-rule-model.js +12 -7
  62. package/dist/core/persisted-rule-arms.d.ts +3 -3
  63. package/dist/core/prompt-section-seat.d.ts +54 -0
  64. package/dist/core/prompt-section-seat.js +49 -0
  65. package/dist/core/protocol-http-auth.d.ts +44 -0
  66. package/dist/core/protocol-http-auth.js +45 -0
  67. package/dist/core/read-face-seats.d.ts +30 -0
  68. package/dist/core/read-face-seats.js +38 -0
  69. package/dist/core/redaction-passes.d.ts +82 -0
  70. package/dist/core/redaction-passes.js +378 -0
  71. package/dist/core/remote-env.d.ts +4 -3
  72. package/dist/core/resume-startup-cause.d.ts +56 -0
  73. package/dist/core/resume-startup-cause.js +43 -0
  74. package/dist/core/rule-denial-sentence.d.ts +2 -2
  75. package/dist/core/runner/announce-once-ledger.js +2 -2
  76. package/dist/core/runner/classifier-ladder.d.ts +17 -13
  77. package/dist/core/runner/classifier-ladder.js +12 -10
  78. package/dist/core/runner/construction-doors.js +9 -20
  79. package/dist/core/runner/contracts.d.ts +4 -4
  80. package/dist/core/runner/gate-exit.js +2 -2
  81. package/dist/core/runner/memory-consolidation-write-judge.d.ts +53 -9
  82. package/dist/core/runner/memory-consolidation-write-judge.js +39 -1
  83. package/dist/core/runner/permission-rule-lanes.d.ts +8 -13
  84. package/dist/core/runner/person-path-rows.d.ts +4 -3
  85. package/dist/core/runner/policy-throw.js +4 -4
  86. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -3
  87. package/dist/core/runner/prepare-caps-and-workflow.js +8 -6
  88. package/dist/core/runner/prepare-gate-stations.js +4 -4
  89. package/dist/core/runner/prepare-hands-readface.js +1 -19
  90. package/dist/core/runner/prepare-inherited-gate.js +10 -9
  91. package/dist/core/runner/prepare-policy-chain.d.ts +3 -5
  92. package/dist/core/runner/prepare-policy-chain.js +10 -13
  93. package/dist/core/runner/prepare-project-context.d.ts +4 -4
  94. package/dist/core/runner/prepare-project-context.js +2 -1
  95. package/dist/core/runner/prepare-prompt-assembly.js +1 -0
  96. package/dist/core/runner/prepare-prompt-inputs.d.ts +1 -1
  97. package/dist/core/runner/prepare-prompt-inputs.js +4 -4
  98. package/dist/core/runner/prepare-protocol-tools.d.ts +4 -3
  99. package/dist/core/runner/prepare-protocol-tools.js +3 -2
  100. package/dist/core/runner/prepare-task.js +6 -4
  101. package/dist/core/runner/prepare-turn-wiring.d.ts +16 -13
  102. package/dist/core/runner/prepare-turn-wiring.js +8 -7
  103. package/dist/core/runner/run-harness-handlers.js +2 -2
  104. package/dist/core/runner/run-leg.js +10 -9
  105. package/dist/core/runner/run-settle-and-teardown.js +5 -5
  106. package/dist/core/runner/run-turn-boundary.js +3 -2
  107. package/dist/core/runner/runtask.d.ts +1 -2
  108. package/dist/core/runner/runtask.js +8 -12
  109. package/dist/core/runner/session-rule-policy.d.ts +15 -11
  110. package/dist/core/runner/session-rule-policy.js +43 -33
  111. package/dist/core/runner/stream-lifecycle-verbs.js +2 -2
  112. package/dist/core/runner/stream-settle-backstop.js +2 -1
  113. package/dist/core/runner/sub-agent-deny-lists.d.ts +12 -8
  114. package/dist/core/runner/sub-agent-deny-lists.js +23 -12
  115. package/dist/core/runner/terminal-account.js +3 -3
  116. package/dist/core/runner/tool-output-projection.d.ts +5 -0
  117. package/dist/core/runner/tool-output-projection.js +6 -11
  118. package/dist/core/runner/tool-result-media-seat.d.ts +19 -0
  119. package/dist/core/runner/tool-result-media-seat.js +13 -0
  120. package/dist/core/runner/turn-attachments.d.ts +13 -12
  121. package/dist/core/runner/turn-attachments.js +11 -7
  122. package/dist/core/runner-deps.d.ts +4 -3
  123. package/dist/core/safety-merge-corpus.js +21 -21
  124. package/dist/core/secret-env.d.ts +41 -15
  125. package/dist/core/secret-env.js +85 -19
  126. package/dist/core/session-policy-store.d.ts +89 -72
  127. package/dist/core/session-policy-store.js +100 -93
  128. package/dist/core/shell-read-silence.d.ts +38 -4
  129. package/dist/core/shell-read-silence.js +102 -0
  130. package/dist/core/side-query.d.ts +7 -8
  131. package/dist/core/skill-grant-binding.d.ts +32 -11
  132. package/dist/core/skill-grant-binding.js +11 -3
  133. package/dist/core/skill-grant.d.ts +4 -3
  134. package/dist/core/skill-grant.js +5 -4
  135. package/dist/core/skills-directory.d.ts +12 -1
  136. package/dist/core/skills-directory.js +17 -8
  137. package/dist/core/spec-contract.d.ts +5 -3
  138. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +2 -0
  139. package/dist/core/store-contracts/checkpoint-store-contract.js +3 -0
  140. package/dist/core/swappable-deps.d.ts +15 -1
  141. package/dist/core/swappable-deps.js +25 -1
  142. package/dist/core/synthetic-read-verdict.d.ts +16 -14
  143. package/dist/core/synthetic-read-verdict.js +28 -8
  144. package/dist/core/task-notification.d.ts +13 -3
  145. package/dist/core/task-notification.js +12 -1
  146. package/dist/core/task-spec.d.ts +14 -15
  147. package/dist/core/tool-name-readings.d.ts +0 -4
  148. package/dist/core/tool-name-readings.js +0 -1
  149. package/dist/core/tool-policy.d.ts +40 -9
  150. package/dist/core/tool-policy.js +25 -7
  151. package/dist/core/types.d.ts +3 -3
  152. package/dist/core/types.js +1 -1
  153. package/dist/core/untrusted-egress.d.ts +60 -11
  154. package/dist/core/untrusted-egress.js +120 -9
  155. package/dist/core/untrusted-text.js +16 -0
  156. package/dist/core/wiring-manifest.d.ts +22 -8
  157. package/dist/core/wiring-manifest.js +12 -4
  158. package/dist/engine/execution-env/kill-tree.js +4 -4
  159. package/dist/engine/execution-env/node-execution-env.d.ts +11 -4
  160. package/dist/engine/execution-env/node-execution-env.js +9 -4
  161. package/dist/engine/harness/agent-harness.d.ts +2 -1
  162. package/dist/engine/harness/agent-harness.js +4 -1
  163. package/dist/engine/harness/types.d.ts +3 -0
  164. package/dist/engine/llm/types.d.ts +7 -9
  165. package/dist/engine/loop/agent-loop.d.ts +8 -3
  166. package/dist/engine/loop/agent-loop.js +26 -9
  167. package/dist/engine/loop/types.d.ts +10 -0
  168. package/dist/engine/lsp/node-lsp-manager.d.ts +2 -1
  169. package/dist/engine/lsp/node-lsp-manager.js +2 -2
  170. package/dist/index.d.ts +22 -5
  171. package/dist/index.js +18 -5
  172. package/dist/orchestration/run-workflow-tool.js +8 -8
  173. package/dist/orchestration/workflow.js +2 -2
  174. package/dist/prompt-assembly/assemble.d.ts +3 -1
  175. package/dist/prompt-assembly/assemble.js +6 -0
  176. package/dist/prompts/cowork.d.ts +42 -0
  177. package/dist/prompts/cowork.js +529 -0
  178. package/dist/prompts/default.d.ts +14 -1
  179. package/dist/prompts/default.js +3 -0
  180. package/dist/stores/file/fs-atomic.js +2 -2
  181. package/dist/stores/file/session-policy-store.d.ts +6 -3
  182. package/dist/stores/file/session-policy-store.js +5 -16
  183. package/dist/tools/artifact/artifact-tool.js +2 -0
  184. package/dist/tools/fs/image-admission.d.ts +57 -4
  185. package/dist/tools/fs/image-admission.js +85 -0
  186. package/dist/tools/fs/path-containment.d.ts +2 -0
  187. package/dist/tools/fs/path-containment.js +5 -0
  188. package/dist/tools/fs/shell-read-check.d.ts +31 -6
  189. package/dist/tools/fs/shell-read-check.js +2 -4
  190. package/dist/tools/fs/shell-read-environment.d.ts +10 -25
  191. package/dist/tools/fs/shell-read-environment.js +6 -18
  192. package/dist/tools/fs/shell-read-establish.d.ts +3 -2
  193. package/dist/tools/fs/shell-read-establish.js +5 -5
  194. package/dist/tools/fs/shell-read-judge.d.ts +38 -8
  195. package/dist/tools/fs/shell-read-judge.js +12 -10
  196. package/dist/tools/web.js +2 -2
  197. package/package.json +1 -1
  198. package/test/export-surface.snapshot.json +112 -10
  199. package/wire-changes/core@7.51.0.json +2421 -0
  200. package/wire-changes/core@7.51.1.json +259 -0
@@ -7,7 +7,7 @@ import { admitToolCallId, providerResultIds, providerToolCallId, responseToolCal
7
7
  import { emitBrainTelemetry, reportReasoningWireFacts } from "./status-sink.js";
8
8
  import { errorResultMediaNote, IMAGE_OMITTED_NO_VISION, modelSupportsVision, sendableImages } from "./media-degrade.js";
9
9
  import { ANTHROPIC_RESERVED, OUTPUT_CAP_KEYS, applyExtraBody, effectiveOutputCap, lockHeader, mergeHeaders, takeHeaderCasefold } from "./request-params.js";
10
- import { adjudicateModelRoute, applyRouteCredentialHeaders, assertServableRequestRoot, createBrainRouteJudge, joinRequestUrl, resolveRequestRootOf, resolveRouteCredential, routeRefusalText } from "./route-adjudicator.js";
10
+ import { adjudicateModelRoute, applyRouteCredentialHeaders, assertServableRequestRoot, brainConfigSnapshot, createBrainRouteJudge, joinRequestUrl, resolveRequestRootOf, resolveRouteCredential, routeRefusalText } from "./route-adjudicator.js";
11
11
  import { requestRouteOf } from "./request-route.js";
12
12
  import { MIN_THINKING_TOKENS, budgetCapSkipsThinking, declaredEffortLevels, reasoningBudgetShare, reasoningRequestCarried, resolveEffort } from "./reasoning.js";
13
13
  import { runStreamingBrain } from "./stream-engine.js";
@@ -226,7 +226,9 @@ function closeToolUseBlock(acc, responseIds, closed) {
226
226
  acc.closedTc = { type: "toolCall", id: engineId, ...(wireId !== engineId ? { wireId } : {}), name: acc.toolName, arguments: args };
227
227
  return acc.closedTc;
228
228
  }
229
- export function createAnthropicBrain(config = {}) {
229
+ const ANTHROPIC_LANE_DEFAULT = "https://api.anthropic.com";
230
+ export function createAnthropicBrain(given = {}) {
231
+ const config = brainConfigSnapshot(given, "createAnthropicBrain config");
230
232
  assertServableRequestRoot(config.baseUrl, "config.baseUrl");
231
233
  refuseRetiredTimeoutKnobs(config, "construction", "createAnthropicBrain config");
232
234
  const doFetch = resolveFetch(config);
@@ -244,7 +246,7 @@ export function createAnthropicBrain(config = {}) {
244
246
  maxRetries: options?.maxRetries,
245
247
  stallTimeouts: options?.stallTimeouts,
246
248
  buildRequest: (overrides) => {
247
- const { root, origin: rootOrigin } = resolveRequestRootOf(model.baseUrl, config.baseUrl, "https://api.anthropic.com");
249
+ const { root, origin: rootOrigin } = resolveRequestRootOf(model.baseUrl, config.baseUrl, ANTHROPIC_LANE_DEFAULT);
248
250
  const routeCredential = resolveRouteCredential({
249
251
  optionsApiKey: options?.apiKey,
250
252
  optionsHeaders: options?.headers,
@@ -252,7 +254,7 @@ export function createAnthropicBrain(config = {}) {
252
254
  configApiKey: config.apiKey,
253
255
  configHeaders: config.headers,
254
256
  });
255
- const routeVerdict = adjudicateModelRoute(model, routeCredential, { baseUrl: config.baseUrl });
257
+ const routeVerdict = adjudicateModelRoute(model, routeCredential, { baseUrl: config.baseUrl, laneDefault: ANTHROPIC_LANE_DEFAULT });
256
258
  if (!routeVerdict.ok)
257
259
  throw new BrainError("invalid_request", routeRefusalText(routeVerdict));
258
260
  const apiKey = routeCredential.apiKey;
@@ -766,5 +768,5 @@ export function createAnthropicBrain(config = {}) {
766
768
  },
767
769
  });
768
770
  };
769
- return { stream, adjudicateRoute: createBrainRouteJudge(config) };
771
+ return { stream, adjudicateRoute: createBrainRouteJudge({ ...config, laneDefault: ANTHROPIC_LANE_DEFAULT }) };
770
772
  }
@@ -28,4 +28,4 @@ export interface OpenResponsesBrainConfig extends StreamEngineConfig {
28
28
  * Create a Brain that talks to an Open Responses endpoint. See the file header for the stateless
29
29
  * invariant, the capability-audit posture, and the reasoning-replay rule.
30
30
  */
31
- export declare function createOpenResponsesBrain(config?: OpenResponsesBrainConfig): Brain;
31
+ export declare function createOpenResponsesBrain(given?: OpenResponsesBrainConfig): Brain;
@@ -6,7 +6,7 @@ import { admitToolCallId, providerResultIds, providerToolCallId, responseToolCal
6
6
  import { emitBrainTelemetry } from "./status-sink.js";
7
7
  import { errorResultMediaNote, IMAGE_OMITTED_NO_VISION, imagesOmittedNoVisionNote, modelSupportsVision, sendableImages } from "./media-degrade.js";
8
8
  import { OUTPUT_CAP_KEYS, RESPONSES_RESERVED, applyExtraBody, effectiveOutputCap, lockHeader, mergeHeaders } from "./request-params.js";
9
- import { adjudicateModelRoute, applyRouteCredentialHeaders, assertServableRequestRoot, createBrainRouteJudge, joinRequestUrl, resolveRequestRootOf, resolveRouteCredential, routeRefusalText } from "./route-adjudicator.js";
9
+ import { adjudicateModelRoute, applyRouteCredentialHeaders, assertServableRequestRoot, brainConfigSnapshot, createBrainRouteJudge, joinRequestUrl, resolveRequestRootOf, resolveRouteCredential, routeRefusalText } from "./route-adjudicator.js";
10
10
  import { requestRouteOf } from "./request-route.js";
11
11
  import { declaredOffSpelling, mintEffortWireValue, reasoningRequestCarried } from "./reasoning.js";
12
12
  import { runStreamingBrain } from "./stream-engine.js";
@@ -219,7 +219,8 @@ function mapTerminalStopReason(incompleteReason, hadToolCalls) {
219
219
  return "length";
220
220
  return "stop";
221
221
  }
222
- export function createOpenResponsesBrain(config = {}) {
222
+ export function createOpenResponsesBrain(given = {}) {
223
+ const config = brainConfigSnapshot(given, "createOpenResponsesBrain config");
223
224
  assertServableRequestRoot(config.baseUrl, "config.baseUrl");
224
225
  refuseRetiredTimeoutKnobs(config, "construction", "createOpenResponsesBrain config");
225
226
  const doFetch = resolveFetch(config);
@@ -57,4 +57,4 @@ export declare function inferMaxTokensField(modelId: string): "max_completion_to
57
57
  * machinery lives in the shared {@link runStreamingBrain} engine (design/32); this adapter owns the
58
58
  * OpenAI request shaping + chat-completions SSE parsing + finalization.
59
59
  */
60
- export declare function createOpenAIBrain(config?: OpenAIBrainConfig): Brain;
60
+ export declare function createOpenAIBrain(given?: OpenAIBrainConfig): Brain;
@@ -8,7 +8,7 @@ import { errorResultMediaNote, IMAGE_OMITTED_NO_VISION, imagesOmittedNoVisionNot
8
8
  import { OPENAI_RESERVED, OUTPUT_CAP_KEYS, applyExtraBody, effectiveOutputCap, lockHeader, mergeHeaders } from "./request-params.js";
9
9
  import { BrainError, providerErrorWord } from "./errors.js";
10
10
  import { inBandProviderError } from "./provider-dialect.js";
11
- import { adjudicateModelRoute, applyRouteCredentialHeaders, assertServableRequestRoot, createBrainRouteJudge, joinRequestUrl, resolveRequestRootOf, resolveRouteCredential, routeRefusalText } from "./route-adjudicator.js";
11
+ import { adjudicateModelRoute, applyRouteCredentialHeaders, assertServableRequestRoot, brainConfigSnapshot, createBrainRouteJudge, joinRequestUrl, resolveRequestRootOf, resolveRouteCredential, routeRefusalText } from "./route-adjudicator.js";
12
12
  import { requestRouteOf } from "./request-route.js";
13
13
  import { declaredOffSpelling, mintEffortWireValue, reasoningRequestCarried } from "./reasoning.js";
14
14
  import { runStreamingBrain } from "./stream-engine.js";
@@ -323,7 +323,8 @@ export function inferMaxTokensField(modelId) {
323
323
  return "max_completion_tokens";
324
324
  return "max_tokens";
325
325
  }
326
- export function createOpenAIBrain(config = {}) {
326
+ export function createOpenAIBrain(given = {}) {
327
+ const config = brainConfigSnapshot(given, "createOpenAIBrain config");
327
328
  assertServableRequestRoot(config.baseUrl, "config.baseUrl");
328
329
  refuseRetiredTimeoutKnobs(config, "construction", "createOpenAIBrain config");
329
330
  const doFetch = resolveFetch(config);
@@ -16,7 +16,7 @@ import type { RouteCredential } from "../internal/llm.js";
16
16
  * (`getApiKeyAndHeaders`), or an auth-bearing header declared on `Model.headers`. Paired by
17
17
  * construction (the resolver was asked about THIS entry), so the adjudicator never refuses it.
18
18
  * - `"deployment-config"` — the brain construction-time fallback (`config.apiKey` / an auth header
19
- * in `config.headers`). Paired only with the deployment's own declared root (`config.baseUrl`).
19
+ * in `config.headers`). Paired only with the deployment's own root (`config.baseUrl`, else the lane's default).
20
20
  * - `"absent"` — no credential anywhere. Legal for keyless gateways; refused only where an entry
21
21
  * declares its OWN URL away from a declared deployment root (a distinct endpoint with no
22
22
  * credential route is a configuration hole, not a keyless deployment). */
@@ -64,7 +64,7 @@ export interface RouteCredentialPairingRow {
64
64
  readonly credentialKnobs: readonly string[];
65
65
  /** The root knob the credential is paired with: `"Model.baseUrl"` for a per-model credential (paired by
66
66
  * construction — it was resolved for this entry), `"config.baseUrl"` for the deployment credential (paired only
67
- * when that root is declared and the request targets it; undeclared = the unpinned posture). Absent for
67
+ * when the request targets that root — or, undeclared, the lane's own default endpoint). Absent for
68
68
  * `"absent"`. */
69
69
  readonly pairsWith?: "Model.baseUrl" | "config.baseUrl";
70
70
  }
@@ -15,13 +15,12 @@
15
15
  * - `per-model` → OK always. A per-model credential was resolved FOR this entry (auth hook or an
16
16
  * auth header on `Model.headers`); it is paired by construction, so the per-model lane pays no
17
17
  * new cost.
18
- * - `deployment-config` → OK only where the pairing is verifiable-and-true: the deployment DECLARED
19
- * its credential's URL half (`config.baseUrl`) and the request targets exactly that root.
20
- * A declared root with a diverging entry URL is the torn pair this module exists to stop
21
- * (`route.credential_mismatch`). An UNDECLARED root (`config.apiKey` with no `config.baseUrl`)
22
- * stays allowed as the `"unpinned"` posture — that is the documented single-endpoint quick-start
23
- * shape (key on config, URL on the model) and refusing it would break every deployment built from
24
- * it; the posture is surfaced so read faces can annotate the unverifiable pairing instead.
18
+ * - `deployment-config` → a deployment credential has exactly ONE root: the one its brain sends to when
19
+ * an entry names none — `config.baseUrl`, else the lane's own default endpoint
20
+ * (`RoutePairingConfig.laneDefault`). A request to that root is `"paired"`; a request to any other
21
+ * root — an entry carrying its own `baseUrl` — is the torn pair this module exists to stop
22
+ * (`route.credential_mismatch`), whether or not the deployment wrote its root down. A key on the
23
+ * config with the URL only on the model is that torn pair: nothing says the key belongs there.
25
24
  * - `absent` → OK for keyless deployments (nothing to protect); refused as
26
25
  * `route.credential_missing` only where an entry declares its OWN URL away from a DECLARED
27
26
  * deployment root — a distinct endpoint with no credential route is a configuration hole, and a
@@ -180,10 +179,20 @@ export declare function applyRouteCredentialHeaders(merged: Record<string, strin
180
179
  * EXACTLY the resolution + judgment the brain's own request build runs (single source, two call
181
180
  * sites), so a pre-flight verdict and the request-time verdict can never disagree.
182
181
  */
182
+ /** A brain's construction config, received ONCE by the engine's receipt law for caller objects
183
+ * ({@link receiveOwnProperties}: a plain object, every own member read once): the request gate and the pre-flight
184
+ * judge both read this frozen copy, so a config object mutated after construction moves neither and the two cannot
185
+ * disagree about the root a credential is paired with. The header bag is a caller object too: it is received by the
186
+ * same law from that one reading into a frozen bag of the copy's own. */
187
+ export declare function brainConfigSnapshot<T extends {
188
+ headers?: Record<string, string>;
189
+ }>(given: T, subject: string): Readonly<T>;
183
190
  export declare function createBrainRouteJudge(config: {
184
191
  apiKey?: string;
185
192
  baseUrl?: string;
186
193
  headers?: Record<string, string>;
194
+ /** The lane's own default endpoint (the brain's constant), when it has one. */
195
+ laneDefault?: string;
187
196
  }): (model: Model, perModelAuth?: {
188
197
  apiKey?: string;
189
198
  headers?: Record<string, string>;
@@ -1,5 +1,6 @@
1
1
  import { AUTH_CARRIER_NAMES } from "./request-params.js";
2
2
  import { BrainError } from "./errors.js";
3
+ import { receiptRefusal, receiveOwnProperties } from "../core/own-property-receipt.js";
3
4
  const REDACTED_VALUE = "«redacted»";
4
5
  const REDACTED_CREDENTIALS = "[redacted-credentials]";
5
6
  function servableParse(raw) {
@@ -223,17 +224,21 @@ export function resolveRouteCredential(input) {
223
224
  export function adjudicateModelRoute(model, credential, config) {
224
225
  const entry = routeKeyOf(model.baseUrl);
225
226
  const declared = routeKeyOf(config.baseUrl);
226
- const request = entry || declared;
227
+ const deploymentRoot = declared || routeKeyOf(config.laneDefault);
228
+ const request = entry || deploymentRoot;
227
229
  const entryShown = displayUrlOf(entry);
228
230
  const declaredShown = displayUrlOf(declared);
229
231
  const requestShown = displayUrlOf(request);
230
232
  if (credential.source === "per-model")
231
233
  return { ok: true, posture: "per-model" };
232
234
  if (credential.source === "deployment-config") {
233
- if (declared === "")
234
- return { ok: true, posture: "unpinned" };
235
- if (request === declared)
235
+ if (request !== "" && request === deploymentRoot)
236
236
  return { ok: true, posture: "paired" };
237
+ const pairedWith = declared !== ""
238
+ ? `is paired with ${declaredShown}`
239
+ : deploymentRoot !== ""
240
+ ? `is paired with this brain's default endpoint ${displayUrlOf(deploymentRoot)} (config.baseUrl is unset)`
241
+ : `names no root of its own (config.baseUrl is unset)`;
237
242
  return {
238
243
  ok: false,
239
244
  code: "route.credential_mismatch",
@@ -242,8 +247,8 @@ export function adjudicateModelRoute(model, credential, config) {
242
247
  entryBaseUrl: entryShown,
243
248
  requestBaseUrl: requestShown,
244
249
  configBaseUrl: declaredShown,
245
- fixHint: `model "${model.id}" targets ${requestShown} but the deployment credential is paired with ${declaredShown}` +
246
- `${queryDifferenceNote(request, declared)}: ` +
250
+ fixHint: `model "${model.id}" targets ${requestShown || "no root"} but the deployment credential ${pairedWith}` +
251
+ `${queryDifferenceNote(request, deploymentRoot)}: ` +
247
252
  `give the model its own credential (a per-model key via getApiKeyAndHeaders, or an auth header on Model.headers), ` +
248
253
  `or serve it through a brain whose config.baseUrl is the model's own root`,
249
254
  },
@@ -285,6 +290,17 @@ export function applyRouteCredentialHeaders(merged, credential, bags) {
285
290
  merged[k] = v;
286
291
  }
287
292
  }
293
+ export function brainConfigSnapshot(given, subject) {
294
+ const words = { subject, unit: "member", moment: "at construction" };
295
+ const received = receiveOwnProperties(given, words);
296
+ const headers = received.headers;
297
+ if (headers !== undefined) {
298
+ if (headers === null || typeof headers !== "object")
299
+ throw receiptRefusal(`${subject}.headers must be an object of header names to values`);
300
+ Object.defineProperty(received, "headers", { value: Object.freeze(receiveOwnProperties(headers, { ...words, subject: `${subject}.headers`, unit: "header" })), enumerable: true });
301
+ }
302
+ return Object.freeze(received);
303
+ }
288
304
  export function createBrainRouteJudge(config) {
289
305
  return (model, perModelAuth) => {
290
306
  const credential = resolveRouteCredential({
@@ -294,7 +310,7 @@ export function createBrainRouteJudge(config) {
294
310
  configApiKey: config.apiKey,
295
311
  configHeaders: config.headers,
296
312
  });
297
- return adjudicateModelRoute(model, credential, { baseUrl: config.baseUrl });
313
+ return adjudicateModelRoute(model, credential, { baseUrl: config.baseUrl, laneDefault: config.laneDefault });
298
314
  };
299
315
  }
300
316
  export function routePairingStatus(brain, model, perModelAuth) {
@@ -101,12 +101,30 @@ export const ROUTE_ADJUDICATION_CONFORMANCE_CORPUS = [
101
101
  leg: "named",
102
102
  },
103
103
  {
104
- name: "deployment-unpinned",
105
- intent: "the quick-start posture: config key with NO declared config root stays allowed (single-endpoint form: key on config, URL on the model) — surfaced as unpinned, not refused",
106
- model: { id: "m-quickstart", baseUrl: "https://api.example.com/v1" },
107
- credential: { source: "deployment-config", apiKey: "k-quickstart" },
104
+ name: "deployment-undeclared-entry-root",
105
+ intent: "a config key with NO declared root and an entry carrying its own root: nothing says the key belongs there — refused, never sent (declare config.baseUrl, or give the entry its own credential)",
106
+ model: { id: "m-catalogue", baseUrl: "https://api.example.com/v1" },
107
+ credential: { source: "deployment-config", apiKey: "k-deployment" },
108
108
  config: {},
109
- expect: { ok: true, posture: "unpinned" },
109
+ expect: { ok: false, code: "route.credential_mismatch" },
110
+ leg: "named",
111
+ },
112
+ {
113
+ name: "deployment-lane-default",
114
+ intent: "a config key with no declared root is paired with the lane's own default endpoint — a request there is paired",
115
+ model: { id: "m-vendor", baseUrl: "https://api.vendor.example" },
116
+ credential: { source: "deployment-config", apiKey: "k-vendor" },
117
+ config: { laneDefault: "https://api.vendor.example" },
118
+ expect: { ok: true, posture: "paired" },
119
+ },
120
+ {
121
+ name: "deployment-lane-default-entry-elsewhere",
122
+ intent: "the same key with an entry rooted away from the lane default is refused, exactly like a declared root",
123
+ model: { id: "m-catalogue", baseUrl: "https://third-party.example.net/v1" },
124
+ credential: { source: "deployment-config", apiKey: "k-vendor" },
125
+ config: { laneDefault: "https://api.vendor.example" },
126
+ expect: { ok: false, code: "route.credential_mismatch" },
127
+ leg: "named",
110
128
  },
111
129
  {
112
130
  name: "absent-keyless-on-root",
@@ -9,7 +9,7 @@ import { FAST_MAX_BACKOFF_MS, providerWaitHint, retryBackoffMs } from "./retry.j
9
9
  import { bindBrainStatusEmitter, emitBrainStatus, emitBrainTelemetry, reportExtraBodyDropped } from "./status-sink.js";
10
10
  import { createAttemptController, firstByteWindow, refuseRetiredTimeoutKnobs, resolveFirstByteRetries, resolveWatchdogMs, STALL_FIRST_BYTE_MS, STALL_FIRST_TOKEN_MS, STALL_IDLE_MS, } from "./timeout.js";
11
11
  import { readMember, thrownText } from "../core/thrown-value.js";
12
- import { redactSecrets } from "../core/untrusted-egress.js";
12
+ import { egressText } from "../core/untrusted-egress.js";
13
13
  const DEFAULT_MAX_RETRIES = 10;
14
14
  const MAX_RETRIES_ENV_CEILING = 15;
15
15
  const MAX_RETRIES_ENV = "SEMA_MAX_RETRIES";
@@ -455,7 +455,7 @@ export function runStreamingBrain(args) {
455
455
  };
456
456
  };
457
457
  try {
458
- r = await doFetch(req.url, { method: "POST", headers: req.headers, body: req.body, signal: cc.signal });
458
+ r = await doFetch(req.url, { method: "POST", headers: req.headers, body: req.body, signal: cc.signal, redirect: "manual" });
459
459
  requestIdSeen = readProviderRequestId(r);
460
460
  if (cc.timedOut()) {
461
461
  discardResponseBody(r);
@@ -474,7 +474,7 @@ export function runStreamingBrain(args) {
474
474
  }
475
475
  else if (classifyFetchThrow(e) === "request_unbuildable") {
476
476
  cc.dispose();
477
- throw new BrainError("invalid_request", `${httpLabel} request could not be built: ${redactSecrets(stripUrlCredentials(thrownText(e)))}`);
477
+ throw new BrainError("invalid_request", `${httpLabel} request could not be built: ${egressText("same-principal", stripUrlCredentials(thrownText(e)))}`);
478
478
  }
479
479
  else {
480
480
  netErr = e;
@@ -486,6 +486,12 @@ export function runStreamingBrain(args) {
486
486
  else
487
487
  headerWait.stop();
488
488
  }
489
+ if (r !== undefined && (r.type === "opaqueredirect" || (r.status >= 300 && r.status < 400))) {
490
+ const status = r.status;
491
+ discardResponseBody(r);
492
+ cc.dispose();
493
+ throw new BrainError("network", `${httpLabel} endpoint answered with a redirect${status > 0 ? ` (HTTP ${status})` : ""}; it was not followed and no credential was sent anywhere else — declare the final endpoint as the brain's baseUrl`);
494
+ }
489
495
  if (r && r.ok && r.body) {
490
496
  cc.clearTimer();
491
497
  connect = cc;
package/dist/core/a2a.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { Type } from "typebox";
3
3
  import { A2A_TASK_STATES } from "./a2a-task-state.js";
4
- import { resolveProtocolHttpHeaders } from "./mcp.js";
4
+ import { assertEndpointBound, redirectPolicy, resolveProtocolHttpHeaders, underDeclaredHeaders } from "./protocol-http-auth.js";
5
5
  import { describeHttpTransportFailure } from "./mcp-failure.js";
6
6
  import { findNamespacePrefixCollision, mintNamespacePrefix, mintNamespacedToolName } from "./protocol-naming.js";
7
7
  import { A2A_NAMESPACE } from "./protocol-table.js";
@@ -146,12 +146,8 @@ async function requestJson(t, what, init) {
146
146
  const res = await doFetch(init.url, {
147
147
  method: init.method,
148
148
  signal: deadline.signal,
149
- headers: {
150
- accept: "application/json",
151
- "a2a-version": A2A_PROTOCOL_VERSION,
152
- ...(init.body !== undefined ? { "content-type": "application/json" } : {}),
153
- ...t.headers,
154
- },
149
+ redirect: redirectPolicy(t.headers),
150
+ headers: underDeclaredHeaders({ accept: "application/json", "a2a-version": A2A_PROTOCOL_VERSION, ...(init.body !== undefined ? { "content-type": "application/json" } : {}) }, t.headers),
155
151
  ...(init.body !== undefined ? { body: init.body } : {}),
156
152
  });
157
153
  const text = await readBoundedText(res, `${what} from A2A peer "${inlineUntrusted(t.peer, A2A_ID_MAX_CHARS)}"`);
@@ -544,10 +540,12 @@ function buildSkillTool(spec, t, skill, lifecycle) {
544
540
  },
545
541
  };
546
542
  }
547
- async function materializePeer(spec, principal, lifecycle, signal) {
543
+ async function materializePeer(declaredSpec, principal, lifecycle, signal) {
544
+ const spec = Object.freeze({ ...declaredSpec });
548
545
  const headers = resolveProtocolHttpHeaders(A2A_NAMESPACE.id, spec, principal);
549
546
  const discovery = { peer: spec.name, endpoint: spec.url, headers };
550
547
  const card = readCard(spec.name, await discoverCardBody(spec, discovery, signal), spec.url);
548
+ assertEndpointBound(A2A_NAMESPACE.id, spec.name, card.endpoint, spec.cardUrl === undefined ? [spec.url] : [spec.url, spec.cardUrl], headers);
551
549
  const transport = { peer: spec.name, endpoint: card.endpoint, headers };
552
550
  const tools = [];
553
551
  const axes = [];
@@ -1,7 +1,7 @@
1
1
  import { type RuleToolNameVerdict } from "./tool-name-aliases.js";
2
2
  import type { ToolCallRequest } from "./tool-policy.js";
3
3
  import { type AllowRuleMember, type AllowRuleShadow } from "./allow-rule-shadow.js";
4
- import { type PathRuleBases, type PersistedRule } from "./permission-rule-model.js";
4
+ import { type AdmissionPosture, type PathRuleBases, type PersistedRule } from "./permission-rule-model.js";
5
5
  import { type AskClass, type AssertAllKeysHandled, type PermissionMode, type PersistedRuleMandate } from "./ask-origin.js";
6
6
  import { type ToolNameListReading } from "./tool-name-list.js";
7
7
  import { type EngineNotice } from "./engine-notice.js";
@@ -10,11 +10,13 @@ import { type EngineNotice } from "./engine-notice.js";
10
10
  * run mounts no tool of that exact name but mounts one that differs only by case (`currentName` is its spelling). */
11
11
  export declare const ALLOW_ENTRY_INERT_REASONS: readonly ["wildcard_all", "namespace_wildcard", "content_unsupported", "grammar", "tool_name", "near_miss"];
12
12
  export type AllowEntryInertReason = (typeof ALLOW_ENTRY_INERT_REASONS)[number];
13
- /** The facts beside a reason: for `tool_name` / `near_miss` the judgment's word and the spelling to write instead when there is
14
- * one; for a list member that is not a string (`grammar`) the TYPE of the value (`a number`, `null`, `an object`…) — `entry` then
15
- * holds {@link nonStringMemberLabel}, never the value. */
13
+ /** The facts beside a reason: for `tool_name` / `near_miss` the judgment's word, the judgment's own one-line sentence when it has
14
+ * one (`guidance`, byte for byte — `retired` and `unmintable_segment` carry one, `unprefixed_namespace` none) and the spelling to
15
+ * write instead when there is one; for a list member that is not a string (`grammar`) the TYPE of the value (`a number`, `null`,
16
+ * `an object`…) — `entry` then holds {@link nonStringMemberLabel}, never the value. */
16
17
  export interface AllowEntryInertDetail {
17
18
  readonly verdict?: Exclude<RuleToolNameVerdict["kind"], "current">;
19
+ readonly guidance?: string;
18
20
  readonly currentName?: string;
19
21
  readonly valueType?: string;
20
22
  }
@@ -60,18 +62,32 @@ export type CompiledAllowEntry = {
60
62
  readonly kind: "domain";
61
63
  readonly rule: PersistedRule;
62
64
  };
65
+ /** What one rule entry means under one direction ({@link readRuleEntry}): the row the grammar compiles it to, or the reason it
66
+ * reads as nothing with the facts a sentence about it names. */
67
+ export type RuleEntryReading = {
68
+ readonly kind: "row";
69
+ readonly compiled: CompiledAllowEntry;
70
+ } | ({
71
+ readonly kind: "inert";
72
+ readonly reason: AllowEntryInertReason;
73
+ } & AllowEntryInertDetail);
63
74
  /**
64
- * Read one entry under one direction. `roster` = the tool names the run mounts (aliases included): given, the entry's tool name
65
- * is also read against it (a stale name, or one that differs from a mounted tool's only by case, reads as `tool_name` /
66
- * `near_miss`); absent, the grammar alone is read — what a resume's pre-claim screen can read before the leg has a roster.
75
+ * THE ONE READER of a rule entry — a member of an entry list in either direction: a skill's `allowed-tools` /
76
+ * `disallowed-tools`, a sub-agent's deny list, the request's and the deployment's `allowedTools`, a session's `toolDeny` /
77
+ * `toolAllow`. Every station that compiles, validates, compares or names such an entry reads this answer and no other.
78
+ * `roster` = the tool names the reader knows are mounted (aliases included): given, the entry's tool name is also read against
79
+ * it (a stale name, or one that differs from a mounted tool's only by case, reads as `tool_name` / `near_miss`); absent, the
80
+ * grammar alone is read — what a resume's pre-claim screen can read before the leg has a roster.
67
81
  */
68
- export declare function compileAllowEntry(raw: string, behavior: "allow" | "deny", roster?: ReadonlySet<string>): CompiledAllowEntry | AllowEntryInertReason;
82
+ export declare function readRuleEntry(raw: unknown, behavior: "allow" | "deny", roster?: ReadonlySet<string>): RuleEntryReading;
83
+ /** The tool name a compiled entry speaks for — its tool, its namespace spelling, or its rule's tool (what a name audit reads). */
84
+ export declare function compiledEntryName(compiled: CompiledAllowEntry): string;
69
85
  /**
70
- * Compile one list WHOLE: the entries it reads and the ones it reads as nothing (each with its reason and, for a name the
71
- * roster refuses, the judgment's word and the spelling to write). An allow list holding `*` voids the list (every other entry
72
- * on it reads as nothing too: the list grants nothing — the safe direction). A `*` on a deny list reads as nothing by itself
73
- * and the rest of that list still refuses: a deny list never loses an entry to a wildcard beside it. `roster` as in
74
- * {@link compileAllowEntry}.
86
+ * Compile one list WHOLE through {@link readRuleEntry}: the entries it reads and the ones it reads as nothing (each with its
87
+ * reason and, for a name the roster refuses, the judgment's word and the spelling to write). An allow list holding `*` voids
88
+ * the list (every other entry on it reads as nothing too: the list grants nothing — the safe direction). A `*` on a deny list
89
+ * reads as nothing by itself and the rest of that list still refuses: a deny list never loses an entry to a wildcard beside
90
+ * it. `roster` as in {@link readRuleEntry}.
75
91
  */
76
92
  export declare function compileAllowList(entries: readonly string[], behavior: "allow" | "deny", roster?: ReadonlySet<string>): {
77
93
  rows: Array<{
@@ -107,16 +123,20 @@ export interface AllowEntryJudgeContext {
107
123
  readonly liveCwd: () => string | undefined;
108
124
  readonly anchors: PathRuleBases;
109
125
  }
126
+ /** For a list whose allow rows admitted no call ({@link judgeAllowEntries}): the first segment of the call's command no command
127
+ * row covers under `posture` — the word a refusal names; `undefined` when the call carries no command or no single segment is
128
+ * to blame (a line the matcher cannot read at all). The same walk the matcher took. */
129
+ export declare function uncoveredCommandSegment(rows: readonly AllowEntryRow[], req: ToolCallRequest, ctx: AllowEntryJudgeContext, posture: AdmissionPosture): string | undefined;
110
130
  /**
111
131
  * The ONE list matcher, both directions — the rows that speak for the call, or undefined. A tool-name row first (the first
112
132
  * that reaches the call); then the content rows, each family by the predicate the stored rows of that grammar meet:
113
- * COMMAND — allow: the persisted allow matcher's coverage (the whole command, or each segment covered by a row or read-only
114
- * by itself); deny: a row whose program run the call performs (the wide tightening reading; a call that cannot be read
115
- * against the row is refused too). PATH — the persisted path adjudication (allow: the narrow reading, every view of the
116
- * target inside what the row names; deny: any view reaching it). NAME / DOMAIN — the one predicate of each (the name the
117
- * call names, the host it fetches).
133
+ * COMMAND — allow: the persisted allow matcher's coverage under the list's {@link AdmissionPosture} (the whole command, or each
134
+ * segment covered by a row — or, for a `grant` list, read-only by itself); deny: a row whose program run the call performs (the
135
+ * wide tightening reading; a call that cannot be read against the row is refused too). PATH — the persisted path adjudication
136
+ * (allow: the narrow reading, every view of the target inside what the row names; deny: any view reaching it). NAME / DOMAIN —
137
+ * the one predicate of each (the name the call names, the host it fetches).
118
138
  */
119
- export declare function judgeAllowEntries<R extends AllowEntryRow>(rows: readonly R[], req: ToolCallRequest, behavior: "allow" | "deny", ctx: AllowEntryJudgeContext): readonly R[] | undefined;
139
+ export declare function judgeAllowEntries<R extends AllowEntryRow>(rows: readonly R[], req: ToolCallRequest, behavior: "allow" | "deny", ctx: AllowEntryJudgeContext, posture?: AdmissionPosture): readonly R[] | undefined;
120
140
  /** The allow rows of a list, as the allow layer reads them in `mode`: the strip applied BEFORE the match (in `auto`). */
121
141
  export declare function allowingRows<R extends AllowEntryRow>(rows: readonly R[], req: ToolCallRequest, mode: PermissionMode | undefined): readonly R[];
122
142
  /** The two sources {@link AllowEntryTable} holds (a skill's list is the skill table's). */
@@ -159,7 +179,7 @@ export declare class AllowEntryTable {
159
179
  private held;
160
180
  private readonly heldEntries;
161
181
  /** Write a list's rows (a row already held is not held twice). Returns the entries that read as nothing. `roster` as in
162
- * {@link compileAllowEntry}: a list the leg itself wrote is read against it, a carried one (a delegating parent's capture) is not. */
182
+ * {@link readRuleEntry}: a list the leg itself wrote is read against it, a carried one (a delegating parent's capture) is not. */
163
183
  seed(entries: readonly string[], roster?: ReadonlySet<string>): readonly AllowEntryInert[];
164
184
  /** The entries held, as written (a copy) — what a delegated child is handed. */
165
185
  entries(): readonly string[];
@@ -210,8 +230,9 @@ export declare function readAllowRuleShadows(value: unknown): readonly AllowRule
210
230
  /** Why a matched entry did not clear the ask, by the ask's class — one row per class, so a class added later is a
211
231
  * compile error here rather than somebody else's reason on the card. Two facts the ask carries are read first, because
212
232
  * they name the reason more exactly than a class shared by several: a table word (`mandate` — the exact mark) and the
213
- * deployment's own authorship (`authoredBy`). */
214
- declare const SHADOW_REASON: {
233
+ * deployment's own authorship (`authoredBy`) — {@link allowRuleShadowSentence} is the reader that applies that order.
234
+ * Exported (frozen) so a host that renders the card's "why it cannot be cleared" half reads the engine's row, never a copy. */
235
+ export declare const SHADOW_REASON: Readonly<{
215
236
  readonly content_question: "the call is the engine's own question to a person";
216
237
  readonly hook: "a PreToolUse hook asks about this call — the deployment's own screening is answered by a person";
217
238
  readonly ancestor: "the task that delegated this one owns this question";
@@ -220,8 +241,7 @@ declare const SHADOW_REASON: {
220
241
  readonly unverifiable_shell_text: "the text this shell will run cannot be read by the engine — it is cleared by confirming this call, never by an allow rule";
221
242
  readonly transcript_write: "this write targets the session transcript — it is cleared by confirming this call, never by an allow rule";
222
243
  readonly org_rule: "an organization policy rule requires approval for this call";
223
- readonly governance_unavailable: "a source of governance for this call cannot currently be read (this deployment's organization policy, or a partition of the person's standing rules), so whether it forbids the call is unknown";
224
- readonly rule_unreadable: "this call could not be read against a deny or ask rule, so whether one forbids it is unknown";
244
+ readonly rule_unreadable: "a rule that may govern this call cannot currently be read (this deployment's organization policy, a partition of the person's standing rules, or this call against a deny or ask rule), so whether one forbids it is unknown";
225
245
  readonly ask_rule: string;
226
246
  readonly denial_limit_fallback: "the auto-mode classifier refused too many calls in a row, so a person decides";
227
247
  readonly classifier_no_ruling: "the auto-mode classifier could not judge this call, so a person decides";
@@ -238,7 +258,7 @@ declare const SHADOW_REASON: {
238
258
  readonly mode: "the permission mode puts this question to a person";
239
259
  readonly unstated: "this call arrived already marked as requiring confirmation (an earlier layer of this run, or the task that delegated it, judged it so) — it is cleared by confirming this call, never by a standing allow rule";
240
260
  readonly ordinary: "this call arrived already marked as requiring confirmation (an earlier layer of this run, or the task that delegated it, judged it so) — it is cleared by confirming this call, never by a standing allow rule";
241
- };
261
+ }>;
242
262
  /** The fence over the reason table: `never` while every class has a row. */
243
263
  export type ShadowReasonTableCoversEveryClass = AssertAllKeysHandled<Exclude<AskClass, keyof typeof SHADOW_REASON>>;
244
264
  /**
@@ -292,4 +312,3 @@ export declare function inertEntryPhrase(e: AllowEntryInert): string;
292
312
  * `disallowed-tools` holds such an entry is named as one that does not activate.
293
313
  */
294
314
  export declare function allowedToolsNotGrantedNotice(inert: readonly AllowEntryInertNote[], sessionId: string | undefined): EngineNotice;
295
- export {};