@sema-agent/core 7.56.0 → 7.57.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 (173) hide show
  1. package/CHANGELOG.md +541 -0
  2. package/LICENSE +1 -1
  3. package/dist/agents/org-delivery-gate.js +3 -4
  4. package/dist/agents/peer-held-queue.d.ts +69 -49
  5. package/dist/agents/peer-held-queue.js +255 -106
  6. package/dist/agents/peer-session-drain.js +63 -43
  7. package/dist/agents/send-message-tool.js +3 -2
  8. package/dist/agents/subagent.js +8 -8
  9. package/dist/brain/anthropic.js +16 -8
  10. package/dist/brain/open-responses.js +10 -8
  11. package/dist/brain/openai.js +11 -9
  12. package/dist/brain/provider-dialect.d.ts +3 -2
  13. package/dist/brain/provider-dialect.js +2 -2
  14. package/dist/brain/request-params.d.ts +6 -0
  15. package/dist/brain/request-params.js +14 -1
  16. package/dist/brain/stream-engine.d.ts +9 -0
  17. package/dist/brain/stream-engine.js +24 -10
  18. package/dist/brain/stream-shared.d.ts +5 -4
  19. package/dist/brain/stream-shared.js +4 -4
  20. package/dist/core/allow-entry.d.ts +3 -10
  21. package/dist/core/allow-entry.js +17 -25
  22. package/dist/core/approval-object.d.ts +21 -5
  23. package/dist/core/approval-object.js +44 -17
  24. package/dist/core/ask-origin.d.ts +2 -2
  25. package/dist/core/auto-compaction.d.ts +71 -6
  26. package/dist/core/auto-compaction.js +42 -26
  27. package/dist/core/background-agent-store.d.ts +6 -2
  28. package/dist/core/background-agent-store.js +1 -1
  29. package/dist/core/checkpoint-requirements.d.ts +7 -1
  30. package/dist/core/checkpoint-requirements.js +2 -0
  31. package/dist/core/checkpoint-store.d.ts +17 -29
  32. package/dist/core/checkpoint-store.js +19 -11
  33. package/dist/core/cross-boundary-taint.js +4 -2
  34. package/dist/core/engine-notice.d.ts +3 -2
  35. package/dist/core/engine-notice.js +6 -0
  36. package/dist/core/file-history-store.d.ts +14 -8
  37. package/dist/core/file-history-store.js +14 -4
  38. package/dist/core/gate-lanes.js +2 -3
  39. package/dist/core/hooks.d.ts +2 -2
  40. package/dist/core/mailbox-store.d.ts +62 -1
  41. package/dist/core/mailbox-store.js +59 -0
  42. package/dist/core/mcp-name-judge.d.ts +46 -0
  43. package/dist/core/mcp-name-judge.js +61 -0
  44. package/dist/core/mcp-probe.d.ts +1 -1
  45. package/dist/core/mcp-probe.js +2 -5
  46. package/dist/core/mcp.js +10 -30
  47. package/dist/core/memory-engine/consolidation-driver.js +4 -3
  48. package/dist/core/memory-engine/distiller.js +26 -17
  49. package/dist/core/memory-engine/layout.js +5 -2
  50. package/dist/core/park-selfcheck.js +2 -0
  51. package/dist/core/permission-rule-consent.d.ts +5 -3
  52. package/dist/core/permission-rule-consent.js +20 -15
  53. package/dist/core/permission-rule-model.d.ts +43 -2
  54. package/dist/core/permission-rule-model.js +52 -8
  55. package/dist/core/permission-rule-org.js +1 -1
  56. package/dist/core/protocol-http-auth.d.ts +16 -2
  57. package/dist/core/protocol-http-auth.js +42 -11
  58. package/dist/core/protocol-naming.d.ts +20 -0
  59. package/dist/core/protocol-naming.js +20 -3
  60. package/dist/core/read-only-shell.d.ts +20 -5
  61. package/dist/core/read-only-shell.js +29 -14
  62. package/dist/core/remote-env.d.ts +37 -1
  63. package/dist/core/remote-env.js +11 -0
  64. package/dist/core/resume-startup-cause.d.ts +24 -2
  65. package/dist/core/resume-startup-cause.js +20 -1
  66. package/dist/core/roles.js +50 -7
  67. package/dist/core/runner/compact-session.d.ts +10 -0
  68. package/dist/core/runner/compact-session.js +202 -0
  69. package/dist/core/runner/compaction-call-options.d.ts +2 -2
  70. package/dist/core/runner/compaction-seams.d.ts +21 -1
  71. package/dist/core/runner/compaction-seams.js +11 -8
  72. package/dist/core/runner/contracts.d.ts +23 -16
  73. package/dist/core/runner/derived-route-fallback.d.ts +13 -0
  74. package/dist/core/runner/derived-route-fallback.js +6 -0
  75. package/dist/core/runner/env-capability-census.d.ts +3 -2
  76. package/dist/core/runner/env-capability-census.js +9 -3
  77. package/dist/core/runner/permission-rule-lanes.d.ts +7 -7
  78. package/dist/core/runner/permission-rule-lanes.js +17 -11
  79. package/dist/core/runner/prepare-acquire-reconcile.d.ts +3 -0
  80. package/dist/core/runner/prepare-acquire-reconcile.js +2 -1
  81. package/dist/core/runner/prepare-config-doors.d.ts +6 -0
  82. package/dist/core/runner/prepare-config-doors.js +4 -3
  83. package/dist/core/runner/prepare-hands-readface.d.ts +3 -1
  84. package/dist/core/runner/prepare-hands-readface.js +2 -0
  85. package/dist/core/runner/prepare-park-ask.d.ts +2 -0
  86. package/dist/core/runner/prepare-park-ask.js +2 -2
  87. package/dist/core/runner/prepare-policy-chain.js +2 -4
  88. package/dist/core/runner/prepare-task.d.ts +4 -1
  89. package/dist/core/runner/prepare-task.js +23 -27
  90. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +13 -0
  91. package/dist/core/runner/prepare-tool-disclosure-mount.js +13 -7
  92. package/dist/core/runner/rewind-files.d.ts +2 -5
  93. package/dist/core/runner/rewind-files.js +9 -51
  94. package/dist/core/runner/run-leg.js +10 -21
  95. package/dist/core/runner/run-notification-lane.d.ts +2 -0
  96. package/dist/core/runner/run-notification-lane.js +2 -2
  97. package/dist/core/runner/run-recovery-lanes.js +3 -4
  98. package/dist/core/runner/run-settle-and-teardown.js +5 -11
  99. package/dist/core/runner/run-terminal-adoption.js +3 -4
  100. package/dist/core/runner/run-turn-boundary.js +3 -5
  101. package/dist/core/runner/runtask.d.ts +8 -1
  102. package/dist/core/runner/runtask.js +28 -34
  103. package/dist/core/runner/send-message-continuation.js +1 -1
  104. package/dist/core/runner/session-verb-doors.d.ts +28 -0
  105. package/dist/core/runner/session-verb-doors.js +48 -0
  106. package/dist/core/runner/session-write-ledger.d.ts +30 -0
  107. package/dist/core/runner/session-write-ledger.js +101 -0
  108. package/dist/core/runner/stream-settle-backstop.js +11 -14
  109. package/dist/core/runner/sub-agent-deny-lists.d.ts +25 -3
  110. package/dist/core/runner/sub-agent-deny-lists.js +21 -10
  111. package/dist/core/runner/teardown-bounded.d.ts +1 -0
  112. package/dist/core/runner/teardown-bounded.js +1 -0
  113. package/dist/core/runner-deps.d.ts +2 -2
  114. package/dist/core/secret-env.d.ts +4 -0
  115. package/dist/core/secret-env.js +3 -0
  116. package/dist/core/session-policy-store.d.ts +7 -2
  117. package/dist/core/session-policy-store.js +1 -1
  118. package/dist/core/shell-wrapper-projection.d.ts +1 -1
  119. package/dist/core/store-contracts/background-agent-store-contract.js +1 -0
  120. package/dist/core/store-contracts/checkpoint-store-contract.js +2 -1
  121. package/dist/core/store-contracts/file-history-store-contract.js +9 -0
  122. package/dist/core/store-contracts/mailbox-store-contract.js +49 -1
  123. package/dist/core/task-registry-agent.d.ts +8 -4
  124. package/dist/core/task-registry-agent.js +14 -8
  125. package/dist/core/task-registry-shared.d.ts +35 -6
  126. package/dist/core/task-registry-shared.js +11 -0
  127. package/dist/core/task-registry.d.ts +1 -1
  128. package/dist/core/task-registry.js +1 -1
  129. package/dist/core/tool-catalog-entries.d.ts +6 -0
  130. package/dist/core/tool-catalog-entries.js +1 -0
  131. package/dist/core/tool-name-aliases.d.ts +32 -2
  132. package/dist/core/tool-name-aliases.js +23 -0
  133. package/dist/core/tool-name-readings.d.ts +4 -0
  134. package/dist/core/tool-name-readings.js +1 -0
  135. package/dist/core/tool-policy.d.ts +10 -6
  136. package/dist/core/tool-policy.js +4 -7
  137. package/dist/core/trace.d.ts +4 -1
  138. package/dist/core/trace.js +2 -1
  139. package/dist/core/untrusted-egress.d.ts +20 -0
  140. package/dist/core/untrusted-egress.js +77 -3
  141. package/dist/engine/harness/agent-harness.d.ts +7 -5
  142. package/dist/engine/harness/agent-harness.js +11 -2
  143. package/dist/engine/loop/agent-loop.d.ts +1 -4
  144. package/dist/engine/loop/agent-loop.js +26 -4
  145. package/dist/index.d.ts +12 -7
  146. package/dist/index.js +7 -4
  147. package/dist/internal/harness.d.ts +2 -1
  148. package/dist/internal/harness.js +1 -0
  149. package/dist/primitives/credential-ref.d.ts +89 -0
  150. package/dist/primitives/credential-ref.js +125 -0
  151. package/dist/primitives/index.d.ts +2 -0
  152. package/dist/primitives/index.js +1 -0
  153. package/dist/stores/file/checkpoint-store.d.ts +0 -4
  154. package/dist/stores/file/checkpoint-store.js +0 -1
  155. package/dist/stores/file/file-history-store.js +12 -4
  156. package/dist/stores/file/mailbox-store.d.ts +2 -1
  157. package/dist/stores/file/mailbox-store.js +26 -1
  158. package/dist/tools/fs/bash-readonly-classifier.d.ts +15 -19
  159. package/dist/tools/fs/bash-readonly-classifier.js +5 -10
  160. package/dist/tools/fs/fs-bash.js +10 -3
  161. package/dist/tools/fs/pattern-kill-guard.d.ts +3 -2
  162. package/dist/tools/fs/pattern-kill-guard.js +68 -36
  163. package/dist/tools/fs/shell-read-check.d.ts +10 -0
  164. package/dist/tools/fs/shell-read-check.js +7 -1
  165. package/dist/tools/fs/shell-read-identity.d.ts +12 -9
  166. package/dist/tools/fs/shell-read-identity.js +9 -4
  167. package/dist/tools/web.js +9 -7
  168. package/package.json +1 -1
  169. package/test/export-surface.snapshot.json +79 -3
  170. package/wire-changes/core-stores-sql@7.57.0.json +29 -0
  171. package/wire-changes/core@7.57.0.json +1487 -0
  172. package/wire-changes/mods-kernel@7.57.0.json +29 -0
  173. package/wire-changes/primitives@7.57.0.json +245 -0
@@ -1,4 +1,4 @@
1
- import { mailboxCrossProcessMountVerdict } from "../core/mailbox-store.js";
1
+ import { isMailboxDecisionFinal, mailboxCrossProcessMountVerdict, moveMailboxDecision } from "../core/mailbox-store.js";
2
2
  import { decodeEngineFrame, ENGINE_FRAME_CODEC_VERSION, renderTaskNotificationXml, taskNotificationDedupKey } from "../core/task-notification.js";
3
3
  import { createFrameSettlement, toolResultReceiptKey } from "../core/frame-settlement.js";
4
4
  import { PRINCIPAL_INBOX_HANDLE } from "./cross-session-ref.js";
@@ -161,7 +161,7 @@ export function createPeerSessionDrain(opts) {
161
161
  ...(live
162
162
  ? {
163
163
  inject: (entry) => {
164
- if (currentRound === undefined)
164
+ if (currentRound === undefined || !currentRound.holds(lane))
165
165
  return "deferred";
166
166
  const payload = buildPeerMessagePayload(lane, entry);
167
167
  if (currentRound.deliver(payload, "next") === "deferred")
@@ -169,9 +169,13 @@ export function createPeerSessionDrain(opts) {
169
169
  noteDelivered(lane, entry.seq, 1, payload, currentRound.carrier);
170
170
  return undefined;
171
171
  },
172
+ canInject: () => currentRound !== undefined && currentRound.holds(lane) && !currentRound.spent,
172
173
  }
173
174
  : {}),
174
175
  receipt: receipt(lane),
176
+ tellsSender: (entry) => receiptAddress(lane, entry) !== undefined,
177
+ decide: (entry, expect, next) => moveMailboxDecision(mailbox, scope, lane.boxHandle, entry.seq, expect, next),
178
+ onFault: (entry, e) => opts.onError?.(e, { phase: "degraded", sessionId, classification: `peer-decision-move:${entry.box}#${entry.seq}` }),
175
179
  onHeld: (entry, count, cause, reannounce) => {
176
180
  if (!reannounce && lane.disclosedHeld.has(entry.seq))
177
181
  return;
@@ -179,12 +183,13 @@ export function createPeerSessionDrain(opts) {
179
183
  disclose(lane, "held", cause, entry, reannounce ? `still held; its cause moved to ${cause} (${count} held)` : undefined);
180
184
  },
181
185
  onSettled: (entry, settlement, reason, count) => {
182
- if (entry.box === lane.boxHandle && settlement !== "delivered")
186
+ if (entry.box === lane.boxHandle && settlement !== "delivered" && settlement !== "retired")
183
187
  lane.ledger.set(entry.seq, { kind: "terminal", why: `held_${settlement}` });
184
188
  const replyTo = receiptAddress(lane, entry);
189
+ const told = reason !== "decided_elsewhere" && settlement !== "retired" && replyTo !== undefined;
185
190
  deliverEngineNotice(opts.onNotice, {
186
191
  code: "peer.held_settled",
187
- message: `held cross-session message seq ${entry.seq} left this session's held queue: ${settlement} (${reason}); ${count} still held${settlement === "expired" && reason === "evicted" ? " — the held queue was full, so the oldest entry was expired to make room" : ""}. The sender ${replyTo !== undefined ? "was sent a receipt" : "has no reply address and was not told"}.`,
192
+ message: `held cross-session message seq ${entry.seq} left this session's held queue: ${settlement} (${reason}); ${count} still held${settlement === "expired" && reason === "evicted" ? " — the held queue was full, so the oldest entry was expired to make room" : ""}${settlement === "retired" ? (reason === "superseded" ? " — its record is no longer in the box (another consumer settled it); nothing was decided here" : " — no decision was taken: it stays held in the box, and the next drain presents it again") : ""}${reason === "decided_elsewhere" ? " — another consumer of the box decided it first" : ""}. The sender ${told ? "was sent a receipt" : replyTo !== undefined ? "was not sent a receipt from here" : "has no reply address and was not told"}.`,
188
193
  detail: { settlement, reason, seq: entry.seq, box: lane.boxHandle, ...(entry.peerMeta?.fromSession !== undefined ? { fromSession: entry.peerMeta.fromSession } : {}), heldCount: count, sessionId, runId },
189
194
  });
190
195
  },
@@ -216,28 +221,29 @@ export function createPeerSessionDrain(opts) {
216
221
  idleMachine.requester.configure({
217
222
  expired: (o) => void routePeerIdleNotice(route(), sessionId, { kind: "expired", label: o.label }),
218
223
  });
219
- const adoptHeldDecisions = (lane) => {
220
- const decided = lane.held.decisions();
221
- if (decided.size === 0)
222
- return;
223
- for (const [seq, settlement] of decided) {
224
- if (lane.ackedUpTo !== undefined && seq <= lane.ackedUpTo)
225
- continue;
226
- const mine = lane.ledger.get(seq);
227
- if (mine !== undefined && mine.kind !== "held")
228
- continue;
229
- lane.ledger.set(seq, { kind: "terminal", why: `held_${settlement}` });
224
+ const reconcileHeld = (lane, lease) => {
225
+ const live = lease.messages.filter((m) => lane.ackedUpTo === undefined || m.seq > lane.ackedUpTo);
226
+ lane.held.reconcile(new Map(live.map((m) => [m.seq, m.decision])));
227
+ adoptFinalDecisions(lane, lease);
228
+ for (const m of live) {
229
+ const d = lane.ledger.get(m.seq);
230
+ if (lane.held.has(m.seq)) {
231
+ if (d === undefined)
232
+ lane.ledger.set(m.seq, { kind: "held" });
233
+ }
234
+ else if (d?.kind === "held")
235
+ lane.ledger.delete(m.seq);
230
236
  }
231
237
  };
232
- const reconcileHeld = (lane, lease) => {
233
- lane.held.reconcile(new Set(lease.messages.map((m) => m.seq)));
234
- let queued;
235
- for (const [seq, d] of lane.ledger) {
236
- if (d.kind !== "held")
238
+ const adoptFinalDecisions = (lane, lease) => {
239
+ for (const m of lease.messages) {
240
+ if (lane.ackedUpTo !== undefined && m.seq <= lane.ackedUpTo)
237
241
  continue;
238
- queued ??= new Set(lane.held.list().filter((e) => e.box === lane.boxHandle).map((e) => e.seq));
239
- if (!queued.has(seq))
240
- lane.ledger.delete(seq);
242
+ if (!isMailboxDecisionFinal(m.decision))
243
+ continue;
244
+ const d = lane.ledger.get(m.seq);
245
+ if (d === undefined || d.kind === "held" || d.kind === "invalidated")
246
+ lane.ledger.set(m.seq, { kind: "terminal", why: `held_${m.decision}` });
241
247
  }
242
248
  };
243
249
  const wantsCompactionEffects = () => {
@@ -280,7 +286,6 @@ export function createPeerSessionDrain(opts) {
280
286
  };
281
287
  const settleLaneLocally = (lane, landed) => {
282
288
  const { ledger, boxHandle } = lane;
283
- adoptHeldDecisions(lane);
284
289
  const hostWithheldSeqs = [];
285
290
  if (seam.receipts.size > 0 || seam.withheld.size > 0 || seam.hostWithheld.size > 0) {
286
291
  for (const [seq, d] of ledger) {
@@ -326,26 +331,17 @@ export function createPeerSessionDrain(opts) {
326
331
  }
327
332
  };
328
333
  const ackSettledPrefix = async (lane, lease) => {
329
- const { held, ledger, boxHandle } = lane;
330
- const decided = held.decisions();
331
- if (decided.size > 0) {
332
- const live = new Set(lease.messages.map((m) => m.seq));
333
- held.forgetDecisions([...decided.keys()].filter((seq) => !live.has(seq)));
334
- }
334
+ const { ledger, boxHandle } = lane;
335
+ adoptFinalDecisions(lane, lease);
335
336
  const prefix = deriveAckPrefix(lane, lease.messages);
336
337
  if (prefix !== undefined && (lane.ackedUpTo === undefined || prefix > lane.ackedUpTo)) {
337
338
  try {
338
339
  const result = await mailbox.ack(scope, boxHandle, drainOwner, prefix);
339
340
  if (result.acked) {
340
341
  lane.ackedUpTo = prefix;
341
- const gone = [];
342
- for (const seq of [...ledger.keys()]) {
343
- if (seq > prefix)
344
- continue;
345
- gone.push(seq);
346
- ledger.delete(seq);
347
- }
348
- held.forgetDecisions(gone);
342
+ for (const seq of [...ledger.keys()])
343
+ if (seq <= prefix)
344
+ ledger.delete(seq);
349
345
  }
350
346
  else {
351
347
  opts.onError?.(new Error(`the ${lane.kind === "session" ? "session box" : "principal inbox"} ${boxHandle} refused this leg's ack up to seq ${prefix}: the drain lease is no longer held by ${drainOwner} (it expired and another cycle claimed it). The records stay parked and the lease holder re-serves them.`), { phase: "degraded", sessionId, classification: "peer-box-ack" });
@@ -451,6 +447,13 @@ export function createPeerSessionDrain(opts) {
451
447
  lane.ledger.set(seq, { kind: "terminal", why });
452
448
  }
453
449
  batch = [];
450
+ leasedLanes = new Set();
451
+ lease(lanes) {
452
+ this.leasedLanes = new Set(lanes);
453
+ }
454
+ holds(lane) {
455
+ return this.leasedLanes.has(lane);
456
+ }
454
457
  chars = 0;
455
458
  constructor(limit, out, carrier, charBudget) {
456
459
  this.limit = limit;
@@ -527,15 +530,17 @@ export function createPeerSessionDrain(opts) {
527
530
  const leased = claims.filter((c) => c.lease !== null);
528
531
  if (leased.length === 0)
529
532
  return { pending: 0, urgent: 0 };
533
+ round.lease(leased.map((c) => c.lane));
530
534
  for (const { lane, lease } of leased) {
531
535
  bindHeld(lane, true);
532
536
  reconcileHeld(lane, lease);
533
- lane.held.rejudge();
537
+ await lane.held.rejudge();
534
538
  }
535
539
  try {
536
540
  for (const { lane, lease } of leased) {
537
541
  const { ledger, disclosedHeld, held, boxHandle } = lane;
538
542
  const ackedUpTo = lane.ackedUpTo;
543
+ let adoptedThisPass = false;
539
544
  for (const m of lease.messages) {
540
545
  const prior = ledger.get(m.seq);
541
546
  if (prior === undefined || prior.kind !== "invalidated")
@@ -545,7 +550,6 @@ export function createPeerSessionDrain(opts) {
545
550
  const attempt = prior.attempt + 1;
546
551
  round.batch.push({ lane, payload: { ...prior.payload, attempt, resupplied: true }, priority: "next", urgent: false, seq: m.seq, attempt });
547
552
  }
548
- adoptHeldDecisions(lane);
549
553
  for (const m of lease.messages) {
550
554
  if (ackedUpTo !== undefined && m.seq <= ackedUpTo)
551
555
  continue;
@@ -597,15 +601,29 @@ export function createPeerSessionDrain(opts) {
597
601
  continue;
598
602
  }
599
603
  const sameTree = meta?.fromRoot !== undefined && meta.fromRoot.toLowerCase() === sessionId.toLowerCase();
604
+ const known = m.decision;
600
605
  if (!sameTree) {
601
606
  const judged = judgeMeta(facts, meta);
602
- if (judged.verdict === "refuse") {
607
+ const shown = known === "held" || known === "approved";
608
+ if (!shown && judged.verdict === "refuse") {
603
609
  disclose(lane, "refused", judged.cause, m);
604
610
  round.dispose(lane, m.seq, `refused:${judged.cause}`);
605
611
  continue;
606
612
  }
607
- if (judged.verdict === "hold") {
608
- held.hold({ seq: m.seq, box: boxHandle, content: m.content, ...(m.from !== undefined ? { from: m.from } : {}), sentAt: m.sentAt, ...(m.hopChain !== undefined ? { hopChain: [...m.hopChain] } : {}), ...(meta !== undefined ? { peerMeta: { ...meta } } : {}), cause: judged.cause });
613
+ if (shown || (judged.verdict === "hold" && known !== "delivered")) {
614
+ const outcome = await held.hold({ seq: m.seq, box: boxHandle, content: m.content, ...(m.from !== undefined ? { from: m.from } : {}), sentAt: m.sentAt, ...(m.hopChain !== undefined ? { hopChain: [...m.hopChain] } : {}), ...(meta !== undefined ? { peerMeta: { ...meta } } : {}), cause: judged.verdict === "hold" ? judged.cause : "explicit-setting" }, shown ? known : undefined);
615
+ if (outcome === "gone" || outcome === "fault")
616
+ continue;
617
+ if (outcome !== "held" && outcome !== "adopted" && outcome !== "uncarded") {
618
+ round.dispose(lane, m.seq, `held_${outcome}`);
619
+ continue;
620
+ }
621
+ if (shown && outcome === "adopted")
622
+ adoptedThisPass = true;
623
+ if (outcome === "uncarded" && !disclosedHeld.has(m.seq)) {
624
+ disclosedHeld.add(m.seq);
625
+ disclose(lane, "held", judged.verdict === "hold" ? judged.cause : "explicit-setting", m, "it is held in the box, but this session's held queue is full; it is shown when a card frees");
626
+ }
609
627
  if (!ledger.has(m.seq))
610
628
  ledger.set(m.seq, { kind: "held" });
611
629
  continue;
@@ -622,6 +640,8 @@ export function createPeerSessionDrain(opts) {
622
640
  }
623
641
  round.batch.push({ lane, payload, priority: "next", urgent: meta?.urgent === true, seq: m.seq, attempt: 1 });
624
642
  }
643
+ if (adoptedThisPass)
644
+ await held.rejudge();
625
645
  }
626
646
  }
627
647
  finally {
@@ -1,7 +1,7 @@
1
1
  import { Type } from "typebox";
2
2
  import { isNotificationInjectionOutcome } from "../core/task-notification.js";
3
3
  import { defineTool } from "../core/tools.js";
4
- import { normalizeAgentName, DURABLE_AGENT_HANDLE_RE, DURABLE_AGENT_HEARTBEAT_MS } from "../core/task-registry.js";
4
+ import { normalizeAgentName, chosenAgentName, DURABLE_AGENT_HANDLE_RE, DURABLE_AGENT_HEARTBEAT_MS } from "../core/task-registry.js";
5
5
  import { announceTranscriptIntegrityGapOnce, canAccessAgentRecord, clearRevivedRowTerminalPayload } from "../core/background-agent-store.js";
6
6
  import { uuidv7 } from "../internal/harness.js";
7
7
  import { MAILBOX_TOMBSTONED_RECIPIENT_CODE, mailboxCrossProcessMountVerdict } from "../core/mailbox-store.js";
@@ -1041,6 +1041,7 @@ export function createSendMessageTool(opts) {
1041
1041
  if (!l3Verdict.ok)
1042
1042
  return admissionRefusal(l3Verdict.reason, who);
1043
1043
  assertParentSpecSeatCurrent(ctx.parentSpecSeat);
1044
+ const chosenRowName = chosenAgentName(targetId, row.name);
1044
1045
  const resume = createSubagentResume({
1045
1046
  ledger,
1046
1047
  parentToolCallId: resumeToolUseId,
@@ -1055,7 +1056,7 @@ export function createSendMessageTool(opts) {
1055
1056
  ...((ctx.onBackgroundChildEvent ?? opts.onBackgroundChildEvent) ? { bgSink: ctx.onBackgroundChildEvent ?? opts.onBackgroundChildEvent } : {}),
1056
1057
  sessionScoped: row.sessionScoped === true,
1057
1058
  ...(row.description !== undefined ? { rowDescription: row.description } : {}),
1058
- ...(row.name !== undefined ? { rowName: row.name } : {}),
1059
+ ...(chosenRowName !== undefined ? { rowName: chosenRowName } : {}),
1059
1060
  ...(row.agentType !== undefined ? { rowAgentType: row.agentType } : {}),
1060
1061
  ...(row.owner !== undefined ? { rowOwner: row.owner } : {}),
1061
1062
  ...(row.scope !== undefined ? { rowScope: row.scope } : {}),
@@ -41,7 +41,7 @@ import { boundedRedactedSummary } from "../core/untrusted-egress.js";
41
41
  import { uuidv7 } from "../internal/harness.js";
42
42
  import { addWorktree } from "../core/git-worktree-env.js";
43
43
  import { shellQuote } from "../tools/fs/search.js";
44
- import { BG_AGENT_REAP_STOP_ERROR } from "../core/task-registry.js";
44
+ import { BG_AGENT_REAP_STOP_ERROR, chosenAgentName } from "../core/task-registry.js";
45
45
  import { readDurableOrgAdmission } from "../core/memory-admission.js";
46
46
  import { governanceRetryClass } from "../core/governance-codes.js";
47
47
  import { readSkillGrantCapture } from "../core/skill-grant.js";
@@ -1534,7 +1534,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
1534
1534
  if (a.name !== undefined && typeof a.name !== "string") {
1535
1535
  return notStartedResult({ isError: true, content: "Sub-agent not started: name must be a string.", details: { error: "name.invalid" } });
1536
1536
  }
1537
- const agentName = a.name;
1537
+ const agentName = reviveClaim !== undefined ? chosenAgentName(reviveClaim.row.handle, a.name) : a.name;
1538
1538
  if (agentName !== undefined) {
1539
1539
  if (!/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/.test(agentName)) {
1540
1540
  return notStartedResult({
@@ -1732,7 +1732,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
1732
1732
  content: errorCodeOf(e) === WORKTREE_NOT_A_REPOSITORY
1733
1733
  ? "Sub-agent not started: worktree isolation failed — the working directory is not a git repository. Retry without the isolation parameter."
1734
1734
  : "Sub-agent not started: worktree isolation failed. Retry without the isolation parameter, or fix the repository state.",
1735
- details: { error: "isolation.failed", cause: thrownText(e) },
1735
+ details: { error: "isolation.failed", cause: childThrownText(e) },
1736
1736
  });
1737
1737
  }
1738
1738
  }
@@ -1756,13 +1756,13 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
1756
1756
  }
1757
1757
  catch (e) {
1758
1758
  worktreeKeptPath = worktreeDir;
1759
- worktreeFault = thrownText(e);
1759
+ worktreeFault = childThrownText(e);
1760
1760
  return `worktree: ${worktreeDir} (cleanup check failed — the directory is kept on disk)`;
1761
1761
  }
1762
1762
  };
1763
1763
  const reportedWorktreeLine = (line) => (line !== undefined && worktreeFault !== undefined ? `${line}: ${worktreeFault}` : line);
1764
1764
  const faultDetails = (cause) => ({
1765
- ...(cause !== undefined ? { cause: thrownText(cause) } : {}),
1765
+ ...(cause !== undefined ? { cause: childThrownText(cause) } : {}),
1766
1766
  ...(worktreeFault !== undefined ? { worktreeCause: worktreeFault } : {}),
1767
1767
  });
1768
1768
  let injectedTools = [];
@@ -2499,7 +2499,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
2499
2499
  }
2500
2500
  }
2501
2501
  if (!settled) {
2502
- const detail = thrownText(lastErr);
2502
+ const detail = childThrownText(lastErr);
2503
2503
  try {
2504
2504
  enqueueMemoryAnnouncement(forkSettlementSeat.controlDir, {
2505
2505
  kind: "gate",
@@ -3007,7 +3007,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3007
3007
  }
3008
3008
  }
3009
3009
  if (!settled) {
3010
- const detail = thrownText(lastErr);
3010
+ const detail = childThrownText(lastErr);
3011
3011
  try {
3012
3012
  enqueueMemoryAnnouncement(settlementSeat.controlDir, {
3013
3013
  kind: "gate",
@@ -3030,7 +3030,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3030
3030
  ...(reviveRow.sessionScoped ? { sessionScoped: true } : {}),
3031
3031
  ...(reviveRow.description !== undefined ? { description: reviveRow.description } : {}),
3032
3032
  agentType: spawnAgentType,
3033
- ...(reviveRow.name !== undefined ? { name: reviveRow.name } : {}),
3033
+ name: reviveRow.name,
3034
3034
  ...(reviveRow.parentTaskId !== undefined ? { parentTaskId: reviveRow.parentTaskId } : {}),
3035
3035
  ...(reviveRow.parentSessionId !== undefined ? { parentSessionId: reviveRow.parentSessionId } : {}),
3036
3036
  ...((reviveRow.rootSessionId ?? reviveRow.parentSessionId) !== undefined
@@ -6,7 +6,7 @@ import { STREAM_TORN_WARNING, createRepetitionPoll, createStreamEndLedger, model
6
6
  import { admitToolCallId, providerResultIds, providerToolCallId, responseToolCallIds } from "./tool-call-id.js";
7
7
  import { emitBrainTelemetry, reportReasoningWireFacts } from "./status-sink.js";
8
8
  import { errorResultMediaNote, IMAGE_OMITTED_NO_VISION, imageWithholdOf, reportImagesWithheld, sendableImages } from "./media-degrade.js";
9
- import { ANTHROPIC_RESERVED, OUTPUT_CAP_KEYS, applyExtraBody, effectiveOutputCap, headerBagOnce, lockHeader, mergeHeaders, takeHeaderSpellings } from "./request-params.js";
9
+ import { ANTHROPIC_RESERVED, OUTPUT_CAP_KEYS, applyExtraBody, declaredRequestValues, effectiveOutputCap, headerBagOnce, headerListEntries, lockHeader, mergeHeaders, takeHeaderSpellings } from "./request-params.js";
10
10
  import { admitRouteRequest, applyRouteCredentialHeaders, assertServableRequestRoot, brainConfigSnapshot, createBrainRouteJudge, joinRequestUrl, resolveRequestRootOf, resolveRouteCredential } from "./route-adjudicator.js";
11
11
  import { requestRouteOf } from "./request-route.js";
12
12
  import { NAMES_AS_WRITTEN, resolveProviderDialect, endpointToolNames } from "./provider-dialect.js";
@@ -296,6 +296,8 @@ export function createAnthropicBrain(given = {}) {
296
296
  const headerLayers = [modelHeaders, config.headers, optionsHeaders];
297
297
  const headers = mergeHeaders(...headerLayers);
298
298
  applyRouteCredentialHeaders(headers, routeCredential);
299
+ const declaredBetas = headerLayers.flatMap((layer) => Object.entries(layer ?? {}).flatMap(([name, value]) => (name.toLowerCase() === "anthropic-beta" ? headerListEntries(value) : [])));
300
+ const declared = declaredRequestValues([...headerLayers, headers], [config.version, ...declaredBetas], apiKey);
299
301
  takeHeaderSpellings(headers, "anthropic-beta");
300
302
  for (const layer of headerLayers) {
301
303
  for (const [name, value] of Object.entries(layer ?? {}))
@@ -320,6 +322,7 @@ export function createAnthropicBrain(given = {}) {
320
322
  return {
321
323
  url,
322
324
  headers,
325
+ declared,
323
326
  body: JSON.stringify(wire),
324
327
  extraBodyDropped,
325
328
  route: requestRouteOf(routeCredential, rootOrigin),
@@ -353,7 +356,7 @@ export function createAnthropicBrain(given = {}) {
353
356
  let lastDegenCheckThinking = 0;
354
357
  const detectRep = config.detectRepetition !== false;
355
358
  let messageStopped = false;
356
- const afterEnd = createStreamEndLedger();
359
+ const afterEnd = createStreamEndLedger(ctrl.providerWords);
357
360
  const streamEnded = () => stopReason != null || messageStopped;
358
361
  const mergeDeltaUsage = (u) => {
359
362
  if (!u)
@@ -430,7 +433,7 @@ export function createAnthropicBrain(given = {}) {
430
433
  else if (acc.type === "tool_use") {
431
434
  const closed = acc.stopped === true || !endedCut;
432
435
  if (!acc.toolName) {
433
- (nameMissingCause(closed) === "truncated" ? malformed : unnamed).push(`id="${acc.toolId || "?"}"(${acc.toolJson.slice(0, 200)})`);
436
+ (nameMissingCause(closed) === "truncated" ? malformed : unnamed).push(`id="${ctrl.providerWords(acc.toolId || "?")}"(${ctrl.providerWords(acc.toolJson).slice(0, 200)})`);
434
437
  }
435
438
  else {
436
439
  const tc = closeToolUseBlock(acc, responseIds, closed);
@@ -439,7 +442,7 @@ export function createAnthropicBrain(given = {}) {
439
442
  finalContent.push(tc);
440
443
  }
441
444
  else {
442
- malformed.push(`${acc.toolName}(${acc.toolJson.slice(0, 200)})`);
445
+ malformed.push(`${ctrl.providerWords(acc.toolName)}(${ctrl.providerWords(acc.toolJson).slice(0, 200)})`);
443
446
  }
444
447
  }
445
448
  }
@@ -580,8 +583,13 @@ export function createAnthropicBrain(given = {}) {
580
583
  const d = evt.delta;
581
584
  if (d?.stop_reason)
582
585
  stopReason = d.stop_reason;
583
- if (d?.stop_details)
584
- stopDetails = d.stop_details;
586
+ if (d?.stop_details) {
587
+ const raw = d.stop_details;
588
+ stopDetails = {
589
+ ...(typeof raw.category === "string" ? { category: ctrl.providerWords(raw.category) } : {}),
590
+ ...(typeof raw.explanation === "string" ? { explanation: ctrl.providerWords(raw.explanation) } : {}),
591
+ };
592
+ }
585
593
  mergeDeltaUsage(evt.usage);
586
594
  break;
587
595
  }
@@ -596,7 +604,7 @@ export function createAnthropicBrain(given = {}) {
596
604
  const inputTooLong = e?.type === "request_too_large" ||
597
605
  (e?.type === "invalid_request_error" && typeof e.message === "string" && startsWithInputTooLongAnchor(e.message));
598
606
  ctrl.keepOutput({ content: assembleTurn({}).content, ...reportedUsage() });
599
- throw new BrainError(anthropicStreamErrorCode(e?.type), `anthropic stream error (${e?.type ?? "unknown"}): ${e?.message ?? ""}`, undefined, { apiError: true, ...(e === undefined ? {} : inputTooLong ? { inputTooLong: true } : { inputTooLongRuledOut: true }) });
607
+ throw new BrainError(anthropicStreamErrorCode(e?.type), `anthropic stream error ${ctrl.providerWords(`(${e?.type ?? "unknown"}): ${e?.message ?? ""}`)}`, undefined, { apiError: true, ...(e === undefined ? {} : inputTooLong ? { inputTooLong: true } : { inputTooLongRuledOut: true }) });
600
608
  }
601
609
  default:
602
610
  break;
@@ -636,7 +644,7 @@ export function createAnthropicBrain(given = {}) {
636
644
  }
637
645
  catch {
638
646
  malformedFrames++;
639
- malformedSample ??= data.slice(0, 160);
647
+ malformedSample ??= ctrl.providerWords(data).slice(0, 160);
640
648
  return;
641
649
  }
642
650
  handleEvent(evt);
@@ -5,7 +5,7 @@ import { STREAM_TORN_WARNING, createRepetitionPoll, modelOutputInvalidOf, noteRe
5
5
  import { admitToolCallId, providerResultIds, providerToolCallId, responseToolCallIds } from "./tool-call-id.js";
6
6
  import { emitBrainTelemetry } from "./status-sink.js";
7
7
  import { errorResultMediaNote, IMAGE_OMITTED_NO_VISION, imageWithholdOf, imagesOmittedNoVisionNote, reportImagesWithheld, sendableImages } from "./media-degrade.js";
8
- import { OUTPUT_CAP_KEYS, RESPONSES_RESERVED, applyExtraBody, effectiveOutputCap, headerBagOnce, lockHeader, mergeHeaders } from "./request-params.js";
8
+ import { OUTPUT_CAP_KEYS, RESPONSES_RESERVED, applyExtraBody, declaredRequestValues, effectiveOutputCap, headerBagOnce, lockHeader, mergeHeaders } from "./request-params.js";
9
9
  import { admitRouteRequest, applyRouteCredentialHeaders, assertServableRequestRoot, brainConfigSnapshot, createBrainRouteJudge, joinRequestUrl, resolveRequestRootOf, resolveRouteCredential } from "./route-adjudicator.js";
10
10
  import { requestRouteOf } from "./request-route.js";
11
11
  import { NAMES_AS_WRITTEN, resolveProviderDialect, endpointToolNames } from "./provider-dialect.js";
@@ -281,6 +281,7 @@ export function createOpenResponsesBrain(given = {}) {
281
281
  const { body: reasoningBody, effort } = applyReasoningToRequest({ api: "openai-responses", model, level: options?.reasoning, body });
282
282
  const headers = mergeHeaders(modelHeaders, config.headers, optionsHeaders);
283
283
  applyRouteCredentialHeaders(headers, routeCredential);
284
+ const declared = declaredRequestValues([modelHeaders, config.headers, optionsHeaders, headers], [], apiKey);
284
285
  lockHeader(headers, "content-type", "application/json");
285
286
  if (apiKey)
286
287
  lockHeader(headers, OPEN_RESPONSES_KEY_HEADER, `Bearer ${apiKey}`);
@@ -288,6 +289,7 @@ export function createOpenResponsesBrain(given = {}) {
288
289
  return {
289
290
  url,
290
291
  headers,
292
+ declared,
291
293
  body: JSON.stringify(wire),
292
294
  extraBodyDropped,
293
295
  route: requestRouteOf(routeCredential, rootOrigin),
@@ -563,10 +565,10 @@ export function createOpenResponsesBrain(given = {}) {
563
565
  case "response.failed": {
564
566
  noteResponseModel(partial, frame.response?.model, model.id);
565
567
  const err = frame.response?.error;
566
- throw new BrainError(responsesErrorCode(err?.code), `responses stream failed (${err?.code ?? "unknown"}): ${err?.message ?? ""}`, undefined, { apiError: true });
568
+ throw new BrainError(responsesErrorCode(err?.code), `responses stream failed ${ctrl.providerWords(`(${err?.code ?? "unknown"}): ${err?.message ?? ""}`)}`, undefined, { apiError: true });
567
569
  }
568
570
  case "error": {
569
- throw new BrainError(responsesErrorCode(frame.code), `responses stream error (${frame.code ?? "unknown"}): ${frame.message ?? ""}`, undefined, { apiError: true });
571
+ throw new BrainError(responsesErrorCode(frame.code), `responses stream error ${ctrl.providerWords(`(${frame.code ?? "unknown"}): ${frame.message ?? ""}`)}`, undefined, { apiError: true });
570
572
  }
571
573
  default:
572
574
  return;
@@ -607,7 +609,7 @@ export function createOpenResponsesBrain(given = {}) {
607
609
  }
608
610
  catch {
609
611
  malformedFrames++;
610
- malformedSample ??= data.slice(0, MALFORMED_SAMPLE_CHARS);
612
+ malformedSample ??= ctrl.providerWords(data).slice(0, MALFORMED_SAMPLE_CHARS);
611
613
  return;
612
614
  }
613
615
  handleFrame(frame);
@@ -660,10 +662,10 @@ export function createOpenResponsesBrain(given = {}) {
660
662
  finalContent.push(tc);
661
663
  }
662
664
  else if (acc.name) {
663
- malformed.push(`${acc.name}(${acc.args.slice(0, MALFORMED_ARGS_CHARS)})`);
665
+ malformed.push(`${ctrl.providerWords(acc.name)}(${ctrl.providerWords(acc.args).slice(0, MALFORMED_ARGS_CHARS)})`);
664
666
  }
665
667
  else {
666
- (nameMissingCause(closed) === "truncated" ? malformed : unnamed).push(`call_id="${acc.callId || "?"}"(${acc.args.slice(0, MALFORMED_ARGS_CHARS)})`);
668
+ (nameMissingCause(closed) === "truncated" ? malformed : unnamed).push(`call_id="${ctrl.providerWords(acc.callId || "?")}"(${ctrl.providerWords(acc.args).slice(0, MALFORMED_ARGS_CHARS)})`);
667
669
  }
668
670
  }
669
671
  }
@@ -708,14 +710,14 @@ export function createOpenResponsesBrain(given = {}) {
708
710
  : safetyCut
709
711
  ? `[refusal] response cut by the provider content filter (incomplete_details.reason="${INCOMPLETE_CONTENT_FILTER}") — the output was censored/truncated for policy and is unreliable`
710
712
  : refused
711
- ? `[refusal] response refused by the model for policy reasons: ${accumRefusal.trim()}`
713
+ ? `[refusal] response refused by the model for policy reasons: ${ctrl.providerWords(accumRefusal.trim())}`
712
714
  : (toolError ??
713
715
  (truncatedEmpty && !dynamicCut && !staticReasoningCut
714
716
  ? reasoningSeen
715
717
  ? `[length_empty] response truncated at max_output_tokens (${maxTokensNote}) with no answer text — the model spent the whole budget on reasoning; raise the cap, or lower the reasoning effort for this task.`
716
718
  : `[length_empty] response truncated at max_output_tokens (${maxTokensNote}) with no answer text — raise the cap`
717
719
  : emptyNoFinish
718
- ? `[stream_torn] model stream ended with no content and ${terminalStatus === undefined ? "no terminal response event" : `a terminal status "${terminalStatus}" with no reason this client recognises (incomplete_details.reason=${incompleteReason ?? "absent"})`} — the response was lost (a torn stream or an in-band provider error frame)${malformedFrames > 0 ? ` — ${malformedFrames} unparseable data frame(s) were dropped on this stream (first sample: ${JSON.stringify(malformedSample)})` : ""}`
720
+ ? `[stream_torn] model stream ended with no content and ${terminalStatus === undefined ? "no terminal response event" : `a terminal status "${ctrl.providerWords(terminalStatus)}" with no reason this client recognises (incomplete_details.reason=${incompleteReason === undefined ? "absent" : ctrl.providerWords(String(incompleteReason))})`} — the response was lost (a torn stream or an in-band provider error frame)${malformedFrames > 0 ? ` — ${malformedFrames} unparseable data frame(s) were dropped on this stream (first sample: ${JSON.stringify(malformedSample)})` : ""}`
719
721
  : undefined));
720
722
  const errored = degenerate ||
721
723
  safetyCut ||
@@ -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, imageWithholdOf, imagesOmittedNoVisionNote, reportImagesWithheld, sendableImages } from "./media-degrade.js";
8
8
  import { capabilityHeld } from "./model-capability.js";
9
- import { OPENAI_RESERVED, OUTPUT_CAP_KEYS, applyExtraBody, effectiveOutputCap, headerBagOnce, lockHeader, mergeHeaders } from "./request-params.js";
9
+ import { OPENAI_RESERVED, OUTPUT_CAP_KEYS, applyExtraBody, declaredRequestValues, effectiveOutputCap, headerBagOnce, lockHeader, mergeHeaders } from "./request-params.js";
10
10
  import { BrainError, providerErrorWord } from "./errors.js";
11
11
  import { NAMES_AS_WRITTEN, inBandProviderError, resolveProviderDialect, endpointToolNames } from "./provider-dialect.js";
12
12
  import { admitRouteRequest, applyRouteCredentialHeaders, assertServableRequestRoot, brainConfigSnapshot, createBrainRouteJudge, joinRequestUrl, resolveRequestRootOf, resolveRouteCredential } from "./route-adjudicator.js";
@@ -320,6 +320,7 @@ export function createOpenAIBrain(given = {}) {
320
320
  const { body: reasoningBody } = applyReasoningToRequest({ api: "openai-completions", model, level: options?.reasoning, body });
321
321
  const headers = mergeHeaders(modelHeaders, config.headers, optionsHeaders);
322
322
  applyRouteCredentialHeaders(headers, routeCredential);
323
+ const declaredValues = declaredRequestValues([modelHeaders, config.headers, optionsHeaders, headers], [], apiKey);
323
324
  lockHeader(headers, "content-type", "application/json");
324
325
  if (apiKey)
325
326
  lockHeader(headers, OPENAI_KEY_HEADER, `Bearer ${apiKey}`);
@@ -327,6 +328,7 @@ export function createOpenAIBrain(given = {}) {
327
328
  return {
328
329
  url,
329
330
  headers,
331
+ declared: declaredValues,
330
332
  body: JSON.stringify(wire),
331
333
  extraBodyDropped,
332
334
  route: requestRouteOf(routeCredential, rootOrigin),
@@ -359,7 +361,7 @@ export function createOpenAIBrain(given = {}) {
359
361
  const responseIds = responseToolCallIds();
360
362
  let finishReason = null;
361
363
  let inBandError;
362
- const afterEnd = createStreamEndLedger();
364
+ const afterEnd = createStreamEndLedger(ctrl.providerWords);
363
365
  const terminalFrameSeen = () => finishReason != null || inBandError !== undefined;
364
366
  let usageRaw;
365
367
  let malformedFrames = 0;
@@ -447,7 +449,7 @@ export function createOpenAIBrain(given = {}) {
447
449
  }
448
450
  catch {
449
451
  malformedFrames++;
450
- malformedSample ??= data.slice(0, 160);
452
+ malformedSample ??= ctrl.providerWords(data).slice(0, 160);
451
453
  return;
452
454
  }
453
455
  const stated = chunk.error;
@@ -456,7 +458,7 @@ export function createOpenAIBrain(given = {}) {
456
458
  afterEnd.note("error", providerErrorWord(stated) ?? "?");
457
459
  return;
458
460
  }
459
- inBandError = inBandProviderError(ctrl.dialect, stated, "gateway");
461
+ inBandError = inBandProviderError(ctrl.dialect, stated, "gateway", ctrl.providerWords);
460
462
  ctrl.cancel();
461
463
  return;
462
464
  }
@@ -537,7 +539,7 @@ export function createOpenAIBrain(given = {}) {
537
539
  emptyPlaceholders++;
538
540
  continue;
539
541
  }
540
- (nameMissingCause(!endedCut) === "truncated" ? malformed : unnamed).push(`id="${acc.id || "?"}"(${acc.args.slice(0, 200)})`);
542
+ (nameMissingCause(!endedCut) === "truncated" ? malformed : unnamed).push(`id="${ctrl.providerWords(acc.id || "?")}"(${ctrl.providerWords(acc.args).slice(0, 200)})`);
541
543
  continue;
542
544
  }
543
545
  const tc = closeToolCallAccum(acc, responseIds, !endedCut);
@@ -546,7 +548,7 @@ export function createOpenAIBrain(given = {}) {
546
548
  finalContent.push(tc);
547
549
  }
548
550
  else {
549
- malformed.push(`${acc.name}(${acc.args.slice(0, 200)})`);
551
+ malformed.push(`${ctrl.providerWords(acc.name)}(${ctrl.providerWords(acc.args).slice(0, 200)})`);
550
552
  }
551
553
  }
552
554
  if (config.repairTextToolCalls && toolCalls.length === 0 && textFace && context.tools && context.tools.length > 0) {
@@ -604,8 +606,8 @@ export function createOpenAIBrain(given = {}) {
604
606
  const safetyCut = finishReading?.kind === "refusal";
605
607
  const contextWindowExceeded = finishReading?.kind === "input_too_long";
606
608
  const providerCut = finishReading?.kind === "provider_cut" ? finishReading : undefined;
607
- const finishReasonUnknown = finishReading?.kind === "unrecognized" ? echoFoldedWord(finishReason ?? "") : undefined;
608
- const finishQuoted = JSON.stringify((finishReason ?? "").slice(0, 64));
609
+ const finishReasonUnknown = finishReading?.kind === "unrecognized" ? echoFoldedWord(finishReason ?? "", ctrl.providerWords) : undefined;
610
+ const finishQuoted = JSON.stringify(ctrl.providerWords(finishReason ?? "").slice(0, 64));
609
611
  const refused = accumRefusal.trim().length > 0;
610
612
  const emptyNoFinish = noUsableContent && finishReason == null && !degenerate;
611
613
  const maxTokensNote = sentMaxTokens !== undefined
@@ -620,7 +622,7 @@ export function createOpenAIBrain(given = {}) {
620
622
  : safetyCut
621
623
  ? `[refusal] response cut by the provider content filter (finish_reason=${finishQuoted}) — the output was censored/truncated for policy and is unreliable`
622
624
  : refused
623
- ? `[refusal] response refused by the model for policy reasons: ${accumRefusal.trim()}`
625
+ ? `[refusal] response refused by the model for policy reasons: ${ctrl.providerWords(accumRefusal.trim())}`
624
626
  : contextWindowExceeded
625
627
  ? `[invalid_request] model context window exceeded (finish_reason=${finishQuoted}) — the conversation no longer fits the model's context window`
626
628
  : providerCut !== undefined
@@ -240,11 +240,12 @@ export declare function judgeInBandError(dialect: ProviderDialect | undefined, e
240
240
  * The failure an in-band error frame states, as the brain error the stream's terminal carries: the code its
241
241
  * condition reads as ({@link PROVIDER_CONDITION_TRAITS}), the provider's word and sentence in the message, the word
242
242
  * on the echo seat, the verdict, and the context-limit reading of the same row. A frame the provider sent on its
243
- * own stream is a provider failure by construction (`apiError`); it states no status.
243
+ * own stream is a provider failure by construction (`apiError`); it states no status. The message carries the
244
+ * provider's words as the stream's mint (`providerWords`) renders them; the verdict reads the frame as it came.
244
245
  */
245
246
  export declare function inBandProviderError(dialect: ProviderDialect | undefined, envelope: {
246
247
  message?: unknown;
247
248
  code?: unknown;
248
249
  type?: unknown;
249
- }, label: string): BrainError;
250
+ }, label: string, providerWords: (words: string) => string): BrainError;
250
251
  export {};
@@ -201,11 +201,11 @@ export function judgeHttpFailure(args) {
201
201
  export function judgeInBandError(dialect, envelope) {
202
202
  return verdictOf(dialect, undefined, statementOf(envelope), true);
203
203
  }
204
- export function inBandProviderError(dialect, envelope, label) {
204
+ export function inBandProviderError(dialect, envelope, label, providerWords) {
205
205
  const v = judgeInBandError(dialect, envelope);
206
206
  const word = carriedProviderWord(envelope);
207
207
  const sentence = typeof envelope.message === "string" ? envelope.message : "";
208
- return new BrainError(PROVIDER_CONDITION_TRAITS[v.condition].inBandCode, `${label} stream error (${word ?? "unknown"}): ${sentence}`, undefined, {
208
+ return new BrainError(PROVIDER_CONDITION_TRAITS[v.condition].inBandCode, `${label} stream error ${providerWords(`(${word ?? "unknown"}): ${sentence}`)}`, undefined, {
209
209
  apiError: true,
210
210
  ...(v.inputTooLong === "yes" ? { inputTooLong: true } : v.inputTooLong === "no" ? { inputTooLongRuledOut: true } : {}),
211
211
  ...(word !== undefined ? { providerCode: word } : {}),
@@ -136,6 +136,12 @@ export declare function headerListEntries(value: unknown): string[];
136
136
  * or keys answer differently on a second read cannot be judged as one bag and sent as another. */
137
137
  export declare function headerBagOnce(bag: Readonly<Record<string, string>> | undefined): Record<string, string> | undefined;
138
138
  export declare function mergeHeaders(...bags: Array<Record<string, string> | undefined>): Record<string, string>;
139
+ /** The values a request is DECLARED with ({@link import("./stream-engine.js").SSERequest.declared}): every value of the header bags it was
140
+ * built from — the route's credential headers included, as the merge after `applyRouteCredentialHeaders` holds them — the values the
141
+ * deployment or the call stated outside a bag (a configured protocol version, each entry of a list header the brain re-folds), and the
142
+ * key when it names a secret ({@link namesASecret}: a placeholder key is left in the words as it is left to every child). The values the
143
+ * brain writes itself (a structural lock, the default version, the beta entries it chose) are not among them. */
144
+ export declare function declaredRequestValues(bags: ReadonlyArray<Readonly<Record<string, string>> | undefined>, stated: ReadonlyArray<string | undefined>, key: string | undefined): string[];
139
145
  /**
140
146
  * The output-cap key(s) each lane's wire form uses. A lane's set is exactly the keys THAT lane's
141
147
  * endpoint reads — a stray cap key belonging to another wire form is inert there and must not be