@sema-agent/core 7.26.3 → 7.28.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.
- package/CHANGELOG.md +59 -0
- package/dist/agents/builtin-agents.d.ts +6 -15
- package/dist/agents/cascade.d.ts +29 -33
- package/dist/agents/observer.d.ts +18 -20
- package/dist/agents/subagent.d.ts +58 -70
- package/dist/agents/tool-filter.d.ts +10 -12
- package/dist/agents/verify.d.ts +44 -49
- package/dist/brain/input-too-long.d.ts +10 -12
- package/dist/brain/media-degrade.d.ts +4 -5
- package/dist/brain/reasoning.d.ts +10 -12
- package/dist/brain/repetition.d.ts +7 -9
- package/dist/brain/request-params.d.ts +20 -25
- package/dist/brain/route-adjudicator.d.ts +4 -4
- package/dist/brain/route-conformance.d.ts +2 -2
- package/dist/brain/status-sink.d.ts +12 -14
- package/dist/brain/terminal-cause.d.ts +12 -15
- package/dist/brain/timeout.d.ts +17 -19
- package/dist/core/auto-mode-directive.d.ts +6 -4
- package/dist/core/auto-mode-directive.js +1 -5
- package/dist/core/engine-notice.d.ts +13 -6
- package/dist/core/memory-engine/consolidation.d.ts +11 -15
- package/dist/core/memory-engine/engine.d.ts +45 -54
- package/dist/core/memory-engine/tools.d.ts +17 -23
- package/dist/core/permission-rule-model.js +5 -3
- package/dist/core/runner/contracts.d.ts +31 -7
- package/dist/core/runner/denial-limit-arms.d.ts +17 -15
- package/dist/core/runner/denial-limit-arms.js +4 -1
- package/dist/core/runner/prepare-gate-stations.d.ts +1 -2
- package/dist/core/runner/prepare-inherited-gate.d.ts +1 -2
- package/dist/core/runner/prepare-policy-chain.d.ts +1 -2
- package/dist/core/runner/prepare-task.js +1 -1
- package/dist/core/runner/run-leg.js +5 -1
- package/dist/core/tool-policy.d.ts +23 -2
- package/dist/core/tool-policy.js +106 -103
- package/dist/index.d.ts +6 -1
- package/dist/shell/lex.d.ts +279 -0
- package/dist/shell/lex.js +1253 -0
- package/dist/stores/file/background-agent-store.d.ts +4 -0
- package/dist/stores/file/background-agent-store.js +10 -0
- package/dist/stores/file/checkpoint-store.d.ts +3 -0
- package/dist/stores/file/checkpoint-store.js +18 -0
- package/dist/stores/file/corrupt-read-sink.d.ts +20 -0
- package/dist/stores/file/corrupt-read-sink.js +15 -0
- package/dist/stores/file/file-history-store.d.ts +1 -2
- package/dist/stores/file/file-history-store.js +3 -8
- package/dist/stores/file/fs-atomic.d.ts +8 -3
- package/dist/stores/file/fs-atomic.js +19 -2
- package/dist/stores/file/index.js +4 -4
- package/dist/stores/file/mailbox-store.d.ts +3 -0
- package/dist/stores/file/mailbox-store.js +24 -18
- package/dist/stores/file/permission-rule-store.js +18 -1
- package/dist/stores/file/session-policy-store.d.ts +17 -1
- package/dist/stores/file/session-policy-store.js +3 -12
- package/dist/stores/file/session-store.d.ts +1 -5
- package/dist/stores/file/session-store.js +9 -14
- package/dist/stores/file/shared-ledger.d.ts +18 -0
- package/dist/stores/file/shared-ledger.js +16 -2
- package/dist/stores/file/tool-result-store.d.ts +9 -1
- package/dist/stores/file/tool-result-store.js +17 -2
- package/dist/stores/file/workflow-journal-store.d.ts +10 -1
- package/dist/stores/file/workflow-journal-store.js +8 -12
- package/dist/stores/file/workflow-run-store.d.ts +3 -0
- package/dist/stores/file/workflow-run-store.js +10 -0
- package/dist/tools/artifact/local-stub.js +16 -1
- package/dist/tools/fs/bash-program-position.d.ts +2 -4
- package/dist/tools/fs/bash-program-position.js +2 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +2 -2
- package/dist/tools/fs/bash-readonly-classifier.js +1 -1
- package/dist/tools/fs/shell-read-check.js +1 -1
- package/dist/tools/fs/shell-read-establish.js +11 -1
- package/dist/tools/fs/shell-read-plan.d.ts +7 -2
- package/dist/tools/fs/shell-read-plan.js +48 -10
- package/package.json +2 -2
- package/test/export-surface.snapshot.json +3 -1
- package/wire-changes/core@7.27.0.json +152 -0
- package/wire-changes/core@7.28.0.json +594 -0
- package/dist/tools/fs/bash-lexer.d.ts +0 -152
- package/dist/tools/fs/bash-lexer.js +0 -627
package/dist/agents/verify.d.ts
CHANGED
|
@@ -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;
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
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
|
|
248
|
-
* with the one placement-axis exception stated where the shape is named in
|
|
249
|
-
* each
|
|
250
|
-
* {@link
|
|
251
|
-
*
|
|
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 → …)
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
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
|
|
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
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
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
|
|
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.
|
|
287
|
-
* leg
|
|
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
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
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"
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
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
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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`)
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* gradient
|
|
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.
|
|
241
|
-
*
|
|
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.
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
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
|
|
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:
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
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
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
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
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
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.
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
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
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* user bag carrying `Content-Type: text/plain`
|
|
76
|
-
* `Headers` fold
|
|
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
|
|
6
|
-
*
|
|
7
|
-
* entry
|
|
8
|
-
*
|
|
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
|
|
14
|
-
*
|
|
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)
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
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
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
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.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
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">;
|
package/dist/brain/timeout.d.ts
CHANGED
|
@@ -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:
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* a time limit. All three surface as the brain's existing retryable `network` stall
|
|
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.
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
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
|
-
*
|
|
62
|
-
* {@link
|
|
63
|
-
*
|
|
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).
|
|
@@ -29,16 +29,18 @@ export interface ApprovalPostureFacts {
|
|
|
29
29
|
approverPosture?: ApproverPosture;
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
|
-
* The ONE composition, and the ONE invariant: **
|
|
32
|
+
* The ONE composition, and the ONE invariant: **the segment the run's posture calls for is stated
|
|
33
33
|
* exactly once in the active model context**. `alreadyStated` is the transcript's ENGINE REGIONS (read
|
|
34
34
|
* by metadata — a caller's own bytes are never in one, so a spliced copy cannot suppress the engine's);
|
|
35
35
|
* a segment already standing there is not restated, and a segment that has left the context — compacted
|
|
36
36
|
* away, or never delivered — is. Returns `undefined` when there is nothing left to say, which is both
|
|
37
37
|
* "no posture declared" (zero bytes, byte-identical to the pre-seat shape) and "already said".
|
|
38
38
|
*
|
|
39
|
-
* Segment-wise rather than body-wise on purpose: a leg
|
|
40
|
-
*
|
|
41
|
-
*
|
|
39
|
+
* Segment-wise rather than body-wise on purpose: a leg whose posture CHANGES — it gains the bypass fact,
|
|
40
|
+
* so the segment it owes changes — must state the new segment without restating one already standing,
|
|
41
|
+
* and must be able to read a body an earlier leg minted under a composition that joined two. The caller
|
|
42
|
+
* wraps the result in the engine's marked reminder shell; this module never wraps, so the mark stays
|
|
43
|
+
* owned by its own mint home.
|
|
42
44
|
*/
|
|
43
45
|
export declare function composeApprovalPostureDirective(facts: ApprovalPostureFacts, statedBodies?: readonly string[]): string | undefined;
|
|
44
46
|
/** SHA-256 of a directive body — the pins' lock port, so the lock is computed the one way. */
|
|
@@ -15,11 +15,7 @@ export function refuseInvalidApproverPosture(spec) {
|
|
|
15
15
|
return spec.approverPosture;
|
|
16
16
|
}
|
|
17
17
|
export function composeApprovalPostureDirective(facts, statedBodies = []) {
|
|
18
|
-
const parts = [];
|
|
19
|
-
if (facts.autoMode)
|
|
20
|
-
parts.push(AUTO_MODE_DIRECTIVE);
|
|
21
|
-
if (facts.approverPosture === "bypass")
|
|
22
|
-
parts.push(BYPASS_POSTURE_DIRECTIVE);
|
|
18
|
+
const parts = facts.approverPosture === "bypass" ? [BYPASS_POSTURE_DIRECTIVE] : facts.autoMode ? [AUTO_MODE_DIRECTIVE] : [];
|
|
23
19
|
if (parts.length === 0)
|
|
24
20
|
return undefined;
|
|
25
21
|
const stated = new Set();
|
|
@@ -392,12 +392,19 @@ export interface EngineNotice {
|
|
|
392
392
|
*
|
|
393
393
|
* - `"classifier.denial_limit"` (#548, CC 2.1.250 `FO`/"too many classifier denials in headless
|
|
394
394
|
* mode") — the auto-mode classifier's DENIAL LIMIT was reached (3 consecutive blocks, or 20 in the
|
|
395
|
-
* run; `RunnerDeps.autoMode.denialLimit`) and the fallback ask it turns into
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
-
*
|
|
399
|
-
*
|
|
400
|
-
*
|
|
395
|
+
* run; `RunnerDeps.autoMode.denialLimit`) and the fallback ask it turns into was never JUDGED by
|
|
396
|
+
* anyone. Two lanes reach it. SYNCHRONOUS: no approver to go to — none wired, a blanket
|
|
397
|
+
* `onAsk:"allow"` (refused for a real-approval ask), or an approver that reported unavailable with
|
|
398
|
+
* no durable park to take it. DURABLE: the fallback PARKED, and the decide that resolved the row
|
|
399
|
+
* reported a settlement the nobody-judged table reads as such (`hostDecision.decidedBy:"policy"` ⇒
|
|
400
|
+
* `policy_refused`) — a machine answered where the bound had asked for a person, so the last resort
|
|
401
|
+
* is the same one. A person's refusal (`"person"` ⇒ `human_refused`) is NOT this notice on either
|
|
402
|
+
* lane: someone looked. The run was STOPPED — a `failed` terminal, `code` the same code, `message`
|
|
403
|
+
* the limit sentence — rather than kept spending on a model the classifier denies without end; the
|
|
404
|
+
* triggering call's own result is the deny that stood. The two lanes' sentences differ in ONE
|
|
405
|
+
* clause (which of the two ways nobody answered), and the durable one states the counts the PARKED
|
|
406
|
+
* row carried, so a resume in a fresh process reports the streak that actually happened. Once per
|
|
407
|
+
* run leg (the first such fallback owns the terminal);
|
|
401
408
|
* `detail: { sessionId, runId, toolName, toolCallId, consecutive, total, limit }` — `limit` is
|
|
402
409
|
* `"consecutive"` | `"total"`, the bound that tripped. Audience `"user"` (the person whose run
|
|
403
410
|
* ended is the one entitled to hear why, and to review the transcript the sentence points at).
|