@sema-agent/core 7.27.0 → 7.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/agents/builtin-agents.d.ts +6 -15
  3. package/dist/agents/cascade.d.ts +29 -33
  4. package/dist/agents/observer.d.ts +18 -20
  5. package/dist/agents/subagent.d.ts +58 -70
  6. package/dist/agents/tool-filter.d.ts +10 -12
  7. package/dist/agents/verify.d.ts +44 -49
  8. package/dist/brain/input-too-long.d.ts +10 -12
  9. package/dist/brain/media-degrade.d.ts +4 -5
  10. package/dist/brain/reasoning.d.ts +10 -12
  11. package/dist/brain/repetition.d.ts +7 -9
  12. package/dist/brain/request-params.d.ts +20 -25
  13. package/dist/brain/route-adjudicator.d.ts +4 -4
  14. package/dist/brain/route-conformance.d.ts +2 -2
  15. package/dist/brain/status-sink.d.ts +12 -14
  16. package/dist/brain/terminal-cause.d.ts +12 -15
  17. package/dist/brain/timeout.d.ts +17 -19
  18. package/dist/core/memory-engine/consolidation.d.ts +11 -15
  19. package/dist/core/memory-engine/engine.d.ts +45 -54
  20. package/dist/core/memory-engine/tools.d.ts +17 -23
  21. package/dist/core/permission-rule-model.js +5 -3
  22. package/dist/shell/lex.d.ts +279 -0
  23. package/dist/shell/lex.js +1253 -0
  24. package/dist/tools/fs/bash-program-position.d.ts +2 -4
  25. package/dist/tools/fs/bash-program-position.js +2 -1
  26. package/dist/tools/fs/bash-readonly-classifier.d.ts +2 -2
  27. package/dist/tools/fs/bash-readonly-classifier.js +1 -1
  28. package/dist/tools/fs/shell-read-check.js +1 -1
  29. package/dist/tools/fs/shell-read-establish.js +11 -1
  30. package/dist/tools/fs/shell-read-plan.d.ts +7 -2
  31. package/dist/tools/fs/shell-read-plan.js +48 -10
  32. package/package.json +2 -2
  33. package/wire-changes/core@7.28.1.json +594 -0
  34. package/dist/tools/fs/bash-lexer.d.ts +0 -152
  35. package/dist/tools/fs/bash-lexer.js +0 -627
@@ -7,22 +7,21 @@ import type { ModelRef, TaskResult, TaskSpec, TaskStatus, ToolSpec } from "../co
7
7
  * an implementation task and tries to BREAK it — read-only, evidence-required, returning a structured
8
8
  * verdict — then the gate loops fix→re-verify until PASS or a round cap.
9
9
  *
10
- * PROVENANCE (2026-07-14; RB-306 ④ — re-verified and CORRECTED against CC 2.1.220): distilled and
11
- * de-branded from the MIT-era upstream source's dedicated `verification` agent — which was an
12
- * Anthropic-internal A/B experiment (third-party default OFF) and has since been RETIRED upstream:
13
- * 2.1.207 through 2.1.220 ship NO verification agent. The current upstream completion-gate shape is
14
- * (a) the Stop hook (`decision:"block"` + reason fed back — sema's Stop hook is at full parity) and
15
- * (b) — corrected — NOT a bundled skill. There is no built-in `verify` skill in the 220 corpus. What
16
- * 220 ships is an INSTRUCTION TO CREATE ONE PER PROJECT: its memory prompt (@190146 (cli250.js:38927)) forbids creating
17
- * project skills ("never create one — a new project skill silently shadows a same-named built-in
18
- * skill") and makes exactly one exception — "The single exception is verify, because how a project
19
- * verifies changes is project-specific: put a verify correction in the `.claude/skills/verify/
20
- * SKILL.md` closest to the code it covers … and if that file does not exist, create it." So upstream's
21
- * position is "verification is project-specific; teach the user's repo to describe its own", not "here
22
- * is a verifier". This primitive is therefore a SUPERSET of what upstream ships in a different
23
- * direction than the old note implied: a spawned falsification-style verifier with a fix loop, bundled. It is
24
- * orthogonal to and composable with the Stop hook (wire the verdict into a stop() hook to make it a
25
- * hard completion gate).
10
+ * PROVENANCE (2026-07-14; re-verified and CORRECTED against CC 2.1.220): distilled and de-branded from the
11
+ * MIT-era upstream source's dedicated `verification` agent — which was an Anthropic-internal A/B experiment
12
+ * (third-party default OFF) and has since been RETIRED upstream: 2.1.207 through 2.1.220 ship NO verification
13
+ * agent. The current upstream completion-gate shape is (a) the Stop hook (`decision:"block"` + reason fed
14
+ * back — sema's Stop hook is at full parity) and (b) — corrected — NOT a bundled skill. There is no built-in
15
+ * `verify` skill in the 220 corpus. What 220 ships is an INSTRUCTION TO CREATE ONE PER PROJECT: its memory
16
+ * prompt (@190146 (cli250.js:38927)) forbids creating project skills ("never create one — a new project skill
17
+ * silently shadows a same-named built-in skill") and makes exactly one exception — "The single exception is
18
+ * verify, because how a project verifies changes is project-specific: put a verify correction in the
19
+ * `.claude/skills/verify/ SKILL.md` closest to the code it covers … and if that file does not exist, create
20
+ * it." So upstream's position is "verification is project-specific; teach the user's repo to describe its
21
+ * own", not "here is a verifier". This primitive is therefore a SUPERSET of what upstream ships in a
22
+ * different direction than the old note implied: a spawned falsification-style verifier with a fix loop,
23
+ * bundled. It is orthogonal to and composable with the Stop hook (wire the verdict into a stop() hook to make
24
+ * it a hard completion gate).
26
25
  *
27
26
  * DEPLOYMENT POSTURE (ruled 2026-07-14): opt-in library primitive ONLY — never a scenario default,
28
27
  * never deployed implicitly. It is a **thin composition** over existing core seams — a verifier subtask
@@ -244,11 +243,11 @@ export interface VerificationResult extends TaskResult {
244
243
  export declare function verifyCompleted(runner: RunnerSelfSeat, result: TaskResult, specBase: ResumeTaskConfig, objective: string, config: VerifyConfig,
245
244
  /**
246
245
  * The TRUSTED, run-scoped internals channel — the same object `Runner.runTask` takes as its second
247
- * argument — forwarded to EVERY leg this gate drives under the composition forward contract (identity,
248
- * with the one placement-axis exception stated where the shape is named in `src/index.ts`): the verifier run of each round and
249
- * each fix continuation (and, from {@link runWithVerification} / {@link resumeWithVerification} /
250
- * {@link runDeveloperTask}, the implementation leg as well). This doc is the contract for the whole
251
- * family; the sibling entries carry the same parameter and point here.
246
+ * argument — forwarded to EVERY leg this gate drives under the composition forward contract
247
+ * (identity, with the one placement-axis exception stated where the shape is named in
248
+ * `src/index.ts`): each round's verifier run and each fix continuation, plus (from
249
+ * {@link runWithVerification} / {@link resumeWithVerification} / {@link runDeveloperTask}) the
250
+ * implementation leg. This doc is the contract for the family; the siblings point here.
252
251
  *
253
252
  * WHY THE SEAT EXISTS: these entries return a `TaskResult` and DRAIN each leg's event stream
254
253
  * internally, so the host never holds a `TaskStream` for any of them. Without this parameter a
@@ -257,34 +256,31 @@ export declare function verifyCompleted(runner: RunnerSelfSeat, result: TaskResu
257
256
  * (`onActivity`, `onForwardEvent`, `onWorkspaceResolved`, `onWiringManifest`) is the only way in.
258
257
  *
259
258
  * ATTRIBUTION — WEAK, and deliberately stated as such: the legs run SEQUENTIALLY (impl → verifier →
260
- * fix → verifier → …) and each is dispatched with the SAME object, so a leg's own tool beats are all
261
- * delivered before the next leg is dispatched. That ORDERING is the whole of it. `ToolActivity`
262
- * carries no leg identity, so generic activity CANNOT be attributed to a leg from the frame alone:
263
- * two adjacent legs calling the same tool are indistinguishable, and the structured-output tool is
264
- * no verifier marker either (an impl spec carrying its own `outputSchema` mounts it too).
265
- * {@link VerifyConfig.onRound} bounds a ROUND, not a leg — the segment before it holds the impl (or
266
- * fix) leg's beats AND that round's verifier beats together. A host that needs true per-leg
267
- * attribution for THOSE frames must drive the legs itself (one `runner.runTask` per leg) rather than
268
- * read it out of this stream. Two further honesty notes: SUBAGENT events forwarded through
259
+ * fix → verifier → …) with the SAME object, so a leg's beats are all delivered before the next leg
260
+ * is dispatched, and that ORDERING is the whole of it. `ToolActivity` carries no leg identity, so
261
+ * generic activity CANNOT be attributed to a leg from the frame alone: two adjacent legs calling
262
+ * the same tool are indistinguishable, and the structured-output tool is no verifier marker either
263
+ * (an impl spec carrying its own `outputSchema` mounts it too). {@link VerifyConfig.onRound} bounds
264
+ * a ROUND, not a leg — the segment before it holds the impl (or fix) leg's beats AND that round's
265
+ * verifier beats. A host needing true per-leg attribution for THOSE frames must drive the legs
266
+ * itself (one `runner.runTask` per leg). Two honesty notes: SUBAGENT events forwarded through
269
267
  * `onForwardEvent` do carry their own `sourceTaskId`, and a BACKGROUND child outlives the leg that
270
- * spawned it — its frames can arrive after that leg returned, i.e. interleaved into a later leg's
271
- * segment.
268
+ * spawned it — its frames can arrive interleaved into a later leg's segment.
272
269
  *
273
- * ONE SEAT IS EXEMPT, because its frame is per-leg by construction: `onWiringManifest` fires exactly
274
- * once per leg, at that leg's start, and this gate hands it the leg's own IDENTITY beside the
275
- * manifest (the axis, and the two readings a consumer cannot infer, are documented at the seat).
276
- * That identity is minted HERE, in the dispatch that knows it — the engine stamps none and infers
277
- * none — so a consumer of this seat reads per-leg attribution instead of reconstructing it from
278
- * arrival order. This is also the one place the forwarding below is not by identity: a caller that
279
- * wired that sink gets a fresh per-dispatch copy carrying the bound sink (see `withLegIdentity`),
280
- * every other field still by its own reference.
270
+ * ONE SEAT IS EXEMPT, because its frame is per-leg by construction: `onWiringManifest` fires once
271
+ * per leg, at that leg's start, and this gate hands it the leg's own IDENTITY beside the manifest
272
+ * (the axis, and the two readings a consumer cannot infer, are documented at the seat). That
273
+ * identity is minted HERE, in the dispatch that knows it — the engine stamps none — so this seat's
274
+ * consumer reads per-leg attribution instead of reconstructing it from arrival order. It is also
275
+ * the one place the forwarding below is not by identity: a caller that wired that sink gets a fresh
276
+ * per-dispatch copy carrying the bound sink (see `withLegIdentity`), other fields by reference.
281
277
  *
282
- * FAILURE-ISOLATED since #248, by the same primitive as {@link VerifyConfig.onRound} /
278
+ * FAILURE-ISOLATED, by the same primitive as {@link VerifyConfig.onRound} /
283
279
  * {@link VerifyConfig.onVerifierPause}: the fields on this object are invoked by the ENGINE on the
284
280
  * run's own path, through a run-scoped safe notifier — a throwing `onActivity` is contained (first
285
281
  * failure per site disclosed via `console.warn`, later ones counted) and never faults the leg it
286
- * fires on. Before #248 a throw here failed the leg (misclassified `provider.error`) and an impl
287
- * leg was reported incomplete — the MEASURED HAZARD pins that recorded that now pin containment.
282
+ * fires on. Uncontained, such a throw fails the leg (misclassified `provider.error`) and reports an
283
+ * impl leg incomplete — the MEASURED HAZARD pins hold containment.
288
284
  *
289
285
  * FORWARDED WHOLE, NOT FILTERED, with ONE refusal: every field rides every leg, including the
290
286
  * ref-valued ones (`peerSelfRef`, `ownOrgAdmissionRef`) whose own contract is one ref per RUN —
@@ -296,12 +292,11 @@ export declare function verifyCompleted(runner: RunnerSelfSeat, result: TaskResu
296
292
  * (`refuseUnhonorableInternals`) carries the reasoning, the re-check rule, and what a mid-gate
297
293
  * refusal does and does not undo.
298
294
  *
299
- * Absent ⇒ every leg is dispatched exactly as before (each call site passes `undefined`, which is
300
- * what an omitted optional argument already was) — with ONE design/380 O1② placement delta either
301
- * way: when neither `placementRoot` nor `rootSessionId` is present, the verifier and fix legs are
302
- * dispatched with a fresh shallow copy carrying `placementRoot` = the entry implementation leg's
303
- * sessionId (the gate's placement fixed point); every other field still rides by the same
304
- * reference, and an internals object that already carries a fixed point is forwarded verbatim.
295
+ * Absent ⇒ every leg is dispatched with `undefined`, with ONE design/380 O1② placement delta either
296
+ * way: when neither `placementRoot` nor `rootSessionId` is present, the verifier and fix legs get a
297
+ * fresh shallow copy carrying `placementRoot` = the entry implementation leg's sessionId (the
298
+ * gate's placement fixed point); every other field still rides by the same reference, and an
299
+ * internals object that already carries a fixed point is forwarded verbatim.
305
300
  */
306
301
  internals?: RunInternals): Promise<VerificationResult>;
307
302
  /**
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * design/374 slice 2 — TYPED input-too-long classification for provider refusals.
3
3
  *
4
- * "The request INPUT alone exceeds the model's context limit" used to be recognized only by the
5
- * loop's prose regex over the human-facing `errorMessage` — a classifier that (a) cannot tell a
6
- * provider's own diagnostic from caller text a gateway reflected, and (b) breaks the moment a
7
- * provider rewords. This module classifies at the BRAIN boundary, where provenance is decidable,
8
- * and the verdict rides `BrainError.inputTooLong` → `AssistantMessage.errorKind: "input_too_long"`
9
- * (the same typed lane `degenerate`/`length_empty` already use).
4
+ * "The request INPUT alone exceeds the model's context limit" is recognizable from prose over the
5
+ * human-facing `errorMessage`, but such a classifier cannot tell a provider's own diagnostic from
6
+ * caller text a gateway reflected, and breaks the moment a provider rewords. This module classifies
7
+ * at the BRAIN boundary, where provenance is decidable, and the verdict rides
8
+ * `BrainError.inputTooLong` → `AssistantMessage.errorKind: "input_too_long"` (the same typed lane
9
+ * `degenerate`/`length_empty` already use).
10
10
  *
11
11
  * PROVENANCE DISCIPLINE (mirrors `context-overflow.ts`, the sibling classifier for the
12
12
  * input+max_tokens overflow — a DIFFERENT error: there the input fits and lowering the output cap
@@ -24,12 +24,10 @@
24
24
  * - Structural signals need no prose at all: OpenAI-family `error.code ===
25
25
  * "context_length_exceeded"`, and HTTP 413 (the transport's own "request entity too large").
26
26
  *
27
- * Misclassification cost note (updated with the r1 three-state verdict — the original "a miss
28
- * keeps exactly the pre-374 behavior" claim died with the binary form): inside a PARSED provider
29
- * envelope a miss is an AUTHORITATIVE negative that SUPPRESSES the loop's prose fallback — which
30
- * is why the family must carry every observed genuine opening (the r2/r3/r4 additions below);
31
- * prose-only bodies keep the pre-374 fallback reach via "unknown". A FALSE HIT costs one bounded
32
- * recovery attempt — but only on a 400/413/422 the request already failed on.
27
+ * Misclassification cost: inside a PARSED provider envelope a miss is an AUTHORITATIVE negative
28
+ * that SUPPRESSES the loop's prose fallback — which is why the family must carry every observed
29
+ * genuine opening; prose-only bodies keep the fallback reach via the "unknown" arm. A FALSE HIT
30
+ * costs one bounded recovery attempt, and only on a 400/413/422 the request already failed on.
33
31
  */
34
32
  /** True when the (provider-authored) diagnostic OPENS with the context-limit refusal family. */
35
33
  export declare function startsWithInputTooLongAnchor(diagnostic: string): boolean;
@@ -1,11 +1,10 @@
1
1
  /**
2
2
  * Media degradation shared by the two message assemblers (anthropic.ts / openai.ts).
3
3
  *
4
- * Both lanes have to answer the same two questions on every request — "may this model see images?"
5
- * and "what do I say when a media block cannot ride along?" — and both used to answer them with
6
- * their own copy of the predicate and their own copy of the sentence. One construction point:
7
- * a capability rule that drifts between lanes is a silent per-provider behavior difference, and a
8
- * placeholder sentence that drifts is a string consumers grep for.
4
+ * Both lanes answer the same two questions on every request — "may this model see images?" and
5
+ * "what do I say when a media block cannot ride along?" — from ONE construction point: a capability
6
+ * rule that drifts between lanes is a silent per-provider behavior difference, and a placeholder
7
+ * sentence that drifts is a string consumers grep for.
9
8
  */
10
9
  import type { Model } from "../internal/llm.js";
11
10
  /**
@@ -30,11 +30,10 @@ export declare function isThinkingLevel(v: unknown): v is ThinkingLevel;
30
30
  * where it lives); this predicate only closes the gate the three appliers used to re-spell.
31
31
  *
32
32
  * Single-sourced here because every wire applier (openai.ts `applyThinking`, anthropic.ts's thinking
33
- * + effort arms, open-responses.ts `resolveWireEffort`) used to re-spell the same three conjuncts —
34
- * and the REPORTING resolver ({@link resolveReasoning}) mirrored only ONE of them (`!model.reasoning`),
35
- * so a garbage tier put ZERO parameters on the wire while the resolution claimed a clamped-to-minimal
36
- * gradient (or echoed the garbage string as `effective` on binary formats). A predicate the appliers
37
- * and the reporter both call cannot drift.
33
+ * + effort arms, open-responses.ts `resolveWireEffort`) would otherwise re-spell the same three
34
+ * conjuncts while the REPORTING resolver ({@link resolveReasoning}) mirrored one of them, so a
35
+ * garbage tier puts ZERO parameters on the wire while the resolution claims a clamped-to-minimal
36
+ * gradient. A predicate the appliers and the reporter both call cannot drift.
38
37
  */
39
38
  export declare function reasoningRequestCarried(model: {
40
39
  reasoning?: boolean;
@@ -237,17 +236,16 @@ export interface ResolvedReasoning extends ReasoningResolution {
237
236
  * - A CLAMPED tier that `model.thinkingLevelMap` maps to `null` (declared unsupported on this model) →
238
237
  * `graded:false`: both effort-wire appliers key the map by the post-clamp tier and then carry NO effort
239
238
  * value (thinking still enables via the format's own enable key where one exists), so the tier is not
240
- * honored. Previously this resolver never read the map and reported such a request as exactly honored
241
- * (`graded:true`, `clamped:false`) while the wire dropped the value — trace/result-face drift.
239
+ * honored. A resolver blind to the map reports such a request as exactly honored while the wire
240
+ * drops the value — trace/result-face drift.
242
241
  *
243
242
  * - A request that never CARRIES ({@link reasoningRequestCarried} false on a non-"off" value: the model's
244
243
  * `reasoning` capability flag is FALSY, or the requested value is not a valid tier — no brain emits any
245
244
  * thinking parameter for either, whatever the format) → `effective:"off"`, `graded:false`, `clamped:true`,
246
- * `dropped:true` — the loud-drop arm. Previously this resolver described the capability dispatch for such
247
- * a model (a resolution the request never carried), and the runner's trace guard skipped the frame — the
248
- * one arm where the request evaporates was the one arm with no disclosure; and an out-of-contract tier
249
- * was worse still — reported as a clamped-to-minimal gradient (or echoed verbatim as `effective` on the
250
- * binary formats) while the wire carried nothing.
245
+ * `dropped:true` — the loud-drop arm. Describing the capability dispatch instead would report a
246
+ * resolution the request never carried, leaving the one arm where the request evaporates as the one
247
+ * arm with no disclosure (and an out-of-contract tier reported as a clamped gradient, or echoed as
248
+ * `effective` on the binary formats, while the wire carried nothing).
251
249
  *
252
250
  * `off`/falsy never enables thinking, so it resolves trivially (no clamp, graded:true) — the caller decides
253
251
  * whether to emit at all.
@@ -72,17 +72,15 @@ export declare function looksDegenerate(text: string): boolean;
72
72
  * bounded degenerate-recovery continue would otherwise re-feed the model as context — the very text
73
73
  * it must not repeat).
74
74
  *
75
- * Always-on by design (no opt-out flag). The honest argument (fable review tightened the original
76
- * "zero information loss" claim):
75
+ * Always-on by design (no opt-out flag). The honest bound — not "zero information loss":
77
76
  * - everything removed is a byte-identical duplicate of the unit that is KEPT, so no UNIQUE text is
78
77
  * ever swallowed (a non-repeating tail never matches and is returned unchanged). What CAN be lost
79
- * is a repetition COUNT: if the legitimate text immediately before the loop happened to end with
80
- * the same unit repeated (e.g. an intentional "very very" directly abutting a "very"-loop), those
81
- * legitimate repeats fold into the loop and collapse to the single kept instance. This only ever
82
- * happens on a stream the detector ALREADY cut as degenerate, and the alternative is worse (the
83
- * full loop tail re-fed to the model);
84
- * - most legitimate verbatim repetition (code lines, dividers) never gets here at all — the 2e1c161
85
- * allowances spare it upstream, and a spared stream is never cut;
78
+ * is a repetition COUNT: legitimate text ending with the same unit repeated (an intentional
79
+ * "very very" abutting a "very"-loop) folds into the loop and collapses to the single kept
80
+ * instance. That only happens on a stream the detector ALREADY cut as degenerate, and the
81
+ * alternative is worse (the full loop tail re-fed to the model);
82
+ * - most legitimate verbatim repetition (code lines, dividers) never gets here — the allowances
83
+ * spare it upstream, and a spared stream is never cut;
86
84
  * - the loop may have grown far past the 400-char inspection window before the poll fired; the
87
85
  * scan below strips ALL trailing repeats (and one trailing PARTIAL unit from post-cut buffered
88
86
  * deltas), not just the reps the window measured.
@@ -35,25 +35,21 @@ export declare const AUTH_CARRIER_NAMES: ReadonlySet<string>;
35
35
  /**
36
36
  * #343 — the shared USER-HEADER merge layer (`model.headers` → construction `config.headers` →
37
37
  * per-call `options.headers`, later bag wins), CASE-FOLD deduplicated: HTTP header field names are
38
- * case-insensitive (RFC 9110), but the plain-object spread the three brains used
39
- * (`{...model.headers, ...config.headers, ...options.headers}`) keyed by exact spelling — a
40
- * `X-Tenant` in one bag and `x-tenant` in another BOTH survived and both went on the wire, where
41
- * fetch's Headers folds them into one comma-joined value ("a, b"): neither writer's value, and the
42
- * later layer's documented override silently defeated. Now a later bag's entry replaces an earlier
43
- * case-variant; the WINNER'S spelling and value survive (a single-spelling config — every existing
44
- * deployment — is byte-identical on the wire).
38
+ * case-insensitive (RFC 9110), but a plain-object spread keys by exact spelling, so an `X-Tenant`
39
+ * in one bag and an `x-tenant` in another both reach the wire, where fetch's Headers folds them
40
+ * into one comma-joined value ("a, b") — neither writer's value, the later layer's documented
41
+ * override silently defeated. A later bag's entry replaces an earlier case-variant instead; the
42
+ * WINNER'S spelling and value survive (a single-spelling config is byte-identical on the wire).
45
43
  *
46
44
  * EXEMPT: the auth carriers (`authorization` / `x-api-key`, any case) pass through with plain
47
45
  * spread semantics (same-spelling override only, no case-fold dedup), so that the credential
48
46
  * enforcement (`applyRouteCredentialHeaders`, route-adjudicator.ts) stays the ONE authority over
49
47
  * auth spelling and this layer never becomes a second, subtly different one.
50
48
  *
51
- * RE-RULED, because the exemption used to be justified by a reason that does not hold: the
52
- * note claimed it protected "the header-only ANTHROPIC_AUTH_TOKEN shape, which must survive under its
53
- * own capital-A spelling". Dedup would not endanger that shape — it keeps the WINNER'S spelling, and a
54
- * lone `Authorization` has nothing to be deduped against, so it survives either way; nor does the
55
- * credential-replaces flow depend on the exemption, since the enforcement already deletes every
56
- * spelling present when a per-model credential wins. Measured, not reasoned:
49
+ * The exemption is NOT what keeps a header-only `ANTHROPIC_AUTH_TOKEN` alive: dedup keeps the
50
+ * winner's spelling, a lone `Authorization` has nothing to dedup against, and the credential
51
+ * enforcement already deletes every spelling present when a per-model credential wins. What the
52
+ * exemption really costs is measured, not reasoned:
57
53
  * `mergeHeaders({Authorization:A},{authorization:B})` keeps BOTH, and the platform `Headers` fold
58
54
  * sends `authorization: A, B`.
59
55
  *
@@ -61,20 +57,19 @@ export declare const AUTH_CARRIER_NAMES: ReadonlySet<string>;
61
57
  * PER-MODEL credential wins (an options- or entry-bag carrier), every carrier is stripped and only
62
58
  * the winning bag's re-asserted, so a cross-layer double spelling now folds to the winner. What
63
59
  * remains is the DEPLOYMENT-CONFIG arm — its bytes ride untouched by design — so a config bag that
64
- * itself spells the SAME auth carrier two ways still ships both, comma-folded. It is held, not
65
- * denied, on severity: no server accepts a comma-joined credential, so the failure is a LOUD 401
66
- * attributable to the misconfiguration, whereas the non-auth case this function exists for produced
67
- * a silently WRONG value (`X-Tenant: a, b` — neither writer's, the later layer's documented
68
- * override defeated). Tightening it changes which credential reaches the wire, so it belongs in a
69
- * window that discloses an auth-face behavior change.
60
+ * itself spells the SAME auth carrier two ways still ships both, comma-folded. Held, not denied, on
61
+ * severity: no server accepts a comma-joined credential, so that failure is a LOUD 401 attributable
62
+ * to the misconfiguration, while the non-auth case this function exists for produces a silently
63
+ * WRONG value (`X-Tenant: a, b`). Tightening it changes which credential reaches the wire, so it
64
+ * belongs in a window that discloses an auth-face behavior change.
70
65
  */
71
66
  /**
72
- * #343 (review r4) — assign a STRUCTURAL locked header under its canonical lowercase name, deleting
73
- * every case-variant spelling first. The brains hard-lock `content-type` / `anthropic-version` AFTER
74
- * the user-bag merge precisely so they "can NEVER be overridden" (council design/40) — but a valid
75
- * user bag carrying `Content-Type: text/plain` survived BESIDE the lowercase lock, and the platform
76
- * `Headers` fold turns the pair into `text/plain, application/json` on the wire: the lock decided
77
- * nothing. Auth carriers are deliberately NOT routed through here (credential replacement + the
67
+ * #343 — assign a STRUCTURAL locked header under its canonical lowercase name, deleting every
68
+ * case-variant spelling first. The brains hard-lock `content-type` / `anthropic-version` AFTER the
69
+ * user-bag merge precisely so they "can NEVER be overridden" (council design/40) — and without the
70
+ * delete a user bag carrying `Content-Type: text/plain` survives BESIDE the lowercase lock, the
71
+ * platform `Headers` fold turning the pair into `text/plain, application/json` on the wire: a lock
72
+ * that decides nothing. Auth carriers are deliberately NOT routed through here (credential replacement + the
78
73
  * header-only boot flow are the pinned jurisdiction of `applyRouteCredentialHeaders`,
79
74
  * route-adjudicator.ts).
80
75
  */
@@ -2,10 +2,10 @@
2
2
  * key↔URL route pairing adjudication — the SINGLE source of the pairing law.
3
3
  *
4
4
  * The invariant: **the credential a request carries must belong to the URL the request targets.**
5
- * A request's URL half is per-ENTRY (`Model.baseUrl`, a required field) while the historical
6
- * credential fallback was per-BRAIN-INSTANCE (`options.apiKey ?? config.apiKey`): with more than one
7
- * entry flowing through one brain, the deployment credential silently followed EVERY entry's URL —
8
- * including a third-party host it was never meant for. This module is what the three first-party
5
+ * A request's URL half is per-ENTRY (`Model.baseUrl`, a required field) while the credential
6
+ * fallback is per-BRAIN-INSTANCE (`options.apiKey ?? config.apiKey`): unadjudicated, a deployment
7
+ * credential follows EVERY entry's URL through one brain — third-party hosts included. This module
8
+ * is what the three first-party
9
9
  * brains now consult before shaping a request, what the brain decorators re-walk per hop (each
10
10
  * hop/route re-resolves the three credential states for ITS model against ITS config), and what
11
11
  * external consumers (a server annotating a model roster, a conformance suite) import so there is
@@ -10,8 +10,8 @@ import type { RouteAdjudication, RouteCredential, RoutePairingConfig } from "../
10
10
  * (the gold-corpus discipline):
11
11
  * - CORE runs it as a regression test (`test/route-adjudication.test.ts`): every vector's `expect`
12
12
  * MUST equal `adjudicateModelRoute(model, credential, config)` on the `ok`/`posture`/`code`
13
- * axes. A change to the law fails the test and updates the corpus in lockstep with the engine
14
- * version, so the published contract can never silently drift from the implementation.
13
+ * axes, so a change to the law fails the test and updates the corpus in lockstep — the published
14
+ * contract cannot silently drift from the implementation.
15
15
  * - A CONSUMER imports this corpus from `@sema-agent/core` and runs it as a CONTRACT test against
16
16
  * whatever face it wired the adjudicator into: for each vector, its surface's
17
17
  * allow/annotate/refuse of the described route MUST match `expect.ok` (and forward
@@ -81,21 +81,19 @@ export declare function emitBrainTelemetry(t: BrainTelemetry): void;
81
81
  * belong to ONE outgoing request — the output cap that actually reached the wire and whether it was a
82
82
  * HARD bound. The runner's per-leg eager mint cannot know them without re-deriving the adapter's own
83
83
  * cap precedence chain (engine override > caller `options.maxTokens` > `model.maxTokens` >
84
- * brain-construction default), i.e. without standing up a SECOND source of truth that a deployment's
85
- * own brain, a construction-time default, or a per-attempt engine override would silently desync. So
86
- * the MINTER reports the facts it judged — the same law `SSERequest.outputCapTokens` already states
87
- * for the overflow recovery ("what the adapter actually put on the wire", never what a consumer
88
- * infers) — and the resolver stays the single predicate both faces read.
84
+ * brain-construction default) — a SECOND source of truth that a deployment's own brain, a
85
+ * construction-time default or a per-attempt engine override would silently desync. So the MINTER
86
+ * reports the facts it judged — the law `SSERequest.outputCapTokens` already states for the overflow
87
+ * recovery ("what the adapter actually put on the wire") — and the resolver stays one predicate.
89
88
  *
90
- * REVOKED ON SETTLEMENT, unlike its two siblings, because this sink WRITES to a seat that is later
91
- * read into a returned value instead of pushing an advisory frame. `AsyncLocalStorage.run` does not
92
- * revoke the store from async resources created inside `fn`: a brain call the engine ABANDONED (the
93
- * brain-call guardrail's whole purpose) keeps the scope alive in its own continuation and can call
94
- * `observe` long after the task settled — mutating a resolution whose `TaskResult` has already been
95
- * assembled and handed back, and emitting a trace correction after the task's terminal. The scope
96
- * therefore carries a liveness flag cleared when `fn`'s promise settles, which is also the "freeze
97
- * before result assembly" boundary: assembly runs after the brain-driving call resolves, so a report
98
- * that could still land is exactly one that arrives before the freeze.
89
+ * REVOKED ON SETTLEMENT, unlike its two siblings, because this sink WRITES to a seat later read into
90
+ * a returned value instead of pushing an advisory frame. `AsyncLocalStorage.run` does not revoke the
91
+ * store from async resources created inside `fn`: a brain call the engine ABANDONED (the guardrail's
92
+ * whole purpose) keeps the scope alive in its own continuation and can call `observe` long after the
93
+ * task settled — mutating a resolution whose `TaskResult` was already assembled and handed back. The
94
+ * scope therefore carries a liveness flag cleared when `fn`'s promise settles, which is also the
95
+ * "freeze before result assembly" boundary: assembly runs after the brain-driving call resolves, so
96
+ * a report that could still land is exactly one that arrives before the freeze.
99
97
  *
100
98
  * Passing a NO-OP `observe` is the documented way to SHIELD a nested internal brain call (compaction
101
99
  * summary, side query): it installs a fresh innermost scope for the duration, so the inner call's
@@ -4,22 +4,19 @@ import type { AssistantMessage } from "../internal/llm.js";
4
4
  * turn's human-readable `errorMessage` against an exported sentinel string.
5
5
  *
6
6
  * Why this file exists at all. The cut is a cross-LAYER signal: a brain stamps it, the loop's
7
- * recovery lane reacts, and the assembler maps it to a terminal code. Before
8
- * RB-464 the only carrier was the sentinel prose, compared by identity at six sites in two files.
9
- * Identity-comparing an exported constant is much safer than matching a prose fragment — the value
10
- * can be reworded freely and every site follows — but it still makes the CAUSE a string: a brain
11
- * that stamps the same sentence without importing the constant (a custom `Brain`, a message rebuilt
12
- * from a field whitelist, a re-encode through a host that normalizes whitespace) is invisible to all
13
- * six sites at once, silently, with every test still green because the tests import the constant too.
14
- * `errorKind` carries the cause as data instead; these predicates are the single place that knows how
15
- * to read it.
7
+ * recovery lane reacts, and the assembler maps it to a terminal code. Carrying it as sentinel prose
8
+ * — even an exported constant compared by identity, which at least reworders freely — makes the
9
+ * CAUSE a string: a brain that stamps the same sentence without importing the constant (a custom
10
+ * `Brain`, a message rebuilt from a field whitelist, a re-encode through a host that normalizes
11
+ * whitespace) is invisible to every reading site at once, silently, with the tests still green
12
+ * because they import the constant too. `errorKind` carries the cause as data instead; these
13
+ * predicates are the single place that knows how to read it.
16
14
  *
17
- * RB-482 #12 (ruled 2026-08-03, the RB-464 retirement condition met at this window): the transitional
18
- * `errorMessage`-sentinel fallback is DELETED — `errorKind` is the ONE read. The two shapes the
19
- * fallback served are closed the documented way: pre-RB-464 stored turns are the sacrificed-replay
20
- * class (RB-476-A precedent), and stamping `errorKind` on every cut turn is now a documented Brain
21
- * obligation (see the field's contract note on AssistantMessage). A custom brain that stamps only the
22
- * sentinel prose is no longer recognized — declared in the 5.1.0 CHANGELOG.
15
+ * There is no `errorMessage`-sentinel fallback: `errorKind` is the ONE read. The two shapes such a
16
+ * fallback served are closed the documented way — older stored turns are the sacrificed-replay class,
17
+ * and stamping `errorKind` on every cut turn is a documented Brain obligation (see the field's
18
+ * contract note on AssistantMessage). A custom brain that stamps only the sentinel prose is not
19
+ * recognized; the retirement was declared in the 5.1.0 CHANGELOG.
23
20
  */
24
21
  /** The subset of an assistant turn that carries a terminal cause. Accepts a full {@link AssistantMessage}. */
25
22
  export type TerminalCauseCarrier = Pick<AssistantMessage, "errorKind">;
@@ -29,26 +29,25 @@ export declare const MAX_TIMER_DELAY_MS = 2147483647;
29
29
  export declare function resolveStallTimeoutMs(value: number | undefined, knob: string): number | undefined;
30
30
  /**
31
31
  * The engine's ADVISORY per-call stall watchdog values (`StreamOptions.stallTimeouts`), supplied on
32
- * every provider call. They are a GUARDRAIL, not part of any budget lane: TB2.0 run89's production
33
- * frame had an in-flight model call sit 680s with ZERO deltas, and `BrainTimeoutConfig` defaults
34
- * every watchdog to OFF, so nothing finer than the hour-scale {@link BRAIN_CALL_GUARDRAIL_DEFAULT_MS}
35
- * bounded it. A silent stream is never worth more than these bounds, whether or not the task declared
36
- * a time limit. All three surface as the brain's existing retryable `network` stall class (design/124
37
- * tier C retries a zero-content cut, so a transient gateway hang still recovers). 量纲:
32
+ * every provider call. They are a GUARDRAIL, not part of any budget lane: `BrainTimeoutConfig` defaults
33
+ * every watchdog to OFF, so without these an in-flight call that goes silent is bounded by nothing
34
+ * finer than the hour-scale {@link BRAIN_CALL_GUARDRAIL_DEFAULT_MS} (a measured production frame sat
35
+ * 680s with ZERO deltas). A silent stream is never worth more than these bounds, whether or not the
36
+ * task declared a time limit. All three surface as the brain's existing retryable `network` stall
37
+ * class (design/124 tier C retries a zero-content cut, so a transient gateway hang still recovers). 量纲:
38
38
  * - connect 30s: bounds the fetch→response-HEADERS wait — the one segment every post-headers
39
39
  * watchdog is blind to. On a streaming API the model's time is spent AFTER headers, so 30s with
40
40
  * none at all is a hung gateway, not a slow model.
41
41
  * - first-token 600s: bounds the headers→first-delta wait, NOT the model's thought (the first
42
42
  * THINKING delta already counts as the first token, design/17 O5 — see
43
- * {@link BrainTimeoutConfig.firstTokenTimeoutMs}), so a slow reasoner is safe. The old 120s rested
44
- * on "2 minutes with literally NOTHING is a hung gateway", which is FALSE for a real class of
45
- * backend: a self-hosted server working through a long prefill emits nothing at all for 2–3
46
- * minutes, and the watchdog cancelled it — burning the prefill and re-sending a request that then
47
- * burned it again. A guardrail whose reason is false for healthy traffic is not a guardrail. 600s
48
- * is the same order of magnitude as the reference implementation's own request timeout, and the
49
- * shapes this bound exists for (a gateway that accepted the request and then died) are still
50
- * caught, just later; the task's own hard abort remains the outer bound for everything else.
51
- * HONEST CEILING (measured, adversarial round): this watchdog is not the only clock over a
43
+ * {@link BrainTimeoutConfig.firstTokenTimeoutMs}), so a slow reasoner is safe. A minutes-scale
44
+ * bound would be FALSE for a real class of backend: a self-hosted server working through a long
45
+ * prefill emits nothing at all for 2–3 minutes, and cancelling it burns the prefill and re-sends
46
+ * a request that burns it again — a guardrail whose reason is false for healthy traffic is not a
47
+ * guardrail. 600s is the same order of magnitude as the reference implementation's own request
48
+ * timeout, and the shapes this bound exists for (a gateway that accepted the request and then
49
+ * died) are still caught, just later; the task's own hard abort remains the outer bound.
50
+ * HONEST CEILING (measured): this watchdog is not the only clock over a
52
51
  * byte-silent stream, so 600s is the bound this engine ASKS FOR, not the silence a deployment is
53
52
  * necessarily granted. On Node's own `fetch` — the default transport for both adapters, and the
54
53
  * one a deployment gets by supplying no `fetchImpl` — undici destroys a body that has produced no
@@ -58,10 +57,9 @@ export declare function resolveStallTimeoutMs(value: number | undefined, knob: s
58
57
  * takes any `fetch`, so a deployment needing the full 600s passes one built on an undici
59
58
  * `Agent`/`Dispatcher` with its own `bodyTimeout`. Named here so the next reader who measures ~300s
60
59
  * of tolerance does not conclude this number is a lie.
61
- * The OTHER clock that used to sit below this one is fixed rather than disclosed:
62
- * {@link import("../orchestration/workflow.js").WORKFLOW_AGENT_STALL_MS} is now DERIVED from
63
- * {@link STALL_CONNECT_MS} + this value, so the coarse agent-restart no longer sits UNDER a single
64
- * silent provider call by construction. Move this number and that one follows. It is a floor, not
60
+ * {@link import("../orchestration/workflow.js").WORKFLOW_AGENT_STALL_MS} is DERIVED from
61
+ * {@link STALL_CONNECT_MS} + this value, so the coarse agent-restart cannot sit UNDER a single
62
+ * silent provider call. Move this number and that one follows. It is a floor, not
65
63
  * a guarantee of ordering — see that constant's own comment for the cases that still invert.
66
64
  * - idle 90s: mid-stream silence after output started — a healthy stream emits steadily
67
65
  * (comfortably above any provider keep-alive cadence).
@@ -537,21 +537,17 @@ export declare function productAddPatch(product: MemoryEntry): NotePatch;
537
537
  * - `driver_failed` — the DRIVER half could not complete. Two grains, one verdict: a failed
538
538
  * MINT (model unreachable/unreadable/over the repair budget) settles with
539
539
  * zero writes, zero seat and an unburned time gate; a run whose surviving
540
- * plan reached its fixpoint while the driver's own half left groups
541
- * unfolded (writing-pass failures, or products dropped stale against
542
- * inputs edited mid-run) settles here TOO — committed cycles stand
543
- * (add-only, no rollback), the residue is named, and a fresh run re-mints
544
- * over the current bytes. The clean `converged` label never covers a
545
- * driver-side residue;
546
- * - `mint_invalidated` — design/376-C1 D-1b (driver layer): the mint's served-world ROSTER
547
- * failed the cross-cycle re-check — a served candidate moved rev, flipped
548
- * its marked flag, or left the candidate set without a fold this run's own
549
- * plans attest. The cached plan is discarded (its prompts were composed
550
- * over a world that no longer stands — replaying it could publish clean
551
- * products whose authoring chain silently absorbed the moved row's
552
- * influence); committed cycles stand (add-only, no rollback), and the host
553
- * re-runs to re-mint over the current bytes (immediately or next window —
554
- * host policy).
540
+ * plan reached its fixpoint while the driver left groups unfolded (writing
541
+ * failures, or products dropped stale against inputs edited mid-run) settles
542
+ * here TOO — committed cycles stand (add-only), the residue is named, and a
543
+ * fresh run re-mints over current bytes. `converged` never covers a residue;
544
+ * - `mint_invalidated` — design/376-C1 D-1b: the mint's served-world ROSTER failed the
545
+ * cross-cycle re-check — a served candidate moved rev, flipped its marked
546
+ * flag, or left the candidate set without a fold this run's own plans
547
+ * attest. The cached plan is discarded (its prompts were composed over a
548
+ * world that no longer stands, and replaying it could publish clean products
549
+ * whose authoring chain absorbed the moved row's influence); committed
550
+ * cycles stand, and the host re-runs to re-mint over the current bytes.
555
551
  * - `purity_violation` — design/383 §1.3 (S-1): the minted plan carries a product that will
556
552
  * commit UNMARKED while containing bytes only the MARKED arm was shown. The
557
553
  * exposure partition's isolation premise is falsified, so the whole mint is