@sema-agent/core 7.52.0 → 7.53.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 (191) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/README.md +3 -1
  3. package/dist/agents/cross-session-envelope.d.ts +8 -0
  4. package/dist/agents/cross-session-envelope.js +6 -0
  5. package/dist/agents/cross-session-judge.d.ts +21 -1
  6. package/dist/agents/cross-session-judge.js +8 -0
  7. package/dist/agents/cross-session-ref.d.ts +5 -0
  8. package/dist/agents/cross-session-ref.js +1 -0
  9. package/dist/agents/org-delivery-gate.d.ts +122 -0
  10. package/dist/agents/org-delivery-gate.js +69 -0
  11. package/dist/agents/peer-held-queue.d.ts +9 -4
  12. package/dist/agents/peer-held-queue.js +6 -5
  13. package/dist/agents/peer-session-drain.d.ts +35 -5
  14. package/dist/agents/peer-session-drain.js +271 -180
  15. package/dist/agents/read-inbox-tool.d.ts +7 -0
  16. package/dist/agents/read-inbox-tool.js +5 -1
  17. package/dist/agents/subagent.d.ts +3 -0
  18. package/dist/agents/subagent.js +99 -72
  19. package/dist/agents/tool-filter.d.ts +18 -0
  20. package/dist/agents/tool-filter.js +6 -0
  21. package/dist/brain/anthropic.d.ts +6 -1
  22. package/dist/brain/anthropic.js +4 -4
  23. package/dist/brain/model-presets.js +2 -1
  24. package/dist/brain/route-adjudicator.d.ts +6 -0
  25. package/dist/brain/routing.d.ts +24 -1
  26. package/dist/brain/routing.js +54 -19
  27. package/dist/brain/timeout.js +5 -4
  28. package/dist/core/a2a.d.ts +5 -2
  29. package/dist/core/a2a.js +30 -13
  30. package/dist/core/allow-entry.d.ts +2 -1
  31. package/dist/core/allow-entry.js +2 -2
  32. package/dist/core/auto-compaction.d.ts +57 -0
  33. package/dist/core/auto-compaction.js +28 -2
  34. package/dist/core/checkpoint-store.d.ts +29 -20
  35. package/dist/core/checkpoint-store.js +3 -3
  36. package/dist/core/compliance.js +3 -1
  37. package/dist/core/effective-path-target.d.ts +25 -6
  38. package/dist/core/effective-path-target.js +24 -2
  39. package/dist/core/engine-notice.d.ts +13 -2
  40. package/dist/core/gate-fold.js +3 -2
  41. package/dist/core/gate-lanes.js +11 -13
  42. package/dist/core/gate-outcome.d.ts +11 -6
  43. package/dist/core/governance-codes.d.ts +3 -1
  44. package/dist/core/governance-codes.js +3 -0
  45. package/dist/core/hands-band-screen.d.ts +7 -0
  46. package/dist/core/hands-band-screen.js +32 -0
  47. package/dist/core/hooks.d.ts +7 -5
  48. package/dist/core/hooks.js +5 -3
  49. package/dist/core/locked-config.d.ts +84 -32
  50. package/dist/core/locked-config.js +128 -42
  51. package/dist/core/mcp-probe.js +1 -1
  52. package/dist/core/mcp-server-spec.d.ts +88 -10
  53. package/dist/core/mcp-server-spec.js +183 -33
  54. package/dist/core/mcp.d.ts +21 -14
  55. package/dist/core/mcp.js +55 -51
  56. package/dist/core/memory-engine/engine.d.ts +14 -19
  57. package/dist/core/memory-engine/engine.js +7 -5
  58. package/dist/core/mode-policy.d.ts +3 -18
  59. package/dist/core/mode-policy.js +2 -16
  60. package/dist/core/mods/kernel/dispatch.js +13 -15
  61. package/dist/core/mods/kernel/events.d.ts +9 -6
  62. package/dist/core/mods/kernel/events.js +2 -2
  63. package/dist/core/mods/kernel/receive.d.ts +39 -10
  64. package/dist/core/mods/kernel/receive.js +119 -54
  65. package/dist/core/mods/kernel/site-rules.d.ts +80 -15
  66. package/dist/core/mods/kernel/site-rules.js +232 -40
  67. package/dist/core/mods/stations.d.ts +0 -6
  68. package/dist/core/mods/stations.js +0 -7
  69. package/dist/core/mounted-mcp.d.ts +57 -0
  70. package/dist/core/mounted-mcp.js +71 -2
  71. package/dist/core/own-property-receipt.d.ts +13 -0
  72. package/dist/core/own-property-receipt.js +3 -1
  73. package/dist/core/parent-spec-seat.d.ts +2 -2
  74. package/dist/core/permission-rule-model.d.ts +58 -9
  75. package/dist/core/permission-rule-model.js +81 -7
  76. package/dist/core/permission-rule-org.d.ts +163 -33
  77. package/dist/core/permission-rule-org.js +205 -71
  78. package/dist/core/permission-rules.d.ts +7 -1
  79. package/dist/core/permission-rules.js +37 -8
  80. package/dist/core/retention.js +7 -2
  81. package/dist/core/retired-keys.d.ts +8 -2
  82. package/dist/core/retired-keys.js +12 -1
  83. package/dist/core/roles.js +60 -10
  84. package/dist/core/rule-lane-reads.js +1 -1
  85. package/dist/core/runner/contracts.d.ts +1 -0
  86. package/dist/core/runner/gate-exit.js +4 -3
  87. package/dist/core/runner/mcp-redial.d.ts +52 -10
  88. package/dist/core/runner/mcp-redial.js +38 -9
  89. package/dist/core/runner/permission-rule-lanes.d.ts +5 -2
  90. package/dist/core/runner/permission-rule-lanes.js +22 -10
  91. package/dist/core/runner/person-path-rows.d.ts +3 -1
  92. package/dist/core/runner/person-path-rows.js +2 -2
  93. package/dist/core/runner/prepare-config-doors.d.ts +48 -2
  94. package/dist/core/runner/prepare-config-doors.js +161 -145
  95. package/dist/core/runner/prepare-defer-classify.js +4 -7
  96. package/dist/core/runner/prepare-delegation-surface.d.ts +2 -2
  97. package/dist/core/runner/prepare-delegation-surface.js +7 -5
  98. package/dist/core/runner/prepare-hands-readface.d.ts +1 -1
  99. package/dist/core/runner/prepare-hands-readface.js +2 -1
  100. package/dist/core/runner/prepare-inherited-gate.js +2 -2
  101. package/dist/core/runner/prepare-listings.d.ts +1 -1
  102. package/dist/core/runner/prepare-listings.js +4 -2
  103. package/dist/core/runner/prepare-memory-engine-session.d.ts +0 -6
  104. package/dist/core/runner/prepare-memory-engine-session.js +19 -6
  105. package/dist/core/runner/prepare-memory.d.ts +3 -2
  106. package/dist/core/runner/prepare-memory.js +6 -2
  107. package/dist/core/runner/prepare-offload-wrappers.d.ts +4 -4
  108. package/dist/core/runner/prepare-offload-wrappers.js +2 -1
  109. package/dist/core/runner/prepare-policy-chain.d.ts +1 -1
  110. package/dist/core/runner/prepare-policy-chain.js +12 -12
  111. package/dist/core/runner/prepare-project-context.d.ts +1 -1
  112. package/dist/core/runner/prepare-project-context.js +6 -4
  113. package/dist/core/runner/prepare-prompt-assembly.d.ts +1 -1
  114. package/dist/core/runner/prepare-prompt-assembly.js +20 -16
  115. package/dist/core/runner/prepare-prompt-inputs.d.ts +12 -6
  116. package/dist/core/runner/prepare-prompt-inputs.js +6 -3
  117. package/dist/core/runner/prepare-protocol-tools.d.ts +7 -5
  118. package/dist/core/runner/prepare-protocol-tools.js +19 -12
  119. package/dist/core/runner/prepare-question-face.d.ts +4 -2
  120. package/dist/core/runner/prepare-question-face.js +4 -3
  121. package/dist/core/runner/prepare-task.js +10 -10
  122. package/dist/core/runner/prepare-wiring-manifest.d.ts +8 -3
  123. package/dist/core/runner/prepare-wiring-manifest.js +8 -5
  124. package/dist/core/runner/resume-admission.d.ts +4 -1
  125. package/dist/core/runner/resume-admission.js +7 -5
  126. package/dist/core/runner/resume-internals-and-config.d.ts +3 -3
  127. package/dist/core/runner/resume-internals-and-config.js +3 -8
  128. package/dist/core/runner/retired-runner-deps.d.ts +5 -6
  129. package/dist/core/runner/retired-runner-deps.js +1 -4
  130. package/dist/core/runner/run-leg.js +42 -12
  131. package/dist/core/runner/run-recovery-lanes.js +5 -3
  132. package/dist/core/runner/run-terminal-adoption.js +6 -5
  133. package/dist/core/runner/run-turn-boundary.js +9 -3
  134. package/dist/core/runner/runtask.js +1 -1
  135. package/dist/core/runner/skills-seat.d.ts +15 -0
  136. package/dist/core/runner/skills-seat.js +57 -0
  137. package/dist/core/runner/sub-agent-deny-lists.d.ts +13 -11
  138. package/dist/core/runner/sub-agent-deny-lists.js +7 -5
  139. package/dist/core/runner/synthetic-tools.d.ts +5 -2
  140. package/dist/core/runner/synthetic-tools.js +2 -12
  141. package/dist/core/runner-deps.d.ts +29 -14
  142. package/dist/core/session-policy-store.d.ts +19 -0
  143. package/dist/core/session-policy-store.js +22 -2
  144. package/dist/core/store-contracts/mailbox-store-contract.js +13 -0
  145. package/dist/core/task-event.d.ts +38 -2
  146. package/dist/core/task-notification.js +2 -2
  147. package/dist/core/task-spec.d.ts +1 -1
  148. package/dist/core/tool-policy.d.ts +3 -0
  149. package/dist/core/tool-policy.js +10 -4
  150. package/dist/core/types.d.ts +2 -2
  151. package/dist/core/types.js +1 -1
  152. package/dist/core/untrusted-text.d.ts +16 -0
  153. package/dist/core/untrusted-text.js +7 -2
  154. package/dist/core/user-attachment.d.ts +13 -2
  155. package/dist/core/user-attachment.js +47 -41
  156. package/dist/core/wiring-manifest.d.ts +4 -2
  157. package/dist/engine/harness/agent-harness.js +2 -1
  158. package/dist/engine/harness/types.d.ts +3 -0
  159. package/dist/engine/loop/agent-loop.d.ts +12 -1
  160. package/dist/engine/loop/agent-loop.js +17 -5
  161. package/dist/engine/loop/types.d.ts +7 -1
  162. package/dist/index.d.ts +15 -9
  163. package/dist/index.js +12 -6
  164. package/dist/internal/harness.d.ts +1 -0
  165. package/dist/internal/harness.js +1 -0
  166. package/dist/orchestration/goal.d.ts +24 -1
  167. package/dist/orchestration/goal.js +3 -2
  168. package/dist/prompt-assembly/artifact-store.d.ts +44 -9
  169. package/dist/prompt-assembly/artifact-store.js +38 -12
  170. package/dist/prompt-assembly/artifact.d.ts +12 -11
  171. package/dist/prompt-assembly/artifact.js +52 -21
  172. package/dist/prompt-assembly/assemble.d.ts +2 -0
  173. package/dist/prompt-assembly/assemble.js +2 -1
  174. package/dist/prompt-assembly/catalog/index.js +2 -1
  175. package/dist/prompt-assembly/catalog/memory.d.ts +13 -0
  176. package/dist/prompt-assembly/catalog/memory.js +6 -0
  177. package/dist/prompt-assembly/catalog/schema.d.ts +16 -2
  178. package/dist/prompt-assembly/composer.d.ts +2 -0
  179. package/dist/prompt-assembly/composer.js +1 -1
  180. package/dist/prompt-assembly/overlay.d.ts +21 -11
  181. package/dist/prompt-assembly/overlay.js +55 -17
  182. package/dist/prompt-assembly/select.d.ts +21 -6
  183. package/dist/prompt-assembly/select.js +39 -2
  184. package/dist/prompts/default.d.ts +0 -11
  185. package/dist/prompts/default.js +0 -3
  186. package/dist/stores/file/permission-rule-store.d.ts +1 -1
  187. package/dist/stores/file/permission-rule-store.js +2 -1
  188. package/dist/tools/artifact/artifact-tool.js +4 -3
  189. package/package.json +1 -1
  190. package/test/export-surface.snapshot.json +65 -5
  191. package/wire-changes/core@7.53.0.json +1946 -0
@@ -1,11 +1,29 @@
1
1
  import { selectModel } from "./select-model.js";
2
2
  import { MODEL_ROLES } from "./types.js";
3
3
  export { MODEL_ROLES } from "./types.js";
4
+ function catalogModel(ref, models) {
5
+ const m = models != null && (typeof models === "object" || typeof models === "function") && Object.hasOwn(models, ref) ? models[ref] : undefined;
6
+ return m || undefined;
7
+ }
8
+ function modelBindingRefusal(seat, reason, message) {
9
+ const e = new Error(message);
10
+ e.code = "config.unknown_role";
11
+ e.detail = { field: seat, reason };
12
+ return e;
13
+ }
14
+ function bindingShown(value) {
15
+ if (typeof value === "string")
16
+ return JSON.stringify(value.length > 80 ? `${value.slice(0, 80)}…` : value);
17
+ return value === null ? "null" : Array.isArray(value) ? "a list" : `a ${typeof value}`;
18
+ }
19
+ function unknownModelRefRefusal(ref, seat) {
20
+ return modelBindingRefusal(seat, "unknown_model_ref", `Unknown model ref ${bindingShown(ref)} in ${seat}. Provide it in RunnerDeps.models or pass a Model object.`);
21
+ }
4
22
  export function resolveModel(ref, models) {
5
23
  if (typeof ref !== "string") {
6
24
  return ref;
7
25
  }
8
- const m = models !== undefined && Object.hasOwn(models, ref) ? models[ref] : undefined;
26
+ const m = catalogModel(ref, models);
9
27
  if (!m) {
10
28
  throw new Error(`Unknown model ref "${ref}". Provide it in RunnerDeps.models or pass a Model object.`);
11
29
  }
@@ -33,7 +51,19 @@ export function expandTiers(models, tiers) {
33
51
  out[TIER_ALIAS_VIA] = aliasVia;
34
52
  const bound = (name) => {
35
53
  const b = Object.hasOwn(tiers, name) ? tiers[name] : undefined;
36
- return b === undefined ? undefined : resolveModel(b, models);
54
+ if (b === undefined)
55
+ return undefined;
56
+ const seat = `tiers.${name}`;
57
+ if (typeof b === "string") {
58
+ const held = catalogModel(b, models);
59
+ if (held === undefined)
60
+ throw unknownModelRefRefusal(b, seat);
61
+ return held;
62
+ }
63
+ if (typeof b !== "object" || b === null || Array.isArray(b)) {
64
+ throw modelBindingRefusal(seat, "not_a_model_ref", `${seat} is ${bindingShown(b)}, not a model reference — a tier binds a catalog model name or a Model object.`);
65
+ }
66
+ return b;
37
67
  };
38
68
  const ownKey = (k) => models !== undefined && Object.hasOwn(models, k) && Boolean(models[k]);
39
69
  const landing = (t, self) => {
@@ -131,13 +161,16 @@ const FALLBACK = {
131
161
  classifier: ["summarize", "default"],
132
162
  consolidate: ["summarize"],
133
163
  };
134
- function specParts(s) {
164
+ function specParts(s, seat = "a role seat") {
135
165
  if (s == null) {
136
166
  return {};
137
167
  }
138
168
  if (typeof s === "string") {
139
169
  return { model: s };
140
170
  }
171
+ if (typeof s !== "object" && typeof s !== "function") {
172
+ throw modelBindingRefusal(seat, "not_a_model_ref", `${seat} is ${bindingShown(s)}, not a model reference — a role binds a catalog model name, a Model object or a { model, select, thinking, systemPrompt } record.`);
173
+ }
141
174
  if ("id" in s && "api" in s) {
142
175
  return { model: s };
143
176
  }
@@ -191,12 +224,17 @@ function mergeRoles(base, over) {
191
224
  }
192
225
  return out;
193
226
  }
194
- function partsModel(parts, models) {
227
+ function partsModel(parts, models, seat = "a role seat") {
195
228
  if (parts.model) {
196
229
  return resolveModel(parts.model, models);
197
230
  }
198
231
  if (parts.select) {
199
- return selectModel(models ?? {}, parts.select);
232
+ const needs = parts.select.needs;
233
+ if (needs != null && !Array.isArray(needs)) {
234
+ throw modelBindingRefusal(`${seat}.select`, "not_a_model_ref", `${seat}.select.needs is ${bindingShown(needs)}, not a list of capabilities (vision / reasoning).`);
235
+ }
236
+ const held = models != null && typeof models === "object" ? Object.values(models).filter(Boolean) : [];
237
+ return selectModel(held, parts.select);
200
238
  }
201
239
  return undefined;
202
240
  }
@@ -204,7 +242,7 @@ export function resolveTaskModel(spec, deps) {
204
242
  const start = spec.modelRole ?? "default";
205
243
  assertKnownRole(start, "modelRole");
206
244
  if (spec.model) {
207
- const startParts = specParts(mergeRoles(deps.roles, spec.roles)[start]);
245
+ const startParts = specParts(mergeRoles(deps.roles, spec.roles)[start], `roles.${start}`);
208
246
  return {
209
247
  model: resolveModel(spec.model, deps.models),
210
248
  modelRef: spec.model,
@@ -213,10 +251,10 @@ export function resolveTaskModel(spec, deps) {
213
251
  };
214
252
  }
215
253
  const merged = mergeRoles(deps.roles, spec.roles);
216
- const startSystemPrompt = specParts(merged[start]).systemPrompt;
254
+ const startSystemPrompt = specParts(merged[start], `roles.${start}`).systemPrompt;
217
255
  for (const role of [start, ...FALLBACK[start]]) {
218
- const parts = specParts(merged[role]);
219
- const fromParts = partsModel(parts, deps.models);
256
+ const parts = specParts(merged[role], `roles.${role}`);
257
+ const fromParts = partsModel(parts, deps.models, `roles.${role}`);
220
258
  const model = fromParts ?? roleTierModel(role, deps.models);
221
259
  if (model) {
222
260
  const modelRef = fromParts !== undefined ? (parts.model ?? model) : (ROLE_TIER_DEFAULTS[role] ?? model);
@@ -227,5 +265,17 @@ export function resolveTaskModel(spec, deps) {
227
265
  }
228
266
  export function roleModelIfSet(role, spec, deps) {
229
267
  const merged = mergeRoles(deps.roles, spec.roles);
230
- return partsModel(specParts(merged[role]), deps.models) ?? roleTierModel(role, deps.models);
268
+ const seat = `roles.${role}`;
269
+ const parts = specParts(merged[role], seat);
270
+ const named = parts.model;
271
+ if (named) {
272
+ if (typeof named === "string") {
273
+ if (catalogModel(named, deps.models) === undefined)
274
+ throw unknownModelRefRefusal(named, seat);
275
+ }
276
+ else if (typeof named !== "object" || Array.isArray(named)) {
277
+ throw modelBindingRefusal(seat, "not_a_model_ref", `${seat}.model is ${bindingShown(named)}, not a model reference — a role binds a catalog model name or a Model object.`);
278
+ }
279
+ }
280
+ return partsModel(parts, deps.models, seat) ?? roleTierModel(role, deps.models);
231
281
  }
@@ -65,7 +65,7 @@ export function normalizeOrgGateVerdict(answer, unreadable) {
65
65
  if ((behavior !== "deny" && behavior !== "ask") || typeof rule !== "string")
66
66
  return clean({ status: "unavailable", disclosures: [unreadable] });
67
67
  const reason = ownDataValue(verdict, "unreadable");
68
- const unreadableCell = behavior === "ask" && typeof reason === "string" ? { unreadable: reason } : {};
68
+ const unreadableCell = typeof reason === "string" ? { unreadable: reason } : {};
69
69
  return clean({ status: "available", verdict: clean({ behavior: behavior, rule, ...unreadableCell }), ...revisionCell });
70
70
  }
71
71
  function copiedDenial(raw, ruleTexts) {
@@ -2938,6 +2938,7 @@ export interface ManualCompactRef {
2938
2938
  */
2939
2939
  export type EndOfTaskCompaction = {
2940
2940
  compacted: boolean;
2941
+ pass?: string;
2941
2942
  tokensBefore?: number;
2942
2943
  freedTokens?: number;
2943
2944
  triggerTokens?: number;
@@ -5,6 +5,7 @@ import { isProtectedWrite } from "../tool-registry.js";
5
5
  import { attestationOf, decisionText } from "../tool-policy.js";
6
6
  import { pathDenialCardOf } from "../../tools/fs/safety.js";
7
7
  import { boundInputHashOf } from "../canonical-json.js";
8
+ import { argsAsTheToolReads, argsReadFrom } from "../effective-path-target.js";
8
9
  import { askUnresolvableRemedySeat } from "../ask-unresolvable-notice.js";
9
10
  import { inlineUntrusted } from "../untrusted-text.js";
10
11
  import { formatHookFeedback } from "../reminder-mint.js";
@@ -127,7 +128,7 @@ export function createSettlementLedger(call) {
127
128
  return { decision, deniedBy: refusal ? "ask_resolution" : "policy" };
128
129
  },
129
130
  keepForBytes(runs) {
130
- if (settled !== undefined && settled.licensed !== boundInputHashOf(runs))
131
+ if (settled !== undefined && settled.licensed !== boundInputHashOf(runs) && settled.licensed !== boundInputHashOf(argsReadFrom(runs)))
131
132
  settled = undefined;
132
133
  },
133
134
  causeOf(decision) {
@@ -220,7 +221,7 @@ export async function exitGate(pass) {
220
221
  const { input, toolName, toolCallId, ledger, notifyPermissionDenied } = pass;
221
222
  if (pass.decision.action === "deny") {
222
223
  if (pass.decision.updatedInput !== undefined) {
223
- pass.currentInput = pass.decision.updatedInput;
224
+ pass.currentInput = argsAsTheToolReads(pass.req, pass.decision.updatedInput);
224
225
  }
225
226
  if (pass.deniedBy === undefined)
226
227
  throw new Error(`the tool gate refused "${toolName}" without a refusing layer — every deny site attributes itself`);
@@ -265,7 +266,7 @@ export async function exitGate(pass) {
265
266
  if (pass.decision.action === "allow") {
266
267
  const rw = pass.decision.updatedInput !== undefined ? pass.decision.updatedInput : pass.policyRewrite;
267
268
  if (rw !== undefined)
268
- pass.currentInput = rw;
269
+ pass.currentInput = argsAsTheToolReads(pass.req, rw);
269
270
  }
270
271
  ledger.keepForBytes(pass.currentInput);
271
272
  const release = pass.arbitration?.kind === "released" ? pass.arbitration : (() => { if (ledger.settled !== undefined)
@@ -15,22 +15,28 @@
15
15
  */
16
16
  import type { AgentTool } from "../../internal/harness.js";
17
17
  import type { McpDroppedTool, McpReconnectResult, MaterializedMcp, McpToolAxis } from "../mcp.js";
18
- import type { RunnerDeps, ToolEffect } from "../types.js";
18
+ import type { RunnerDeps, TaskSpec, ToolEffect } from "../types.js";
19
19
  import type { IrreversibilityTier, ToolFaceSnapshot } from "./contracts.js";
20
20
  import type { RosterBuilder, ToolRosterDeltaSeat } from "../tool-roster.js";
21
+ import type { McpInstructionsEntry } from "../prompt-section-seat.js";
21
22
  /** One server's assertion about its own prefix domain — the shape both faces reduce to. `tools` is
22
23
  * the roster the domain must END UP with (empty withdraws it); `added`/`removed` are the reporting
23
- * half and never drive the swap. */
24
+ * half and never drive the swap. `instructions`: the server's current connection's instructions entry, when the face that produced
25
+ * the delta hands it back (a refresh does; a re-dial queues its new text itself). */
24
26
  export interface McpRosterDelta {
25
27
  server: string;
26
28
  prefix: string;
27
29
  tools: AgentTool[];
28
30
  axes: McpToolAxis[];
29
31
  dropped: McpDroppedTool[];
32
+ instructions?: McpInstructionsEntry;
30
33
  }
31
34
  /** The seats a roster application writes through. Every one of them is borrowed from the phase that
32
35
  * minted it — this module owns nothing. */
33
36
  export interface McpRosterApplyInput {
37
+ /** borrowed-readonly — the run's spec; only `excludeAllTools` is read: the valve's whole-face switch decides what a live roster
38
+ * change may mount, beside the selector (`valveLeaves`). */
39
+ spec: Pick<TaskSpec, "excludeAllTools">;
34
40
  /** borrowed-readonly — the frozen tool face; its selector keeps a tool the leg may not use from ever
35
41
  * being mounted by a face the model or a host can trigger. */
36
42
  toolFaceSnapshot: Pick<ToolFaceSnapshot, "selector">;
@@ -48,20 +54,53 @@ export interface McpRosterApplyInput {
48
54
  }>;
49
55
  /** borrowed-mutable — the run's shared roster. */
50
56
  roster: RosterBuilder;
57
+ /** borrowed-readonly — the run's frozen MCP server declarations: a roster reading is of a declared server, and a name none is declared under holds nothing. */
58
+ declared: ReadonlyArray<{
59
+ readonly name: string;
60
+ }>;
61
+ /** borrowed-mutable — the materialization's instructions delta lane; a roster change settles the server's adds on it. */
62
+ instructionsDelta: MaterializedMcp["instructionsDelta"];
63
+ }
64
+ /** What one application did to the server's domain on the roster — read off the roster before and after the swap, the ONE source of
65
+ * "how many of this server's tools are mounted" for the re-dial's notice and receipt and the refresh tool's receipt. A name the
66
+ * valve left out, a name the deployment's `toolFaces` overlay refused and a name the server listed but the roster did not take are
67
+ * all simply absent from `mounted`. */
68
+ export interface McpRosterReading {
69
+ /** The names of the server's tools the roster holds after the application, in wire order. */
70
+ mounted: string[];
71
+ /** The names `mounted` holds that the roster did not hold before. */
72
+ added: string[];
73
+ /** The names the roster held before that `mounted` no longer holds. */
74
+ removed: string[];
75
+ /** The names the server listed that the roster did not take (the valve's, the overlay's). */
76
+ excluded: string[];
51
77
  }
52
78
  /** What one application did, for the caller's receipt. `applied:false` means the axis fold refused
53
- * and the domain was left exactly as it was (the tables restored) — no roster write happened. */
54
- export type McpRosterApplyOutcome = {
79
+ * and the domain was left exactly as it was (the tables restored) — no roster write happened, so `mounted` is what it held. */
80
+ export type McpRosterApplyOutcome = ({
55
81
  applied: true;
56
- pushed: number;
57
- excluded: string[];
58
- } | {
82
+ } & McpRosterReading) | ({
59
83
  applied: false;
60
84
  foldError: string;
61
- };
85
+ } & McpRosterReading);
86
+ /** What the roster holds of one server with no swap made — the reading of an arm that asserted no roster (a failed refresh, a refused re-dial, a fold refusal).
87
+ * A name no server is declared under holds nothing, even when it mints a declared server's prefix. */
88
+ export declare function rosterHolding(input: Pick<McpRosterApplyInput, "roster" | "declared">, serverName: string): McpRosterReading;
89
+ /** A refresh or re-dial result read off the roster instead of the server's own listing: `toolCount` is the tools mounted, `added` /
90
+ * `removed` the names that came onto / left the roster, and — on a result that asserts a roster — `tools` the roster's tools of the
91
+ * server (with `axes` cut to them), so `toolCount === tools.length`. One meaning, the one `MaterializedMcp.refresh` / `reconnect` give
92
+ * for their own handles. */
93
+ export declare function settledOnRoster<T extends {
94
+ toolCount: number;
95
+ added: string[];
96
+ removed: string[];
97
+ tools?: AgentTool[];
98
+ axes?: McpToolAxis[];
99
+ }>(result: T, reading: McpRosterReading, roster: Pick<RosterBuilder, "tools">): T;
62
100
  /**
63
- * Apply one server's delta to the live roster. Behavior-identical to the body this was extracted
64
- * from; the comments are the extracted reasoning, not new claims.
101
+ * Apply one server's delta to the live roster, and settle the server's instruction adds on the delta lane: the teaching answer over the
102
+ * roster after the swap, against the lane's ledger of what the model holds (both faces — the model's refresh and the host's re-dial —
103
+ * through this one step).
65
104
  */
66
105
  export declare function applyMcpRosterDelta(input: McpRosterApplyInput, delta: McpRosterDelta): McpRosterApplyOutcome;
67
106
  /** The seats the post-application commit writes through. */
@@ -105,5 +144,8 @@ export interface McpRedialInput extends McpRosterApplyInput, McpRosterCommitInpu
105
144
  * leave the model holding tools that can only fail), and an arm that never touched the connection —
106
145
  * unknown name, revoked server, re-dial already in flight, disposed materialization — asserts nothing
107
146
  * and the mounted roster is left alone.
147
+ *
148
+ * The result it hands back counts what the ROSTER took: `toolCount` is the server's tools mounted after the swap, `added` /
149
+ * `removed` the names that came onto / left the roster, and the notice reads the same three — one source for "how many are mounted".
108
150
  */
109
151
  export declare function redialMcpServer(input: McpRedialInput, server: string): Promise<McpReconnectResult>;
@@ -1,9 +1,31 @@
1
+ import { valveLeaves } from "../../agents/tool-filter.js";
2
+ import { instructionsTaught, instructionsTold, tellInstructions, toolNamesOfServer, withdrawInstructionAdds } from "../mounted-mcp.js";
1
3
  import { deliverEngineNotice } from "../types.js";
2
4
  import { thrownText } from "../thrown-value.js";
5
+ export function rosterHolding(input, serverName) {
6
+ const mounted = input.declared.some((d) => d.name === serverName) ? toolNamesOfServer(input.roster.tools.map((t) => t.name), serverName) : [];
7
+ return { mounted, added: [], removed: [], excluded: [] };
8
+ }
9
+ export function settledOnRoster(result, reading, roster) {
10
+ const settled = { ...result, toolCount: reading.mounted.length, added: reading.added, removed: reading.removed };
11
+ if (result.tools === undefined)
12
+ return settled;
13
+ const held = new Set(reading.mounted);
14
+ return { ...settled, tools: roster.tools.filter((t) => held.has(t.name)), ...(result.axes !== undefined ? { axes: result.axes.filter((a) => held.has(a.name)) } : {}) };
15
+ }
16
+ function settleInstructions(lane, delta, taught) {
17
+ if (!taught)
18
+ return withdrawInstructionAdds(lane, delta.server);
19
+ if (delta.instructions !== undefined && !instructionsTold(lane, delta.server) && !lane.pendingAdds.some((e) => e.server === delta.server))
20
+ tellInstructions(lane, delta.instructions);
21
+ }
3
22
  export function applyMcpRosterDelta(input, delta) {
4
- const { toolFaceSnapshot, withFaces, remoteToolOffload, foldProtocolAxes, toolEffects, axisExplicitNegatives, roster } = input;
5
- const pushable = delta.tools.filter((t) => toolFaceSnapshot.selector.admits(t));
6
- const excluded = delta.tools.filter((t) => !toolFaceSnapshot.selector.admits(t)).map((t) => t.name);
23
+ const { spec, toolFaceSnapshot, withFaces, remoteToolOffload, foldProtocolAxes, toolEffects, axisExplicitNegatives, roster } = input;
24
+ const domainNow = () => toolNamesOfServer(roster.tools.map((t) => t.name), delta.server);
25
+ const before = domainNow();
26
+ const leaves = valveLeaves(spec, toolFaceSnapshot.selector);
27
+ const pushable = delta.tools.filter(leaves);
28
+ const valveExcluded = delta.tools.filter((t) => !leaves(t)).map((t) => t.name);
7
29
  const domainSnapshot = (m) => new Map([...m].filter(([name]) => name.startsWith(delta.prefix)));
8
30
  const restoreDomain = (m, snap) => {
9
31
  for (const name of [...m.keys()])
@@ -19,15 +41,20 @@ export function applyMcpRosterDelta(input, delta) {
19
41
  for (const name of priorDomainNegatives.keys())
20
42
  axisExplicitNegatives.delete(name);
21
43
  try {
22
- foldProtocolAxes(delta.axes.filter((a) => !excluded.includes(a.name)), "MCP");
44
+ foldProtocolAxes(delta.axes.filter((a) => !valveExcluded.includes(a.name)), "MCP");
23
45
  }
24
46
  catch (foldErr) {
25
47
  restoreDomain(toolEffects, priorDomainEffects);
26
48
  restoreDomain(axisExplicitNegatives, priorDomainNegatives);
27
- return { applied: false, foldError: thrownText(foldErr) };
49
+ settleInstructions(input.instructionsDelta, delta, instructionsTaught(leaves, delta.server, roster.tools, delta.tools));
50
+ return { applied: false, foldError: thrownText(foldErr), ...rosterHolding(input, delta.server) };
28
51
  }
29
52
  roster.replaceDomain(delta.prefix, withFaces(pushable, delta.server).map((t) => remoteToolOffload(t)), { source: "mcp", mountedBy: "optional", origin: { peer: delta.server } });
30
- return { applied: true, pushed: pushable.length, excluded };
53
+ const mounted = domainNow();
54
+ const heldNow = new Set(mounted);
55
+ const heldBefore = new Set(before);
56
+ settleInstructions(input.instructionsDelta, delta, instructionsTaught(leaves, delta.server, roster.tools, delta.tools));
57
+ return { applied: true, mounted, added: mounted.filter((n) => !heldBefore.has(n)), removed: before.filter((n) => !heldNow.has(n)), excluded: delta.tools.map((t) => t.name).filter((n) => !heldNow.has(n)) };
31
58
  }
32
59
  export async function commitMcpRosterChanges(input) {
33
60
  await input.rebuildHarnessToolsRef.current?.();
@@ -38,9 +65,11 @@ export async function commitMcpRosterChanges(input) {
38
65
  return undefined;
39
66
  }
40
67
  export async function redialMcpServer(input, server) {
41
- const result = await input.mcp.reconnect(server);
42
- if (result.tools !== undefined) {
43
- const outcome = applyMcpRosterDelta(input, { server: result.server, prefix: result.prefix, tools: result.tools, axes: result.axes ?? [], dropped: result.dropped ?? [] });
68
+ const listed = await input.mcp.reconnect(server);
69
+ let result = settledOnRoster(listed, rosterHolding(input, listed.server), input.roster);
70
+ if (listed.tools !== undefined) {
71
+ const outcome = applyMcpRosterDelta(input, { server: listed.server, prefix: listed.prefix, tools: listed.tools, axes: listed.axes ?? [], dropped: listed.dropped ?? [] });
72
+ result = settledOnRoster(listed, outcome, input.roster);
44
73
  if (!outcome.applied)
45
74
  input.deps.onError?.(Object.assign(new Error(`MCP re-dial of "${server}" connected but its safety-axis fold refused, so its tools were not mounted: ${outcome.foldError}`), { code: "mcp.server_unavailable" }), { phase: "mcp", sessionId: input.sessionId });
46
75
  const ungated = await commitMcpRosterChanges(input);
@@ -117,12 +117,15 @@ export interface PermissionRuleLanes {
117
117
  } | undefined;
118
118
  }
119
119
  /** The ORG lane: the org's verdict about one call, off the store view `read` supplies — the gate's `orgRules` face over the
120
- * call's pinned read ({@link createPermissionRuleLanes}), and {@link orgVerdictBeforeClaim} over a view of its own. */
120
+ * call's pinned read ({@link createPermissionRuleLanes}), and {@link orgVerdictBeforeClaim} over a view of its own.
121
+ * `onDegraded` receives the snapshot's degraded line ({@link describeUnreadableOrgRows}) on every adjudication over a
122
+ * snapshot carrying a row this layer cannot read. */
121
123
  export declare function orgRuleLane(cfg: {
122
124
  read: (toolCallId: string) => Promise<Pick<EffectivePermissionRules, "org">>;
123
125
  questionToolMounted: boolean;
124
126
  questionToolName: string;
125
127
  onRevisionDefect: (message: string) => void;
128
+ onDegraded: (message: string) => void;
126
129
  }): NonNullable<PermissionRuleLanes["org"]>;
127
130
  /** The org's CURRENT verdict about a parked call, read by the resume entry before the claim (the row's session and principal,
128
131
  * bounded like the gate's wait; unreadable ⇒ unavailable). The mount and a revision defect are the resumed leg's facts, so
@@ -172,7 +175,7 @@ export declare function createPermissionRuleLanes(cfg: {
172
175
  questionToolMounted: boolean;
173
176
  questionToolName: string;
174
177
  onRevisionDefect: (message: string) => void;
175
- /** Sink for a judgment against the rows that threw. */
178
+ /** Sink for a judgment against the rows that threw, and for the org lane's degraded-snapshot line. */
176
179
  onDisclosure: (message: string) => void;
177
180
  /** The run's one reader of the person's rules: the call's ONE read, pinned, that the read station, the org lane and the
178
181
  * personal lane all take (the lanes consume a reader; they do not make one). */
@@ -1,10 +1,10 @@
1
1
  import { persistedRuleMandateOf, ruleClearanceOf } from "../ask-origin.js";
2
2
  import {} from "../hooks.js";
3
- import { adjudicatePersistedDomainRules, adjudicatePersistedNameRules, adjudicatePersistedPathRules, adjudicatePersistedRules, callPathFaceOf, domainRuleSubjectOf, faceRowsSpeakFor, laneToolNamesOf, nameRuleSubjectOf, ruleToolGrammarOf, segmentCoverageOf, suggestRuleForName, suggestRulesForCommand } from "../permission-rule-model.js";
3
+ import { adjudicatePersistedDomainRules, adjudicatePersistedNameRules, adjudicatePersistedPathRules, adjudicatePersistedRules, callPathFaceOf, domainRuleSubjectOf, escapeForDisclosure, faceRowsSpeakFor, laneToolNamesOf, nameRuleSubjectOf, ruleToolGrammarOf, segmentCoverageOf, suggestRuleForName, suggestRulesForCommand } from "../permission-rule-model.js";
4
4
  import { callTargetViewsOf, validatePath } from "../effective-path-target.js";
5
5
  import { bindPathRules, persistedRowAnchors, projectScopeIdentity } from "../path-rule-binding.js";
6
6
  import { heldBy, seatOf, wireOf } from "../tool-name-readings.js";
7
- import { orgRuleVerdictFor, settleOrgVerdictWithin } from "../permission-rule-org.js";
7
+ import { describeUnreadableOrgRows, readOrgRules, settleOrgVerdictWithin } from "../permission-rule-org.js";
8
8
  import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
9
9
  import { ownerStoreOf } from "../permission-rule-provider.js";
10
10
  import { thrownText } from "../thrown-value.js";
@@ -18,12 +18,19 @@ export function inheritedAskRuleEvidence(provider) {
18
18
  return Object.freeze({ orgRevisionAbsent: org, orgRuleAbsent: org, personalRuleDotsAbsent: personal });
19
19
  }
20
20
  export function orgRevisionEvidenceOf(resolution, onDefect) {
21
- const reported = resolution.revision;
21
+ let reported;
22
+ try {
23
+ reported = resolution.revision;
24
+ }
25
+ catch (err) {
26
+ onDefect(`the org rule partition's revision could not be read (${thrownText(err)}) — the adjudication stands, but the ask carries no revision evidence for this call`);
27
+ return {};
28
+ }
22
29
  if (reported === undefined)
23
30
  return {};
24
31
  if (typeof reported === "number" && Number.isFinite(reported))
25
32
  return { revision: reported };
26
- onDefect(`the org rule partition reported revision ${JSON.stringify(reported)} — a snapshot revision is a finite number; ` +
33
+ onDefect(`the org rule partition reported revision ${typeof reported === "string" ? `"${escapeForDisclosure(reported)}"` : escapeForDisclosure(reported)} — a snapshot revision is a finite number; ` +
27
34
  `the adjudication stands, but the ask carries no revision evidence for this call`);
28
35
  return {};
29
36
  }
@@ -59,11 +66,16 @@ export function orgRuleLane(cfg) {
59
66
  if (resolution.status === "unavailable" || resolution.status === "ungoverned") {
60
67
  return { status: "unavailable", disclosures: resolution.status === "ungoverned" ? ["the store reports no org partition while the provider declares one — unreadable governance, never an empty one"] : resolution.disclosures };
61
68
  }
62
- const revisionCell = orgRevisionEvidenceOf(resolution, cfg.onRevisionDefect);
69
+ const reading = readOrgRules(resolution.rules);
70
+ if ("refused" in reading)
71
+ return { status: "unavailable", disclosures: [`the store's org resolution cannot be read (${reading.refused}) — unreadable governance, never an empty one`] };
63
72
  const command = req.args?.command;
64
- if (!laneToolNamesOf(req).includes(PERSISTED_RULE_TOOL) || typeof command !== "string")
65
- return { status: "available", ...revisionCell };
66
- const verdict = orgRuleVerdictFor(resolution.rules, { tool: PERSISTED_RULE_TOOL, command });
73
+ const face = callPathFaceOf(req);
74
+ const verdict = reading.verdictFor({ tool: laneToolNamesOf(req), ...(typeof command === "string" ? { command } : {}), ...(face !== undefined ? { face } : {}) });
75
+ const revisionCell = orgRevisionEvidenceOf(resolution, cfg.onRevisionDefect);
76
+ const degraded = describeUnreadableOrgRows(reading.unreadable, revisionCell.revision);
77
+ if (degraded !== undefined)
78
+ cfg.onDegraded(degraded);
67
79
  return verdict === undefined ? { status: "available", ...revisionCell } : { status: "available", verdict, ...revisionCell };
68
80
  },
69
81
  };
@@ -72,7 +84,7 @@ export function orgVerdictBeforeClaim(cfg, req) {
72
84
  const unreadable = { status: "unavailable", disclosures: [] };
73
85
  const read = () => {
74
86
  const store = ownerStoreOf(cfg.provider, cfg.principal, cfg.localOwnerDeclared);
75
- return orgRuleLane({ read: () => store.effective({ sessionId: cfg.sessionId }), questionToolMounted: true, questionToolName: ASK_USER_QUESTION_TOOL_NAME, onRevisionDefect: () => undefined }).adjudicate(req);
87
+ return orgRuleLane({ read: () => store.effective({ sessionId: cfg.sessionId }), questionToolMounted: true, questionToolName: ASK_USER_QUESTION_TOOL_NAME, onRevisionDefect: () => undefined, onDegraded: () => undefined }).adjudicate(req);
76
88
  };
77
89
  return settleOrgVerdictWithin(Promise.resolve().then(read).catch(() => unreadable), unreadable, { timeoutMs: ORG_ADJUDICATION_TIMEOUT_MS, ...(cfg.signal !== undefined ? { signal: cfg.signal } : {}) });
78
90
  }
@@ -101,7 +113,7 @@ export function createPermissionRuleLanes(cfg) {
101
113
  throw new Error(read.unreadable ?? "the store did not answer");
102
114
  return { org: read.org };
103
115
  };
104
- const org = provider.partitions.org ? orgRuleLane({ read: orgViewOf, questionToolMounted: cfg.questionToolMounted, questionToolName: cfg.questionToolName, onRevisionDefect: cfg.onRevisionDefect }) : undefined;
116
+ const org = provider.partitions.org ? orgRuleLane({ read: orgViewOf, questionToolMounted: cfg.questionToolMounted, questionToolName: cfg.questionToolName, onRevisionDefect: cfg.onRevisionDefect, onDegraded: cfg.onDisclosure }) : undefined;
105
117
  const personalAdjudicate = async (req) => {
106
118
  if (anonymous && !cfg.localOwnerDeclared)
107
119
  return undefined;
@@ -237,7 +237,9 @@ export declare function ruleStoreUnreadableNotice(onNotice: RunnerDeps["onNotice
237
237
  /**
238
238
  * The run's ONE `permission.rule_row_dropped` notice (audience user): the person's store served project-scoped rows whose
239
239
  * scope root is not a complete absolute path, left out of the checks; minted by whichever read met them first, once per
240
- * run. The sentence names no path; the rows (bounded) ride the detail. The trace keeps every read's line.
240
+ * run. The sentence names no path; the rows ride the detail: `rule` is the rule text as the store served it, through the one
241
+ * reading ({@link noticeRuleText}) that `permission.allowed_tool_not_granted`'s `entries[].entry` uses too — the machine-read side carries the
242
+ * original, the sentence spells — and `scopeRoot` neutralized and bounded. The trace keeps every read's line.
241
243
  */
242
244
  export declare function ruleRowsDroppedNotice(onNotice: RunnerDeps["onNotice"], ids: {
243
245
  readonly sessionId: string;
@@ -1,6 +1,6 @@
1
1
  import { ownerStoreOf } from "../permission-rule-provider.js";
2
2
  import { ORG_ADJUDICATION_TIMEOUT_MS } from "../permission-rule-org.js";
3
- import { PATH_RULE_FACES, READ_RULE_TOOL, eligiblePersisted } from "../permission-rule-model.js";
3
+ import { PATH_RULE_FACES, READ_RULE_TOOL, eligiblePersisted, noticeRuleText } from "../permission-rule-model.js";
4
4
  import { bindPathRules, persistedRowAnchors, projectScopeIdentity } from "../path-rule-binding.js";
5
5
  import { DEFAULT_GATE_AWAIT_TIMEOUT_MS, gateWaitFailureOf, withGateTimeout } from "../gate-bounded-await.js";
6
6
  import { thrownText } from "../thrown-value.js";
@@ -382,7 +382,7 @@ export function ruleRowsDroppedNotice(onNotice, ids) {
382
382
  deliverEngineNotice(onNotice, {
383
383
  code: "permission.rule_row_dropped",
384
384
  message: `${rows.length} of your persisted permission rule rows ${one ? "is" : "are"} scoped to a project directory that is not written as a complete absolute path, so ${one ? "it takes" : "they take"} no part in this run's checks — a deny or ask row among them protects nothing. Rewrite ${one ? "it" : "them"} with the project's full path, or remove ${one ? "it" : "them"} (this notice's detail names ${one ? "it" : "them"}).`,
385
- detail: { rows: rows.slice(0, 8).map((r) => ({ rule: inlineUntrusted(r.rule, 200), scopeRoot: inlineUntrusted(r.scopeRoot, 200) })), count: rows.length, sessionId: ids.sessionId, runId: ids.runId },
385
+ detail: { rows: rows.slice(0, 8).map((r) => ({ rule: noticeRuleText(r.rule), scopeRoot: inlineUntrusted(r.scopeRoot, 200) })), count: rows.length, sessionId: ids.sessionId, runId: ids.runId },
386
386
  });
387
387
  };
388
388
  }
@@ -23,7 +23,8 @@ import { type BrainCallGuardrailRef } from "../../brain/timeout.js";
23
23
  import type { Model } from "../../internal/llm.js";
24
24
  import type { ThinkingLevel } from "../../internal/harness.js";
25
25
  import { type MicroCompactMachine } from "../context-edit.js";
26
- import { type LockedPreflight } from "../locked-config.js";
26
+ import { recordedModeSeed } from "../checkpoint-store.js";
27
+ import { type ConfigDoorEntry, type LockedPreflight } from "../locked-config.js";
27
28
  import { type ResolvedRole } from "../roles.js";
28
29
  import { type ResolvedSkillListingBudget } from "../skill-listing-budget.js";
29
30
  import type { SessionStore } from "../session.js";
@@ -146,7 +147,7 @@ export interface PrepareConfigDoorsResult {
146
147
  promptProfile: "simple" | "classic";
147
148
  /** owned — the host selector values (`TaskSpec.promptRole` / `TaskSpec.promptForm`, validated) the assembly selects on. */
148
149
  promptHost: ReturnType<typeof resolveHost>;
149
- /** owned — the center binding (`TaskSpec.promptBinding`, validated) the center layer's scoped overlays select on. */
150
+ /** owned — the run's binding (`TaskSpec.promptBinding`, validated): the selector value every overlay's `when.binding` reads. */
150
151
  promptBinding: string | undefined;
151
152
  /** owned — the administrator lock snapshot; the rest of prepare reads guarded slots through it. */
152
153
  lockedPreflight: LockedPreflight;
@@ -230,4 +231,49 @@ export declare function resolveMicroCompactKnob(bag: RunnerDeps["microCompact"])
230
231
  machine: MicroCompactMachine;
231
232
  clearOnRejection: boolean;
232
233
  };
234
+ /** The seat a checkpoint-store resolution is judged at: the store the task's own config names, else the deployment's. */
235
+ export declare const checkpointStoreSeat: (spec: {
236
+ checkpointStore?: unknown;
237
+ }) => "taskConfig" | "deps";
238
+ /** The first door of a resume: its re-supplied config is a record. A `null`, a primitive or an array where the record is declared is refused by name
239
+ * (`config.invalid_argument`), through the same channel as every door after it, before any of its members is read. */
240
+ export declare function assertResumeConfigRecord(config: unknown): void;
241
+ /** The spec the value doors read: a prepare's (its roster is faces) or a resume's re-supplied config (no objective, no session id). */
242
+ type ValueDoorSpec = Omit<TaskSpec, "tools" | "objective" | "sessionId" | "userAttachments"> & Partial<Pick<TaskSpec, "objective" | "sessionId">>;
243
+ type ConfigValueDoorsResult = Pick<PrepareConfigDoorsResult, "toolFaceLists" | "promptHost" | "promptBinding" | "lockedPreflight" | "microCompactKnob" | "memoryDelegationEvidence" | "memoryProvenance" | "memoryCapturePolicy" | "usageWindows" | "brainCallGuardrailMs"> & {
244
+ skillListingSeats: {
245
+ fraction: unknown;
246
+ maxDescChars: unknown;
247
+ };
248
+ skills: TaskSpec["skills"];
249
+ };
250
+ /**
251
+ * The phase's VALUE doors as ONE function with two callers — each judges a declared value against its own vocabulary, reading only the
252
+ * spec, the deployment's deps, the session store the Runner was constructed over and whether the leg resumes: {@link prepareConfigDoors}
253
+ * enters them as they stand, and a resume's screen before the claim (`refuseResumeDoors`) enters them through the resume's refusal
254
+ * channel, so a resume prepare would refuse here is refused before it consumes the row. `enter(seat, door)` names the input that carries
255
+ * each value; what a door resolves is returned. Every door is total over what a plain-JSON caller can hand it: a `null`, a primitive or an
256
+ * array where a record is declared is refused by its own code, never read through.
257
+ */
258
+ export declare function configValueDoors(input: {
259
+ spec: ValueDoorSpec;
260
+ deps: RunnerDeps;
261
+ resumeLeg: boolean;
262
+ sessions: SessionStore;
263
+ }, enter?: ConfigDoorEntry): ConfigValueDoorsResult;
264
+ /**
265
+ * The doors a RESUMED leg's prepare refuses at, run as one screen BEFORE the claim — prepare's own functions, so a refusal carries
266
+ * the very sentence and code prepare throws: the mode doors (`refusePermissionModeDoors`), the output-style seat, the approval
267
+ * reach, the skills invoked by name (the part no roster changes), the attachments (a resume sends no new human message, so any
268
+ * entry is refused), this phase's value doors over the config, the deployment's deps and the session store the Runner holds
269
+ * ({@link configValueDoors}, the retention capability door included) and the mode words the row recorded (`recordedModeSeed`).
270
+ * It leaves by the resume's ONE refusal channel (`asResumeConfigRefusal`:
271
+ * `checkpoint.config_refused`, `detail.code` = prepare's code, `detail.field` = `"taskConfig"`, `"deps"` or `"inheritedGate"`)
272
+ * where prepare would end the leg `failed` after the claim — the row consumed on a store without `reopen`, replayed as a system
273
+ * retry on one with it, which a configuration fix cannot reach. Before the claim the row stays `pending` and byte-identical and the
274
+ * corrected resume redeems the same token. A throw that is none of prepare's codes leaves as it was thrown. Returns the config the
275
+ * leg is handed: the caller's, its MCP servers and A2A peers the frozen entries the lock layer judged (read once, dialed as judged).
276
+ */
277
+ export declare function refuseResumeDoors<C extends ValueDoorSpec & Pick<TaskSpec, "userAttachments">>(config: C, recordedGate: Parameters<typeof recordedModeSeed>[0], deps: RunnerDeps, sessions: SessionStore): C;
233
278
  export declare function prepareConfigDoors(input: PrepareConfigDoorsInput): PrepareConfigDoorsResult;
279
+ export {};