@warmdrift/kgauto-compiler 2.0.0-alpha.9 → 2.0.0-alpha.90

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 (55) hide show
  1. package/README.md +176 -46
  2. package/dist/brain-proxy.d.mts +113 -0
  3. package/dist/brain-proxy.d.ts +113 -0
  4. package/dist/brain-proxy.js +193 -0
  5. package/dist/brain-proxy.mjs +6 -0
  6. package/dist/chunk-4LYNDEHJ.mjs +219 -0
  7. package/dist/chunk-65ZMX5OT.mjs +169 -0
  8. package/dist/{chunk-5TI6PNSK.mjs → chunk-BVEXV5KC.mjs} +11 -0
  9. package/dist/chunk-ENELVMJI.mjs +858 -0
  10. package/dist/chunk-NBO4R5PC.mjs +313 -0
  11. package/dist/chunk-OK2TMFRR.mjs +1879 -0
  12. package/dist/chunk-P3TOAEG4.mjs +56 -0
  13. package/dist/chunk-RO22VFIF.mjs +29 -0
  14. package/dist/dialect.d.mts +41 -3
  15. package/dist/dialect.d.ts +41 -3
  16. package/dist/dialect.js +14 -2
  17. package/dist/dialect.mjs +5 -3
  18. package/dist/glassbox/index.d.mts +59 -0
  19. package/dist/glassbox/index.d.ts +59 -0
  20. package/dist/glassbox/index.js +312 -0
  21. package/dist/glassbox/index.mjs +12 -0
  22. package/dist/glassbox-routes/format.d.mts +24 -0
  23. package/dist/glassbox-routes/format.d.ts +24 -0
  24. package/dist/glassbox-routes/format.js +86 -0
  25. package/dist/glassbox-routes/format.mjs +18 -0
  26. package/dist/glassbox-routes/index.d.mts +191 -0
  27. package/dist/glassbox-routes/index.d.ts +191 -0
  28. package/dist/glassbox-routes/index.js +3197 -0
  29. package/dist/glassbox-routes/index.mjs +668 -0
  30. package/dist/glassbox-routes/react/index.d.mts +74 -0
  31. package/dist/glassbox-routes/react/index.d.ts +74 -0
  32. package/dist/glassbox-routes/react/index.js +819 -0
  33. package/dist/glassbox-routes/react/index.mjs +754 -0
  34. package/dist/index.d.mts +3782 -99
  35. package/dist/index.d.ts +3782 -99
  36. package/dist/index.js +10835 -2046
  37. package/dist/index.mjs +6270 -276
  38. package/dist/ir-CTx026t0.d.ts +1887 -0
  39. package/dist/ir-DeYMLWge.d.mts +1887 -0
  40. package/dist/key-health.d.mts +166 -0
  41. package/dist/key-health.d.ts +166 -0
  42. package/dist/key-health.js +247 -0
  43. package/dist/key-health.mjs +12 -0
  44. package/dist/profiles.d.mts +352 -2
  45. package/dist/profiles.d.ts +352 -2
  46. package/dist/profiles.js +1282 -51
  47. package/dist/profiles.mjs +19 -1
  48. package/dist/types-BKbRtmUb.d.ts +131 -0
  49. package/dist/types-Cp9ot1HV.d.ts +142 -0
  50. package/dist/types-DD36cCbZ.d.mts +142 -0
  51. package/dist/types-cBzinzUR.d.mts +131 -0
  52. package/package.json +62 -9
  53. package/dist/chunk-3KVKELZN.mjs +0 -657
  54. package/dist/profiles-BYVOc1eW.d.ts +0 -700
  55. package/dist/profiles-NUZOIzGr.d.mts +0 -700
@@ -0,0 +1,1887 @@
1
+ import { IntentArchetypeName, OutputMode } from './dialect.mjs';
2
+
3
+ /**
4
+ * Golden-set capture (alpha.62, eval spine — design brief 2026-07-17).
5
+ *
6
+ * The eval-driven self-improvement contract needs a replay corpus: 20–50 real
7
+ * historical IRs per (app, archetype), stored WITH the incumbent's output.
8
+ * This module is the capture side — a sampling gate on call()'s success path
9
+ * that stores the full PromptIR + served response to `kgauto_golden_irs`.
10
+ *
11
+ * ## Consent posture (s51 amendment, ratified 2026-07-17)
12
+ *
13
+ * Golden rows carry RAW prompts — a deliberate, documented departure from the
14
+ * s51 "brain holds no raw consumer prompts" rule. Capture is therefore
15
+ * strictly opt-in:
16
+ *
17
+ * - `KGAUTO_GOLDEN_CAPTURE=0.1` in the consumer's environment (the env var
18
+ * IS the consent act — the consumer's operator sets it knowingly), or
19
+ * - `CallOptions.goldenCapture.sampleRate` in consumer code.
20
+ *
21
+ * Default is 0 (off): absent flag, empty string, non-finite, or ≤0 all mean
22
+ * "never capture". Every stored row records its consent provenance in the
23
+ * `consent` column. Reads are strictly app-scoped (RLS) — a consumer can only
24
+ * ever see its own raw prompts.
25
+ */
26
+
27
+ /** Consumer-facing capture options (CallOptions.goldenCapture). */
28
+ interface GoldenCaptureOptions {
29
+ /**
30
+ * Probability [0..1] that a successful call's IR + output is stored as a
31
+ * golden row. Overrides the KGAUTO_GOLDEN_CAPTURE env var when set.
32
+ */
33
+ sampleRate?: number;
34
+ /**
35
+ * Optional free-text appended to the stored consent provenance (e.g. a
36
+ * ticket ref or the operator who approved capture for this surface).
37
+ */
38
+ consentNote?: string;
39
+ }
40
+ /**
41
+ * Parse a capture-rate env value. Mirrors the consumer-side
42
+ * `parseShadowProbeRate` semantics playbacksam established: undefined, empty,
43
+ * or non-finite → 0 (default-OFF); result clamped to [0, 1].
44
+ */
45
+ declare function parseGoldenCaptureRate(raw: string | undefined): number;
46
+ /**
47
+ * Resolve the effective capture rate: explicit option wins; otherwise the
48
+ * KGAUTO_GOLDEN_CAPTURE env var (when a process env exists — Edge-safe).
49
+ */
50
+ declare function resolveGoldenCaptureRate(optRate?: number): number;
51
+ /** Sampling gate. Injectable rng for tests (same shape as shouldSampleProbe). */
52
+ declare function shouldCaptureGolden(rate: number, rng?: () => number): boolean;
53
+ /** Everything call()'s success path has in scope when capture fires. */
54
+ interface GoldenCaptureContext {
55
+ ir: PromptIR;
56
+ servedModel: string;
57
+ response: NormalizedResponse;
58
+ latencyMs: number;
59
+ handle: string;
60
+ /** Compile-time shape key (peeked before record() popped the registry). */
61
+ shapeKey?: string;
62
+ /** How the rate was resolved — recorded as consent provenance. */
63
+ consent: string;
64
+ }
65
+ /**
66
+ * Store one golden row from a live served call. Fire-and-forget discipline is
67
+ * recordGoldenIr()'s (dead-letter, ledger, honors BrainConfig.sync); this
68
+ * wrapper only shapes the row. Never throws.
69
+ */
70
+ declare function captureGoldenIr(ctx: GoldenCaptureContext): Promise<void>;
71
+
72
+ /**
73
+ * A semantically-named section of the system prompt. Sections enable
74
+ * intent-aware slicing (drop sections not tagged for this intent), dedupe
75
+ * (collapse identical sections across files), and cache marking (identify
76
+ * the stable prefix).
77
+ */
78
+ interface PromptSection {
79
+ /** Stable identifier — used for slicing, dedupe, and cache markers. */
80
+ id: string;
81
+ /** Section text. */
82
+ text: string;
83
+ /**
84
+ * Which intents this section applies to. Empty = applies to all intents.
85
+ * Pass `compile()` will drop sections whose intents array doesn't include
86
+ * the current intent.
87
+ */
88
+ intents?: IntentArchetypeName[];
89
+ /**
90
+ * If true, this section is part of the stable cacheable prefix. The lower
91
+ * pass uses this to place cache markers correctly per target.
92
+ */
93
+ cacheable?: boolean;
94
+ /**
95
+ * Section weight when ordering — lower = earlier in the assembled prompt.
96
+ * Defaults to insertion order.
97
+ */
98
+ weight?: number;
99
+ /**
100
+ * alpha.29+ — declares the section's semantic kind so kgauto can apply
101
+ * model-aware rewrites at compile time. Default `'arbitrary'` (when
102
+ * unset) for full back-compat — pre-alpha.29 sections continue working
103
+ * unchanged.
104
+ *
105
+ * alpha.29 ships rewrites for `tool_call_contract` only. Other kinds are
106
+ * type-accepted but pass through. alpha.30+ will add rewrites for
107
+ * `narration_contract`, `role_intro`, etc.
108
+ *
109
+ * See `translator.ts` for the rewrite engine that consumes this field.
110
+ */
111
+ kind?: SectionKind;
112
+ }
113
+ /**
114
+ * alpha.29+ — semantic kind tag for a `PromptSection`. The translator
115
+ * (`v2/src/translator.ts`) consumes this to apply model-aware rewrites at
116
+ * compile time. CLOSED union; future kinds extend it explicitly in named
117
+ * alpha releases.
118
+ *
119
+ * alpha.29 ships rewrites for `tool_call_contract` only. Other kinds are
120
+ * type-accepted but pass through.
121
+ *
122
+ * - `role_intro` — "You are a helpful assistant", persona blocks
123
+ * - `tool_call_contract` — tool-use rules ("call X then Y"); the alpha.29
124
+ * translator rewrites this for models with a
125
+ * sequential-tool cliff on the active archetype
126
+ * - `narration_contract` — output-format rules ("don't narrate your steps");
127
+ * alpha.30+ candidate
128
+ * - `discipline_contract`— alpha.68 / Release A (delegation-fanout-accelerator
129
+ * §5.D): surface-scaffolding gate block. When declared
130
+ * on a role/system section, the translator prepends the
131
+ * frozen `discipline-gates-v1` preamble subject to a
132
+ * two-factor eligibility screen — Factor A (archetype:
133
+ * hunt/summarize/plan/critique/judge) AND Factor C
134
+ * (output-shape: text only, never json/tool_call).
135
+ * Clears no cliff, emits no wireOverrides ⇒ no advisor
136
+ * suppression. See `translator.ts`.
137
+ * - `user_turn` — when sections carry user content rather than
138
+ * system context (rare)
139
+ * - `reference` — supporting reference data the model may consult
140
+ * - `arbitrary` — explicit pass-through (default when unset)
141
+ */
142
+ type SectionKind = 'role_intro' | 'tool_call_contract' | 'narration_contract' | 'discipline_contract' | 'user_turn' | 'reference' | 'arbitrary';
143
+ interface ToolDefinition {
144
+ name: string;
145
+ description?: string;
146
+ parameters?: Record<string, unknown>;
147
+ /**
148
+ * Per-intent relevance scores. Compile uses these to drop irrelevant tools.
149
+ * Missing intents default to 0.5 (neutral).
150
+ */
151
+ relevanceByIntent?: Partial<Record<IntentArchetypeName, number>>;
152
+ /** Pass-through for provider-specific fields (Anthropic input_schema, etc.). */
153
+ [key: string]: unknown;
154
+ }
155
+ interface Message {
156
+ role: 'system' | 'user' | 'assistant' | 'tool';
157
+ content: string;
158
+ /** Optional structured parts (tool calls, results) — passed through to lowering. */
159
+ parts?: unknown[];
160
+ /** For tool messages — which tool this corresponds to. */
161
+ toolName?: string;
162
+ /** For tool messages — the call id. */
163
+ toolCallId?: string;
164
+ }
165
+ /**
166
+ * The compile-time intent declaration. `name` is the app's local label;
167
+ * `archetype` is the canonical dialect-v1 archetype the app maps it to.
168
+ *
169
+ * Apps with their own intent vocabulary (tt-intelligence's "ask"/"hunt"/
170
+ * "dashboard") declare the mapping here. The brain learns by archetype, not
171
+ * by app-local name.
172
+ */
173
+ interface IntentDeclaration {
174
+ /** App-local intent name (free-form, for app's own debugging). */
175
+ name: string;
176
+ /** Canonical dialect-v1 archetype. Required for cross-app learning. */
177
+ archetype: IntentArchetypeName;
178
+ }
179
+ /**
180
+ * alpha.57 — dialect-level reasoning-effort tier (data-first).
181
+ *
182
+ * Vocabulary is a portfolio-level tier ladder (low → max), NOT any provider's
183
+ * wire value — mapping to `reasoning_effort` (OpenAI) / thinking budgets
184
+ * (Anthropic) / `thinkingConfig` (Gemini) stays consumer-side until
185
+ * routing-on-effort ships. Declaring it records the tier on the brain row
186
+ * (`compile_outcomes.effort`, migration 033) so effort×archetype quality
187
+ * evidence accumulates BEFORE any routing logic exists — same data-first
188
+ * sequencing as `tool_orchestration` (alpha.20).
189
+ */
190
+ type EffortLevel = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
191
+ interface Constraints {
192
+ /** Hard latency ceiling — compiler will down-rank slow models. Advisory. */
193
+ maxLatencyMs?: number;
194
+ /** Hard cost ceiling per call (USD). Advisory. */
195
+ maxCostUsd?: number;
196
+ /**
197
+ * alpha.57 (data-first): the reasoning-effort tier the consumer ran (or
198
+ * intends to run) this call at. RECORDED, NOT APPLIED — the compiler does
199
+ * not emit provider thinking/effort params from this field and does not
200
+ * route on it yet; it flows to `compile_outcomes.effort` via record()'s
201
+ * registry auto-enrich (same pattern as `mutationsApplied`/advisories).
202
+ * Declare it truthfully: this is consumer-reported ground truth, like
203
+ * `tokensIn`/`latencyMs`. Does NOT enter the shape key (no learning-key
204
+ * fragmentation).
205
+ */
206
+ effort?: EffortLevel;
207
+ /** Caller wants structured (JSON) output. */
208
+ structuredOutput?: boolean;
209
+ /**
210
+ * alpha.69 — consumer-declared output shape. Overrides the inference in
211
+ * `resolveOutputMode` (structuredOutput ⇒ json · tools ⇒ tool_call · else
212
+ * text), which is deliberately conservative: it cannot see that an agentic
213
+ * surface carrying tools ultimately emits prose to the user.
214
+ *
215
+ * Declaring `'text'` on a tool-carrying surface is what re-opens the
216
+ * alpha.68 discipline gates for it (Factor C is a hard screen on inferred
217
+ * `tool_call`, because wrongly firing breaks a parser while wrongly
218
+ * withholding only forgoes a lift). Declare it truthfully — it is
219
+ * consumer-reported ground truth about what the model emits, and it feeds
220
+ * `shape_key`, so a false declaration fragments the learning key AND lets
221
+ * shape-altering rules fire on a structured surface.
222
+ */
223
+ outputMode?: OutputMode;
224
+ /** Hint: caller expects a short response (used to disable thinking on Gemini). */
225
+ expectedShortOutput?: boolean;
226
+ /** Hint: max response words. */
227
+ maxResponseWords?: number;
228
+ /** Override target model selection — if set, compiler uses this instead of routing. */
229
+ forceModel?: string;
230
+ /**
231
+ * alpha.20: consumer-declared tool-orchestration shape for this call.
232
+ * - 'parallel': model may fire multiple tool calls per step (current
233
+ * default behavior; the L-040 cliff applies — DeepSeek's
234
+ * `tool_count >= 1` cliff trims tools because parallel-tool throughput
235
+ * collapses to sequential semantics).
236
+ * - 'sequential': consumer commits to one tool call per step (the agentic
237
+ * loop pattern). DeepSeek V4-Flash + V4-Pro can compete cleanly in
238
+ * this mode — the L-040 cliff is silenced and the hunt chain shifts
239
+ * to a DeepSeek-tier-1 ordering.
240
+ * - 'either': consumer doesn't care; library picks the parallel chain
241
+ * (status-quo default) and may upgrade to brain-driven per-mode perf
242
+ * selection in a future release.
243
+ *
244
+ * Affects:
245
+ * - Chain composition for `archetype: 'hunt'` (see
246
+ * `getDefaultFallbackChain` and `STARTER_CHAINS_BY_MODE`).
247
+ * - L-040 cliff in `passApplyCliffs` (silent when 'sequential').
248
+ *
249
+ * Default (when undefined): equivalent to 'parallel' for back-compat
250
+ * with every pre-alpha.20 caller.
251
+ */
252
+ toolOrchestration?: 'parallel' | 'sequential' | 'either';
253
+ }
254
+ /**
255
+ * Cache marker policy for the messages array (history + currentTurn).
256
+ *
257
+ * Anthropic positional caching: a `cache_control` marker on a content block
258
+ * tells the API "remember the prefix up through this block." On a subsequent
259
+ * request whose first N tokens match, those N billed at the cached rate
260
+ * (10% of the input price). Without a marker, every call re-pays for the
261
+ * entire history.
262
+ *
263
+ * - `'none'` (default when omitted): no history cache marker. System-level
264
+ * cache markers from `PromptSection.cacheable=true` still apply.
265
+ * - `'all-but-latest'`: marks the message immediately preceding `currentTurn`
266
+ * (the last history entry). On the next call, that entire history prefix
267
+ * is cacheable. Good fit for chat/agent loops where every prior turn is
268
+ * stable.
269
+ * - `'fixed-suffix'`: marks the message `suffix` positions from the end of
270
+ * `history`. Use when the last few turns are volatile (e.g., scratchpad,
271
+ * draft revisions) but the earlier prefix is stable.
272
+ *
273
+ * For non-Anthropic providers, no wire-format marker is emitted (Gemini /
274
+ * OpenAI / DeepSeek implicit caching takes effect automatically when a
275
+ * stable prefix is reused). The compiler still computes
276
+ * `diagnostics.historyCacheableTokens` for telemetry on every provider.
277
+ *
278
+ * alpha.5.
279
+ */
280
+ type HistoryCachePolicy = {
281
+ strategy: 'none';
282
+ } | {
283
+ strategy: 'all-but-latest';
284
+ } | {
285
+ strategy: 'fixed-suffix';
286
+ suffix: number;
287
+ };
288
+ /**
289
+ * Consumer-declared policy for model selection. Lives outside the IR
290
+ * (passed via CompileOptions) because it's a SESSION/APP-level constraint,
291
+ * not a per-call shape.
292
+ *
293
+ * The original tt-intelligence scenario (s11): user capped Anthropic
294
+ * spending on Sonnet for cost reasons. v2 compile() kept picking Sonnet
295
+ * as the best target, Hunter's preflight hit the cap and fell back to
296
+ * Flash — every single call. CompilePolicy.blockedModels lets the
297
+ * consumer tell kgauto "don't pick Sonnet right now" and the compiler
298
+ * routes to the next-best option directly. No wasted preflight tax.
299
+ *
300
+ * This is the "coach knows the constraints" feature — kgauto stops
301
+ * recommending things the consumer has already ruled out.
302
+ */
303
+ interface CompilePolicy {
304
+ /**
305
+ * Model IDs the consumer has gated. Compile() will never select these.
306
+ * Use for: cost caps, account-level rate limits, "this model is broken
307
+ * for our workload" decisions.
308
+ *
309
+ * Matching is by EXACT id after alias resolution (alpha.85): an alias
310
+ * and its canonical id are the same model, so blocking either blocks
311
+ * both (`deepseek-chat` ≡ `deepseek-v4-flash`). Matching does NOT widen
312
+ * to family siblings — blocking `deepseek-v4-flash` says nothing about
313
+ * `deepseek-v4-pro` — and a bare family key (`'deepseek'`,
314
+ * `'claude-sonnet'`) is INERT: it matches no model and no warning fires
315
+ * today. To gate a family, list its concrete ids. Beware roster
316
+ * retargets: when a family primary moves to a new id, a literal-id
317
+ * block does not follow it (tt-intel s113 built consumer-side family
318
+ * resolution for exactly this).
319
+ */
320
+ blockedModels?: string[];
321
+ /**
322
+ * Hard ceiling on estimated input cost per call (USD). Models whose
323
+ * estimated cost exceeds this are rejected. Use for: budget enforcement
324
+ * on high-volume routes.
325
+ */
326
+ maxCostPerCallUsd?: number;
327
+ /**
328
+ * alpha.68 / Release A (delegation-fanout-accelerator §5.1) — trace-level
329
+ * cost ceiling (USD) across a fan-out `trace_id`'s cumulative spend.
330
+ *
331
+ * RECORDED / INERT in Release A: nothing fans out yet, so this field is
332
+ * accepted and carried but the compiler enforces NOTHING from it. It is
333
+ * enforced by the `delegate` primitive against the running trace spend in
334
+ * Release D. Kept distinct from `maxCostPerCallUsd` because that per-call
335
+ * ceiling is trivially evadable N times under fan-out — a fan-out budget
336
+ * has to be trace-scoped, not per-call.
337
+ */
338
+ maxCostPerTraceUsd?: number;
339
+ /**
340
+ * Model IDs the consumer prefers. When multiple models fit, preferred
341
+ * models get a rank boost (large enough to overcome small quality
342
+ * differences but not large enough to override hard rejects).
343
+ */
344
+ preferredModels?: string[];
345
+ /**
346
+ * Customer-posture tag (master plan §1.2, alpha.9).
347
+ *
348
+ * - `'locked'` — compliance/contract/brand-promise. Caller passes
349
+ * exactly one model; no fallback is desired. kgauto
350
+ * never walks the chain.
351
+ * - `'preferred'` — user-selected primary, fallback chain as safety
352
+ * net. On 429/5xx, walk the chain and surface
353
+ * `fellOverFrom` so the consumer can show "Claude
354
+ * was busy; we used Pro for this answer."
355
+ * - `'open'` — library picks the chain. Model identity is
356
+ * irrelevant; output is the contract.
357
+ *
358
+ * The field is **informational** — kgauto's execution path is already
359
+ * determined by the shape of `ir.models`. Posture surfaces in
360
+ * telemetry so the cost-watcher can distinguish "locked failed, no
361
+ * fallback was tried" from "open chain exhausted." Default: when
362
+ * `ir.models.length === 1` posture is treated as `'locked'` by the
363
+ * advisor; otherwise unspecified.
364
+ */
365
+ posture?: 'locked' | 'preferred' | 'open';
366
+ /**
367
+ * alpha.87 — what compile() does with a model id no profile covers
368
+ * (gateway review §6; both cold readers hit this wall).
369
+ *
370
+ * - `'refuse'` (default) — throw, naming `registerProfiles()` and this
371
+ * option. The fleet's safety posture, unchanged: guards can only be
372
+ * honest about models kgauto knows.
373
+ * - `'best-effort'` — synthesize a conservative profile (provider
374
+ * inferred from the id prefix; wire mechanics borrowed from a bundled
375
+ * profile of that provider; NO cliffs, NO measured knowledge, cost
376
+ * UNKNOWN and recorded as 0) and compile anyway, with a loud
377
+ * `cliffWarnings` entry + `best_effort_profile` mutation naming
378
+ * every guard that is skipped. If the provider cannot be inferred,
379
+ * refusal still applies — best-effort cannot lower a wire format it
380
+ * cannot name.
381
+ *
382
+ * The option converts "wait for the maintainer's weekend" into a
383
+ * consumer's informed choice; it does not make the unknown model safe.
384
+ */
385
+ onUnprofiledModel?: 'refuse' | 'best-effort';
386
+ }
387
+ /**
388
+ * alpha.41 — entry in `PromptIR.models[]`. Either a literal model id (the
389
+ * pre-alpha.41 shape, fully preserved) or a `{ family: string }` alias that
390
+ * resolves at compile() time to the latest-current model in that family.
391
+ *
392
+ * Family entries resolve via `resolveFamilyEntry` (internal twin of the
393
+ * public `getRecommendedPrimary`). Resolution fails CLOSED — a family that
394
+ * matches no current+active candidate throws `FamilyResolutionError` at
395
+ * compile time. Consumers who want a literal fallback should call
396
+ * `getRecommendedPrimary({ family, fallback, ... })` at IR-construction time
397
+ * and inline the resolved id, rather than passing `{ family }` into the IR.
398
+ *
399
+ * Family taxonomy is defined in `family-resolution.ts`; see also migration
400
+ * 024 (`kgauto_models.family` column + index).
401
+ */
402
+ type ChainModelEntry = string | {
403
+ family: string;
404
+ };
405
+ /**
406
+ * The IR — the input to compile().
407
+ */
408
+ interface PromptIR {
409
+ /** App identifier — required for multi-tenant brain. */
410
+ appId: string;
411
+ /** Intent declaration — what is this call doing? */
412
+ intent: IntentDeclaration;
413
+ /** Structured system prompt sections. */
414
+ sections: PromptSection[];
415
+ /** Available tools (compiler may drop based on intent relevance + budget). */
416
+ tools?: ToolDefinition[];
417
+ /** Conversation history (compiler may compress old turns). */
418
+ history?: Message[];
419
+ /** The user's current turn — never dropped. */
420
+ currentTurn?: Message;
421
+ /**
422
+ * Allowed model IDs (or family aliases — alpha.41+), in caller-preference
423
+ * order. Compiler resolves family entries to literal ids before scoring
424
+ * and picks among them. See {@link ChainModelEntry}.
425
+ *
426
+ * Internal passes downstream of compile()'s family-resolution step
427
+ * receive a narrowed `string[]` shape — `string` is a subtype of
428
+ * `ChainModelEntry`, so a `string[]` IS a `ChainModelEntry[]` at the
429
+ * type level. Code that needs to access string ids (passes.ts) reads
430
+ * via `getModelIds(ir)` from `models-runtime.ts` rather than narrowing
431
+ * inline.
432
+ */
433
+ models: ChainModelEntry[];
434
+ /** Compile constraints. */
435
+ constraints?: Constraints;
436
+ /**
437
+ * Cache marker placement policy for the messages array. Default = no
438
+ * history cache markers. See `HistoryCachePolicy` for semantics.
439
+ * alpha.5.
440
+ */
441
+ historyCachePolicy?: HistoryCachePolicy;
442
+ }
443
+ type Provider = 'anthropic' | 'google' | 'openai' | 'deepseek' | 'zai' | 'moonshot' | 'mistral' | 'xai';
444
+ /**
445
+ * Mutation IDs that fired during compile. Empty in v1 (no mutation engine
446
+ * yet). Populated when the brain is online and pushing mutations.
447
+ */
448
+ type MutationApplied = {
449
+ id: string;
450
+ source: string;
451
+ passName: string;
452
+ description: string;
453
+ /**
454
+ * alpha.73 — the ranking delta this mutation applied, when it applied one.
455
+ * Negative de-ranks, positive boosts. Absent for mutations that do not touch
456
+ * rank (advisory/labelling mutations).
457
+ *
458
+ * Exists because a consumer could previously see *that* a gate fired and
459
+ * *why*, but not *how much* — which cannot distinguish "the gate did not see
460
+ * this model" from "the gate saw it and the penalty lost to a price
461
+ * advantage." PB spent two days and three wrong conclusions inside that gap
462
+ * (2026-07-25/26), and both wrong conclusions were inferences substituted
463
+ * for a quantity that was known at emit time and thrown away. Same class as
464
+ * the `rankedOn` request: a consumer cannot reason about ranking behaviour
465
+ * it cannot observe, so it will guess.
466
+ */
467
+ rankDelta?: number;
468
+ /**
469
+ * alpha.73 — the model's rank before and after this mutation, when it
470
+ * changed one. Together with `rankDelta` this answers the question a
471
+ * penalty magnitude alone cannot: whether the gated model still WON.
472
+ */
473
+ rankBefore?: number;
474
+ rankAfter?: number;
475
+ };
476
+ /**
477
+ * alpha.78 — shape-proof accessor for a mutation entry (PB filing
478
+ * 2026-07-25 addendum #2). TWO public surfaces share the name
479
+ * `mutationsApplied` with DIFFERENT element types: `CompileResult` /
480
+ * `CallResult` carry `MutationApplied[]` (objects), while
481
+ * `compileForAISDKv6` carries `string[]` (ids) — each internally
482
+ * consistent, but a consumer coding to one surface's d.ts while reading the
483
+ * other's runtime gets a silent false negative (`typeof m === 'string'`
484
+ * compiles clean, runs clean, and is wrong on every row). Neither element
485
+ * type can change without breaking a live consumer, so the fix is an
486
+ * accessor that is correct on both.
487
+ */
488
+ declare function mutationId(m: string | MutationApplied | null | undefined): string | undefined;
489
+ /**
490
+ * alpha.78 — does any mutation in the list match `idOrPrefix` (exact id, or
491
+ * prefix when it ends with `*`)? Works on both `mutationsApplied` element
492
+ * shapes. `hasMutation(result.mutationsApplied, 'quality-gate-measured-*')`
493
+ * is the gate-detection idiom PB hand-rolled in their deep-smoke.
494
+ */
495
+ declare function hasMutation(list: ReadonlyArray<string | MutationApplied> | undefined, idOrPrefix: string): boolean;
496
+ /**
497
+ * Target-specific wire request. Shape varies by provider — caller passes the
498
+ * right field to the right SDK.
499
+ */
500
+ type CompiledRequest = {
501
+ provider: 'anthropic';
502
+ model: string;
503
+ system: Array<{
504
+ type: 'text';
505
+ text: string;
506
+ cache_control?: {
507
+ type: 'ephemeral';
508
+ };
509
+ }>;
510
+ messages: Array<{
511
+ role: string;
512
+ content: unknown;
513
+ }>;
514
+ tools?: unknown[];
515
+ max_tokens?: number;
516
+ /**
517
+ * alpha.29 — emitted only when the translator's wire-overrides set
518
+ * `parallelToolCalls = false`. Shape per Anthropic Messages API docs:
519
+ * `{ type: 'auto', disable_parallel_tool_use: true }`. kgauto defaults
520
+ * to omitting `tool_choice` entirely (Anthropic defaults to auto + parallel),
521
+ * so this field's presence signals an explicit override.
522
+ */
523
+ tool_choice?: {
524
+ type: 'auto' | 'any' | 'tool' | 'none';
525
+ disable_parallel_tool_use?: boolean;
526
+ name?: string;
527
+ };
528
+ } | {
529
+ provider: 'google';
530
+ model: string;
531
+ systemInstruction?: {
532
+ role: 'system';
533
+ parts: Array<{
534
+ text: string;
535
+ }>;
536
+ };
537
+ contents: Array<{
538
+ role: string;
539
+ parts: unknown[];
540
+ }>;
541
+ tools?: unknown[];
542
+ generationConfig?: Record<string, unknown>;
543
+ cachedContent?: string;
544
+ } | {
545
+ provider: 'openai';
546
+ model: string;
547
+ messages: Array<{
548
+ role: string;
549
+ content: unknown;
550
+ }>;
551
+ tools?: unknown[];
552
+ response_format?: unknown;
553
+ reasoning_effort?: string;
554
+ /**
555
+ * alpha.29 — emitted only when the translator's wire-overrides set
556
+ * `parallelToolCalls = false`. OpenAI defaults parallel_tool_calls=true
557
+ * server-side; we explicit-set to false only when overriding.
558
+ */
559
+ parallel_tool_calls?: boolean;
560
+ } | {
561
+ provider: 'deepseek';
562
+ model: string;
563
+ messages: Array<{
564
+ role: string;
565
+ content: unknown;
566
+ }>;
567
+ tools?: unknown[];
568
+ } | {
569
+ provider: 'zai';
570
+ model: string;
571
+ messages: Array<{
572
+ role: string;
573
+ content: unknown;
574
+ }>;
575
+ tools?: unknown[];
576
+ response_format?: unknown;
577
+ /**
578
+ * alpha.65 — Z.ai thinking knob (docs.z.ai chat-completion reference):
579
+ * `thinking.type` is 'enabled' (default) or 'disabled'. Emitted only
580
+ * when a cliff forces thinking off (`force_thinking_budget_zero`);
581
+ * omitted otherwise so the provider default applies.
582
+ */
583
+ thinking?: {
584
+ type: 'enabled' | 'disabled';
585
+ };
586
+ } | {
587
+ provider: 'moonshot';
588
+ model: string;
589
+ messages: Array<{
590
+ role: string;
591
+ content: unknown;
592
+ }>;
593
+ tools?: unknown[];
594
+ response_format?: unknown;
595
+ };
596
+ /**
597
+ * Best-practice advisory emitted by the compiler at compile time. Non-fatal —
598
+ * consumers log, surface in dev tools, gate on `level === 'critical'` in CI,
599
+ * or ignore. The advisor inspects the IR + selected profile + diagnostics
600
+ * and emits one entry per detected gap.
601
+ *
602
+ * Codes are stable across releases. `suggestion` and `docsUrl` are optional
603
+ * but encouraged: suggestion = the actionable diff; docsUrl = the
604
+ * interfaces/kgauto.md anchor for context.
605
+ *
606
+ * alpha.6 Phase 1 starter rules:
607
+ * - `caching-off-on-claude` (warn) system >2000 chars on Anthropic, no cacheable=true
608
+ * - `single-chunk-system` (info) Anthropic, only one PromptSection >1000 chars
609
+ * - `tool-bloat` (warn) >10 tools on a short-output archetype
610
+ * - `history-uncached-on-claude` (warn) Anthropic, ≥2 history messages, no historyCachePolicy
611
+ *
612
+ * Phase 2 (catalog as `bestPractices` block in profiles) and Phase 3 (brain
613
+ * telemetry on `advisories_fired`) are alpha.7+ territory.
614
+ */
615
+ interface BestPracticeAdvisory {
616
+ /**
617
+ * Severity. `info` = informational; `warn` = behavioral pattern that's
618
+ * usually expensive or wrong; `critical` = likely bug or production-grade
619
+ * misuse. Phase 1 ships info + warn only.
620
+ */
621
+ level: 'info' | 'warn' | 'critical';
622
+ /** Stable kebab-case code. Consumers filter / gate by this. */
623
+ code: string;
624
+ /** Human-readable explanation of what was detected. */
625
+ message: string;
626
+ /** Optional: how to fix — actionable diff or pattern. */
627
+ suggestion?: string;
628
+ /** Optional: link to docs anchor for more context. */
629
+ docsUrl?: string;
630
+ /**
631
+ * alpha.20 — actionable category for routing/dashboard surfacing. When set,
632
+ * the brain persists this as `recommendation_type` on
633
+ * `compile_outcome_advisories` so consumers can filter "show me all
634
+ * client-side issues that are caching-fix recommendations." Optional;
635
+ * absent on legacy or uncategorized rules.
636
+ *
637
+ * - `'model-swap'` — swap to a different model fixes this
638
+ * - `'prompt-fix'` — restructure prompt (sections, tools, format)
639
+ * - `'caching-fix'` — add cache markers (system or history)
640
+ * - `'no-ai-needed'` — the call shouldn't be using an AI model
641
+ * - `'tier-down'` — current model is overkill for this archetype
642
+ * - `'architecture-change'` — the issue isn't fixable at the kgauto layer
643
+ */
644
+ recommendationType?: 'model-swap' | 'prompt-fix' | 'caching-fix' | 'no-ai-needed' | 'tier-down' | 'architecture-change';
645
+ /**
646
+ * alpha.36 — architectural recommendation when the call shouldn't be an
647
+ * AI call at all, or should be a cheaper non-AI substitute. Optional and
648
+ * orthogonal to {@link recommendationType}: when set, narrows the
649
+ * `no-ai-needed` / `architecture-change` rec-type into an actionable
650
+ * pattern the consumer can implement.
651
+ *
652
+ * - `'lookup-table'` — deterministic input → output mapping (≥95% agreement at high N). Build a domain table; fall back to AI for novel inputs.
653
+ * - `'memoization-cache'` — same canonical input recurs across sessions with equivalent output. Add an edge/KV cache keyed on canonical input.
654
+ * - `'tier-down'` — cheaper model tier delivers equal-or-better oracle score on this shape. Move primary to the cheaper tier.
655
+ * - `'deterministic-parser'` — `constraints.structuredOutput: true` calls where the schema is regex/JSON-extractable from the input.
656
+ * - `'precompute'` — input is derivable at build / cron / batch time; eliminate the per-request call entirely.
657
+ *
658
+ * Operator-side detection lives in `v2/scripts/no-ai-needed-detector.mjs`
659
+ * (alpha.36); a compile-time advisor rule that consumes the brain-side
660
+ * findings cache follows in a later alpha. Today populated only by the
661
+ * operator-side detector + the brain RPC `get_no_ai_needed_candidates`
662
+ * (migration 021).
663
+ */
664
+ recommendedArchitecture?: 'lookup-table' | 'memoization-cache' | 'tier-down' | 'deterministic-parser' | 'precompute';
665
+ /**
666
+ * alpha.28 — when a rule wants to surface a specific structural adaptation
667
+ * (not just a swap or a prompt fix), it attaches the adapter shape here.
668
+ * Shape is the canonical {@link Adapter} discriminated union defined in
669
+ * this module; `compatibility.ts` re-exports it so
670
+ * `getModelCompatibility()` and `BestPracticeAdvisory.suggestedAdaptation`
671
+ * share one source of truth.
672
+ *
673
+ * Today fired by `archetype-perf-floor-breach` (alpha.28) when a
674
+ * documented adapter exists for the chosen model's archetype cliff.
675
+ * Absent on rules without a structural adapter (caching-off-on-claude,
676
+ * tool-bloat, etc.) and on the `reject` branch of
677
+ * `archetype-perf-floor-breach` where no adapter would help.
678
+ *
679
+ * CLOSED discriminated union (R3 from consultation doc) — future adapter
680
+ * parameters extend the union in `compatibility.ts` in named alpha
681
+ * releases. No `| string` escape hatch; consumer code can write
682
+ * exhaustive `switch (suggestedAdaptation.parameter)`.
683
+ *
684
+ * Phase 2 cross-builder coherence: Builder A's
685
+ * `AdvisoryRecord.suggestedAdaptation` (in `glassbox-routes/types.ts`)
686
+ * MUST type to the same union. Phase 2 integration verifies.
687
+ */
688
+ suggestedAdaptation?: Adapter;
689
+ /**
690
+ * alpha.42 — the kgauto request handle that produced this advisory. Stamped
691
+ * by `compile()` after `runAdvisor()` returns, sourced from
692
+ * `CompileResult.handle`. Lets a consumer's admin UI pivot from advisory
693
+ * row → original trace (Glass-Box card, brain `compile_outcomes` lookup).
694
+ *
695
+ * Absent when the advisory was constructed outside the full `compile()`
696
+ * pipeline (e.g., a direct `runAdvisor()` call in tests). Always present
697
+ * on advisories returned from `compile().advisories`.
698
+ */
699
+ kgautoRequestId?: string;
700
+ /**
701
+ * alpha.42 — who is positioned to act on this advisory.
702
+ *
703
+ * - `'consumer-actionable'` (Class A): the consumer can fix this by
704
+ * changing their wire-up — add `cacheable: true` markers, swap their
705
+ * model literal, declare a section, etc. The admin UI should surface
706
+ * a one-click apply or a "fix it" CTA.
707
+ * - `'producer-owned'` (Class B): kgauto needs to ship a code or data
708
+ * change — update a profile, promote a model in the chain, fix a
709
+ * wire contract. The admin UI should surface this as informational
710
+ * ("kgauto is on it") and not present a consumer-side action.
711
+ *
712
+ * Stamped by `compile()` after `runAdvisor()` returns. Rules can self-
713
+ * declare via the matching field on their return; otherwise the stamp
714
+ * consults the static `PRODUCER_OWNED_RULE_CODES` set in `advisor.ts`,
715
+ * defaulting to `'consumer-actionable'` when the code isn't listed.
716
+ *
717
+ * Closes the s47 dogfood gap where the tt-intel admin UI conflated the
718
+ * two classes — consumers couldn't tell whether they should act or wait.
719
+ */
720
+ ownership?: 'consumer-actionable' | 'producer-owned';
721
+ }
722
+ /**
723
+ * alpha.28 — adapter shape attached to advisories and returned by
724
+ * `getModelCompatibility()`. A CLOSED discriminated union: future adapter
725
+ * parameters extend it explicitly in named alpha releases. NO `| string`
726
+ * escape hatch — consumer policy code SHOULD write exhaustive
727
+ * `switch (adapter.parameter)` and rely on the compiler to flag
728
+ * "I added a new adapter parameter and forgot to update consumer policy."
729
+ *
730
+ * Defined here (in `ir.ts`, the foundational types module) and re-exported
731
+ * from `compatibility.ts` for ergonomic consumer imports. Anchoring it
732
+ * here avoids the import cycle that would form if both files tried to be
733
+ * the source of truth (ir.ts → compatibility.ts → profiles.ts → ir.ts).
734
+ *
735
+ * alpha.28 variants:
736
+ * - `{ parameter: 'toolOrchestration'; value: 'sequential'; consequence }`
737
+ * Lifts DeepSeek V4-family on `hunt` from the sequential-tool cliff
738
+ * (L-040). `consequence` is consumer-renderable plain English.
739
+ *
740
+ * Future alpha releases will add e.g. `parallelToolCalls`, `maxTools`,
741
+ * `thinkingBudget` (per tt-intel-Cairn priority list).
742
+ */
743
+ type Adapter = {
744
+ parameter: 'toolOrchestration';
745
+ value: 'sequential';
746
+ consequence: string;
747
+ };
748
+ /**
749
+ * alpha.29+ — record of a single section rewrite fired by the translator at
750
+ * compile time. Surfaces on `CompileResult.sectionRewritesApplied` and (in
751
+ * scrubbed wire form, without original/transformed text) on
752
+ * `TraceDetail.sectionRewritesApplied` for Glass-Box Coaching-card rendering.
753
+ *
754
+ * `originalText` / `transformedText` stay package-internal — they may carry
755
+ * consumer PII. The wire-shape variant (`TraceSectionRewrite` in
756
+ * `glassbox-routes/types.ts`) carries only `summary` for renderer use.
757
+ */
758
+ interface SectionRewrite {
759
+ /** Stable id of the `PromptSection` that was rewritten. */
760
+ sectionId: string;
761
+ /** The `kind` discriminator that matched the rewrite rule. */
762
+ kind: SectionKind;
763
+ /**
764
+ * Stable identifier of the rule that fired (e.g.
765
+ * `'sequential-tool-cliff-below-floor'`). Future rules add named ids; the
766
+ * brain aggregates by this value for cross-app learning.
767
+ */
768
+ rule: string;
769
+ /** The section's text BEFORE the rewrite fired. */
770
+ originalText: string;
771
+ /** The text the translator emitted into the IR for this section. */
772
+ transformedText: string;
773
+ /**
774
+ * Wire-level overrides emitted alongside the text rewrite. Merged into
775
+ * `CompileResult.wireOverrides` by `applySectionRewrites`. alpha.29 ships
776
+ * `parallelToolCalls`; the union extends as more wire-overrides surface.
777
+ */
778
+ wireOverrides?: {
779
+ parallelToolCalls?: boolean;
780
+ };
781
+ }
782
+ interface CompileResult {
783
+ /** Unique handle for this call — pass to record() to correlate the outcome. */
784
+ handle: string;
785
+ /** Selected target model id. */
786
+ target: string;
787
+ /** Selected provider. */
788
+ provider: Provider;
789
+ /** The wire request — pass the appropriate fields to your SDK. */
790
+ request: CompiledRequest;
791
+ /** Estimated tokens (input). */
792
+ tokensIn: number;
793
+ /** Estimated cost in USD (input portion). */
794
+ estimatedCostUsd: number;
795
+ /** Mutations that fired during compile (informational). */
796
+ mutationsApplied: MutationApplied[];
797
+ /** Fallback chain — try these in order if target fails. */
798
+ fallbackChain: string[];
799
+ /**
800
+ * Best-practice advisories emitted by the compiler. Non-fatal. Empty
801
+ * array when no rules fired. alpha.6 Phase 1.
802
+ */
803
+ advisories: BestPracticeAdvisory[];
804
+ /**
805
+ * alpha.29+ — per-section rewrites applied by the translator at compile
806
+ * time. Empty array means no rewrites fired (or pre-alpha.29 behavior —
807
+ * all sections default `kind: 'arbitrary'`, which is pass-through).
808
+ *
809
+ * Surfaces to:
810
+ * - Glass-Box Coaching card (via `TraceDetail.sectionRewritesApplied`,
811
+ * scrubbed of original/transformed text)
812
+ * - brain `compile_outcomes.section_rewrites_applied` (migration 019)
813
+ * for cross-app learning
814
+ */
815
+ sectionRewritesApplied: SectionRewrite[];
816
+ /**
817
+ * alpha.29+ — wire-level overrides emitted by translator rewrites. The
818
+ * provider lowering pass threads these through to the wire request before
819
+ * emit. Today only `parallelToolCalls: boolean`; the type extends as more
820
+ * wire-overrides surface.
821
+ *
822
+ * Undefined when no rewrite emitted overrides — the common case.
823
+ */
824
+ wireOverrides?: {
825
+ parallelToolCalls?: boolean;
826
+ };
827
+ /** Diagnostics for caller-side logging. */
828
+ diagnostics: {
829
+ sectionsKept: number;
830
+ sectionsDropped: number;
831
+ toolsKept: number;
832
+ toolsDropped: number;
833
+ historyKept: number;
834
+ historyDropped: number;
835
+ cacheableTokens: number;
836
+ estimatedCacheSavingsUsd: number;
837
+ /**
838
+ * Tokens in `history` (and `currentTurn` when before the marker) that
839
+ * fall within the cacheable prefix per `historyCachePolicy`. Always
840
+ * computed; only Anthropic actually emits a wire-format marker. For
841
+ * Gemini / OpenAI / DeepSeek, this represents the theoretical cacheable
842
+ * prefix that implicit caching may pick up — useful telemetry for the
843
+ * brain to learn which (app, model, archetype) tuples benefit most
844
+ * from history caching. alpha.5.
845
+ */
846
+ historyCacheableTokens: number;
847
+ /**
848
+ * Total tokens in input `history` (pre-compression). Computed regardless
849
+ * of whether `passCompressHistory` fired — surfaces how close a tuple is
850
+ * to its `compressHistoryAboveTokens` threshold so dashboards / cost-
851
+ * watchers can see the bloat axis the count-based threshold misses.
852
+ * 0 when history is empty. alpha.7.
853
+ */
854
+ historyTokensTotal: number;
855
+ /**
856
+ * alpha.20 E3. Consumer-declared tool-orchestration mode for this call,
857
+ * mirrored from `ir.constraints.toolOrchestration` for downstream
858
+ * observability (Glass-Box panel, brain telemetry, advisor logs).
859
+ * Undefined when the consumer hadn't adopted the constraint yet —
860
+ * treat as 'parallel' equivalent for back-compat.
861
+ */
862
+ toolOrchestration?: 'parallel' | 'sequential' | 'either';
863
+ /**
864
+ * alpha.33. Zero-based index into the resolved (post-compression) history
865
+ * array at which Anthropic prompt-cache marker should land — i.e., the
866
+ * last message that belongs to the stable cacheable prefix. Consumers
867
+ * using AI-SDK's `streamText({ messages: convertToModelMessages(...) })`
868
+ * lose the per-message `providerOptions.anthropic.cacheControl` markers
869
+ * the compiler emits on `result.request.messages`, because
870
+ * `convertToModelMessages` reads raw input not the lowered output.
871
+ * This index gives the consumer a single deterministic position to
872
+ * attach `cacheControl: { type: 'ephemeral' }` after their own conversion.
873
+ *
874
+ * Computation:
875
+ * - `historyCachePolicy.strategy === 'all-but-latest'`: history.length - 1
876
+ * (or undefined if history is empty)
877
+ * - `historyCachePolicy.strategy === 'fixed-suffix'` with `suffix: N`:
878
+ * history.length - 1 - N (undefined if N exceeds history length)
879
+ * - `historyCachePolicy.strategy === 'none'` or omitted: undefined
880
+ *
881
+ * The companion helper `attachCacheControlToStreamTextInput()` reads
882
+ * this field + the consumer's converted messages to perform the per-
883
+ * attempt mutation correctly. Filed by IC + tt-intel cross-consumer
884
+ * pattern 2026-05-20 (`streamText-cache-marker-propagation-gap`).
885
+ */
886
+ historyCacheMarkIndex?: number;
887
+ /**
888
+ * alpha.33. Zero-based index into the structured `systemMessages` array
889
+ * (see top-level `systemMessages` field) at which the cacheable system
890
+ * prefix ends. Useful when the consumer is building a multi-block
891
+ * system parameter for Anthropic streamText — they can attach
892
+ * `providerOptions.anthropic.cacheControl` to `systemMessages[index]`.
893
+ *
894
+ * Undefined when no section had `cacheable: true` OR `systemMessages` is
895
+ * empty. Matches `historyCacheMarkIndex` semantics on the history axis.
896
+ */
897
+ systemCacheMarkIndex?: number;
898
+ /**
899
+ * alpha.43. Cliff-style warnings surfaced by the convention pass
900
+ * (`passApplyConventions`) when the selected profile + archetype carry
901
+ * a `cliffWarning` whose preconditions are met. These are informational
902
+ * — they describe a structural mismatch between the call's shape and
903
+ * the chosen family (e.g. "reasoner family is wrong for parallel-tool
904
+ * hunt") so consumers can route differently next time.
905
+ *
906
+ * Empty array when no convention fired or no warnings surfaced.
907
+ * Separate from `cliff_guard` mutations in `mutationsApplied` — those
908
+ * are profile.cliffs[] runtime triggers; these are convention-level
909
+ * advisory text. Both can fire on the same call.
910
+ */
911
+ cliffWarnings: string[];
912
+ /**
913
+ * alpha.68 / Release A (delegation-fanout-accelerator §5.D) — tokens added
914
+ * by the `discipline_contract` gate block on this call; 0 when the gate
915
+ * didn't fire (no eligible `discipline_contract` section, or Factor A /
916
+ * Factor C screened it out). Measured, never assumed: counted from the
917
+ * exact frozen preamble the translator prepended. Persisted to
918
+ * `compile_outcomes.discipline_gate_tokens` (migration 042) so the tax is a
919
+ * number the cost-watcher can weigh against the lift.
920
+ */
921
+ disciplineGateTokens: number;
922
+ /**
923
+ * alpha.87 — which world answered THIS compile, per data source (the
924
+ * `per-source-freshness-marker-on-callresult` ask, PB s76). kgauto
925
+ * legitimately ranks on bundled profiles when cold and brain-re-ranked
926
+ * profiles when warm, and before this field the response said nothing
927
+ * about which had answered — PB drew three wrong conclusions across
928
+ * s74–s76 from observations taken at unknown temperature. A FLAT marker
929
+ * was rejected in the s76 consult: the app-scoped gates (measured
930
+ * failure, promotions, exclusions, promote-ready) carry cache state
931
+ * independent of the shared config snapshot, and a config-derived
932
+ * marker would read 'brain' in exactly the window where the
933
+ * measured-failure gate is still cold.
934
+ *
935
+ * Semantics: `config` is `'brain'` iff the shared `/v2/config` snapshot
936
+ * held fetched data at compile time (else `'bundled'`). The per-gate
937
+ * fields are `'warm'` iff a successful app-scoped load had completed in
938
+ * this process — `'cold'` covers never-loaded, load-failed, and
939
+ * feature-disabled alike, because in all three the gate read no data.
940
+ * With alpha.73/.86 prefetch + `gateWarmupMs`, a `call()` after warmup
941
+ * should read all-warm; a cold isolate's first compile may not.
942
+ */
943
+ freshness: {
944
+ config: 'bundled' | 'brain';
945
+ measuredFailure: 'cold' | 'warm';
946
+ promotions: 'cold' | 'warm';
947
+ exclusions: 'cold' | 'warm';
948
+ promoteReady: 'cold' | 'warm';
949
+ };
950
+ /**
951
+ * alpha.87 — per-section cost attribution (cc's
952
+ * `per-section-cost-attribution-in-diagnostics` ask, from Sacha's
953
+ * oracle read). `compile()` already receives sections IDed by the
954
+ * consumer, so this itemization is arithmetic on values the compiler
955
+ * computes anyway: zero model calls, no brain write, counts and IDs
956
+ * only — never section text (G3-compatible by construction). Always-on:
957
+ * an itemization nobody remembers to ask for is absent at the moment
958
+ * it is needed, and the payload is one small object per section.
959
+ *
960
+ * One row per INPUT section, in input order — dropped sections appear
961
+ * with `kept: false` and zero cost so a consumer can see what a pass
962
+ * removed and what it was worth.
963
+ *
964
+ * The cacheable interaction is deliberately two explicit columns
965
+ * rather than one blended number or a compile-time `cacheState` guess:
966
+ * whether a given call pays the first-call or the warm price is decided
967
+ * at the provider AFTER compile, so the honest compile-time statement
968
+ * is both prices. `estCostUsdFirstCall` is the section's tokens at the
969
+ * selected model's uncached input price (provider cache-WRITE
970
+ * surcharges, e.g. Anthropic's 1.25×, are not modeled — this
971
+ * understates first-call cost slightly). `estCostUsdWarm` applies the
972
+ * profile's cache-read discount when the section sits in an ACTIVE
973
+ * cacheable prefix (cacheable, and the prefix met the provider's
974
+ * minimum); otherwise it equals `estCostUsdFirstCall` — for a
975
+ * non-cached section the two worlds cost the same.
976
+ */
977
+ sections: Array<{
978
+ id: string;
979
+ tokens: number;
980
+ kept: boolean;
981
+ cacheable: boolean;
982
+ estCostUsdFirstCall: number;
983
+ estCostUsdWarm: number;
984
+ }>;
985
+ };
986
+ /**
987
+ * alpha.33. Structured `system` for AI-SDK `streamText({ system })`
988
+ * consumers. When the consumer's lowered request has cacheable section
989
+ * markers, the flat `system: string` form loses the marker assignment
990
+ * (every `streamText({ system: '<string>' })` call silently strips
991
+ * providerOptions). Pass `systemMessages` instead to `streamText({
992
+ * system: result.systemMessages })` so the cacheable prefix is structurally
993
+ * preserved.
994
+ *
995
+ * Each entry is a `SystemModelMessage`-shaped object:
996
+ * { role: 'system', content: string, providerOptions?: { anthropic?:
997
+ * { cacheControl: { type: 'ephemeral' } } } }
998
+ *
999
+ * Provider-agnostic on emit: only Anthropic actually consumes the
1000
+ * cacheControl block; Gemini / OpenAI / DeepSeek receive the same
1001
+ * shape but ignore the marker (matching their implicit-caching semantics).
1002
+ *
1003
+ * Empty array when the IR carried zero sections (rare; usually means the
1004
+ * compiler dropped everything for the chosen intent). Consumers can fall
1005
+ * back to `result.systemMessages.length === 0 ? '' : result.systemMessages`
1006
+ * or use the helper `attachCacheControlToStreamTextInput()` which handles
1007
+ * both shapes.
1008
+ */
1009
+ systemMessages: SystemModelMessage[];
1010
+ }
1011
+ /**
1012
+ * alpha.33. AI-SDK-compatible system-message shape carried on
1013
+ * `CompileResult.systemMessages`. Matches the AI-SDK `streamText({ system })`
1014
+ * structured form — consumers can pass these directly without conversion.
1015
+ *
1016
+ * The `providerOptions` field is set ONLY for entries that came from a
1017
+ * section with `cacheable: true` AND the chosen provider is Anthropic AND
1018
+ * the compiler computed a non-zero `cacheableTokens`. Other providers see
1019
+ * `providerOptions: undefined` (or omitted entirely).
1020
+ */
1021
+ interface SystemModelMessage {
1022
+ role: 'system';
1023
+ content: string;
1024
+ providerOptions?: {
1025
+ anthropic?: {
1026
+ cacheControl: {
1027
+ type: 'ephemeral';
1028
+ };
1029
+ };
1030
+ };
1031
+ }
1032
+ /**
1033
+ * Token usage normalized across providers. `cached` and `cacheCreated` are
1034
+ * Anthropic prompt-cache reads/writes (Gemini implicit caching populates
1035
+ * `cached` from `usageMetadata.cachedContentTokenCount`; OpenAI populates
1036
+ * from `prompt_tokens_details.cached_tokens`).
1037
+ */
1038
+ interface NormalizedTokens {
1039
+ input: number;
1040
+ output: number;
1041
+ total: number;
1042
+ cached?: number;
1043
+ cacheCreated?: number;
1044
+ }
1045
+ /**
1046
+ * Tool call in a provider-agnostic shape. Anthropic `tool_use` blocks,
1047
+ * Google `functionCall` parts, and OpenAI/DeepSeek `tool_calls[]` all
1048
+ * collapse to this.
1049
+ */
1050
+ interface ToolCall {
1051
+ id: string;
1052
+ name: string;
1053
+ args: Record<string, unknown>;
1054
+ }
1055
+ interface NormalizedResponse {
1056
+ /** Main text body. Empty string if response had no text content. */
1057
+ text: string;
1058
+ /**
1059
+ * Parsed structured output. Populated when ir.constraints.structuredOutput
1060
+ * is true and JSON.parse(text) succeeds. Null otherwise.
1061
+ */
1062
+ structuredOutput: unknown | null;
1063
+ /** Tool calls in normalized shape. Empty array if none. */
1064
+ toolCalls: ToolCall[];
1065
+ tokens: NormalizedTokens;
1066
+ /** Provider-specific finish reason, passed through unchanged. */
1067
+ finishReason?: string;
1068
+ /** Untouched provider response — escape hatch for consumers needing fields not yet normalized. */
1069
+ raw: unknown;
1070
+ /** Set when structuredOutput parsing was attempted and failed. */
1071
+ parseError?: string;
1072
+ }
1073
+ interface ApiKeys {
1074
+ anthropic?: string;
1075
+ google?: string;
1076
+ openai?: string;
1077
+ deepseek?: string;
1078
+ zai?: string;
1079
+ moonshot?: string;
1080
+ }
1081
+ /**
1082
+ * Per-provider override fields shallow-merged into the lowered request before
1083
+ * execution. Lets consumers reach Gemini `safetySettings`, Anthropic
1084
+ * `tool_choice`, OpenAI `seed` etc. without bypassing kgauto.
1085
+ */
1086
+ interface ProviderOverrides {
1087
+ anthropic?: Record<string, unknown>;
1088
+ google?: Record<string, unknown>;
1089
+ openai?: Record<string, unknown>;
1090
+ deepseek?: Record<string, unknown>;
1091
+ zai?: Record<string, unknown>;
1092
+ moonshot?: Record<string, unknown>;
1093
+ }
1094
+ /**
1095
+ * Full-IR inline shadow-probe config (Shape B, Phase 1 — 2026-05-29 s51).
1096
+ *
1097
+ * When set on `call()`, after the primary response is served kgauto re-lowers
1098
+ * the SAME in-memory PromptIR to each candidate and runs it, persisting a
1099
+ * `probe_outcomes` row with `replay_source='inline-full-ir'` and
1100
+ * `prompt_fidelity=1.0` — a fair, full-prompt measurement (vs the watchers'
1101
+ * lossy `prompt_preview` replay). The probe NEVER blocks the user response and
1102
+ * NEVER persists the raw prompt (system/context/payload) — only response
1103
+ * previews + metadata, same policy the brain already holds.
1104
+ *
1105
+ * This is the trustworthy path that earns the right to a quality verdict;
1106
+ * `prompt_preview` replay is retired for verdicts (see CLAUDE.md).
1107
+ *
1108
+ * Phase 1 enforces `sampleRate` and runs the candidate with `judge: 'off'`
1109
+ * (responses stored for an offline batch judge). `judge: 'opus'` (inline
1110
+ * verdict) and `maxPerDay` (per-tuple daily cap, needs a brain-count read)
1111
+ * are accepted by the type but ENFORCED IN PHASE 2.
1112
+ */
1113
+ interface ShadowProbeConfig {
1114
+ /** Model id(s) or family alias(es) to shadow-test against the served model, on the same IR. */
1115
+ candidates: string | string[];
1116
+ /** Probability [0,1] that any given call fires a probe. Default 0.05. */
1117
+ sampleRate?: number;
1118
+ /**
1119
+ * 'off' (Phase 1 default): run candidate + store both responses for an
1120
+ * offline batch judge. 'opus': judge candidate-vs-served inline (Phase 2).
1121
+ */
1122
+ judge?: 'opus' | 'off';
1123
+ /** Per-(appId, archetype, candidate) daily cap. Phase 2 (needs brain-count read). Default 20. */
1124
+ maxPerDay?: number;
1125
+ /**
1126
+ * alpha — latency budget (ms) for the probe leg. **Only enforced in sync mode**
1127
+ * (`BrainConfig.sync === true`), where `call()` awaits the probe before returning
1128
+ * the served response (PB-class Edge consumers, L-086) and the candidate's
1129
+ * latency therefore lands on the user-facing critical path.
1130
+ *
1131
+ * The probe path races against this budget: if the candidate(s) don't finish
1132
+ * within `maxLatencyMs`, the probe aborts, `call()` returns the already-served
1133
+ * response immediately, and an `outcome='aborted_latency_budget'` row is
1134
+ * recorded (no verdict, no false quality data). **The user is never delayed
1135
+ * past the budget.** Default `15000`.
1136
+ *
1137
+ * No-op for fire-and-forget consumers (`sync` unset/false, and every
1138
+ * `probeShadow()` caller fired from `after()`/`waitUntil()`) — they don't block
1139
+ * the user, so there's no latency to bound. The probe runs to completion there.
1140
+ *
1141
+ * Root cause this closes (PB s57 dogfood incident, 2026-06-05): PB arms the
1142
+ * probe inline with `sync:true` on a `maxDuration:120` analyze route; arming a
1143
+ * `latency_tier='slow'` candidate (deepseek-v4-pro, ~78–84s) pushed total
1144
+ * handler time past the frontend's patience and the served analysis came back
1145
+ * empty. A sync probe had no latency budget — this field is the budget.
1146
+ */
1147
+ maxLatencyMs?: number;
1148
+ /**
1149
+ * alpha — when true (the default) **in sync mode**, skip a candidate whose
1150
+ * registry `latency_tier === 'slow'` *before starting it*: a slow-tier model
1151
+ * (e.g. deepseek-v4-pro / deepseek-v4-flash, both `slow` = ~24s+ p50) cannot
1152
+ * fit a sane inline budget, so starting it just to abort it wastes a provider
1153
+ * call. An `outcome='skipped_slow_tier_sync'` row is recorded so the offline
1154
+ * rollup can see the skip (not a silent drop, not a false verdict). Fail-safe.
1155
+ *
1156
+ * **The deeper rule (slow-reasoner → offline-mode):** inline shadow-probing is
1157
+ * for FAST candidates only. To evaluate a slow reasoner you need the offline /
1158
+ * async probe path (the Phase-2 batch direction) which runs off the response
1159
+ * entirely — never the inline sync probe. Set this `false` only if you've moved
1160
+ * the budget high enough that a slow candidate genuinely fits (rare), or you're
1161
+ * in a non-sync consumer where this flag is a no-op anyway.
1162
+ *
1163
+ * No-op for fire-and-forget consumers (`sync` unset/false; `probeShadow()`):
1164
+ * there's no user latency to protect, so slow candidates run normally. Default
1165
+ * `true`.
1166
+ */
1167
+ skipSlowTierInSync?: boolean;
1168
+ }
1169
+ interface CallOptions {
1170
+ /**
1171
+ * alpha.86 — self-mark for synthetic callers (canaries, smokes, eval
1172
+ * replays, probes). Passed through to every outcome row this call
1173
+ * records, including failure rows. OMIT for real traffic. See
1174
+ * {@link OutcomeSource}.
1175
+ */
1176
+ source?: OutcomeSource;
1177
+ /**
1178
+ * alpha.89 — per-end-user metering: passed through to every outcome row
1179
+ * this call records, including failure rows (a failed call still spent
1180
+ * money attributable to the customer). See {@link RecordInput.endUserId}.
1181
+ */
1182
+ endUserId?: string;
1183
+ /**
1184
+ * alpha.84 — fires once per FAILED attempt during the fallback walk, before
1185
+ * the walk continues. IC-Cairn's filing, 2026-07-29.
1186
+ *
1187
+ * ## The gap this closes
1188
+ *
1189
+ * `call()` records exactly ONE outcome row per call. An attempt that fails
1190
+ * and then walks to a successful fallback survives only as
1191
+ * `fellOverFrom`/`fallbackReason` metadata ON the success row — so a
1192
+ * `call()`-only consumer's corpus contains **no failure rows at all**, by
1193
+ * construction, no matter how carefully they wired `record()`.
1194
+ *
1195
+ * That is not hypothetical. kgauto's own `corpus-implausible-success`
1196
+ * detector fired on inspire-central and told them to "wire record() on the
1197
+ * FAILURE path" — which they had already done, in five places. Measured:
1198
+ * 89 rows, 0 failure rows, 2 fellover-bearing. **The detector demanded
1199
+ * output the API could not produce.** Streaming consumers have had
1200
+ * `onFailedAttempt` since alpha.48 via `streamWithFallover`; `call()`
1201
+ * consumers had nothing. This restores parity.
1202
+ *
1203
+ * ## Contract
1204
+ *
1205
+ * Fires for every attempt whose `status` is `'retryable'` or `'terminal'`,
1206
+ * including skipped ones (`*_inferred`, `aborted`) — a skip is a real
1207
+ * datum about the chain, not an absence. Never fires for `'success'`.
1208
+ *
1209
+ * Called synchronously and **its throw is swallowed**: a consumer's
1210
+ * telemetry bug must never take down the call it is observing. It is a
1211
+ * notification, not a hook that can veto or alter the walk.
1212
+ *
1213
+ * Typical use — emit a failure row per attempt so the corpus can contain
1214
+ * failures at all:
1215
+ *
1216
+ * call(ir, { onFailedAttempt: (a) => void recordOutcome({
1217
+ * ...base, success: false, errorType: a.errorCode, model: a.model,
1218
+ * }) })
1219
+ */
1220
+ onFailedAttempt?: (attempt: CallAttempt) => void;
1221
+ /** Forwarded to compile(). */
1222
+ policy?: CompilePolicy;
1223
+ /**
1224
+ * alpha.68 / Release A (delegation-fanout-accelerator §5.0, R0 linkage).
1225
+ * When set, this call is a fan-out BRANCH of the parent trace: the outcome
1226
+ * row records `fanout_role='branch'`, `parent_handle=<this value>`, and
1227
+ * `trace_id` inherits the parent handle (the trace root). Absent ⇒ this is a
1228
+ * root (user-facing) call: `fanout_role='root'`, `trace_id=<own handle>`.
1229
+ * Pass the parent's `CallResult.handle` / `CompileResult.handle`.
1230
+ *
1231
+ * Release A supports 1-level linkage (root → branch); deeper trees await the
1232
+ * `delegate` primitive in Release D. Labelling only — NOT a routing change.
1233
+ */
1234
+ parentHandle?: string;
1235
+ /**
1236
+ * alpha (s51 Phase 1) — full-IR inline shadow-probe. When set, kgauto
1237
+ * measures the candidate(s) on the same IR after serving the primary,
1238
+ * fire-and-forget. See {@link ShadowProbeConfig}.
1239
+ */
1240
+ shadowProbe?: ShadowProbeConfig;
1241
+ /**
1242
+ * alpha.73 — how long `call()` may wait for the measured-failure gate's
1243
+ * first brain read before compiling, in ms. Default 400.
1244
+ *
1245
+ * Exists because `compile()` is synchronous and therefore reads only what
1246
+ * the SWR cache already holds: on a cold isolate that is nothing, so the
1247
+ * gate silently does not fire on the first compile — measured on
1248
+ * playbacksam as two billed inferences and ~27–34s of added latency, every
1249
+ * time an isolate is new. Low-traffic consumers are cold most of the time,
1250
+ * so this hits them hardest, inverting the usual severity ordering.
1251
+ *
1252
+ * The wait is bounded and never required: on timeout, brain-down, or no
1253
+ * brain configured, the call proceeds un-gated exactly as before. Set `0`
1254
+ * to disable the wait entirely (the background prefetch still runs, so
1255
+ * later calls in the same isolate are gated either way).
1256
+ */
1257
+ gateWarmupMs?: number;
1258
+ /**
1259
+ * alpha.78 — per-ATTEMPT time bound on the provider leg, in ms
1260
+ * (IC `per-call-timeout-on-call-options` + the portfolio-wide
1261
+ * unbounded-LLM-call scan). Without it the only ceiling is the serverless
1262
+ * route's `maxDuration`, and one hung socket eats the entire route budget
1263
+ * before the fallback chain — the product's whole promise — can walk.
1264
+ *
1265
+ * Semantics per attempt shape:
1266
+ * - non-streaming: total bound via `AbortSignal.timeout()`; firing
1267
+ * classifies retryable `timeout` (L-061) so the chain WALKS.
1268
+ * - streaming: per-chunk STALL bound (max silence between bytes,
1269
+ * covering time-to-first-byte) — a total bound would cut legitimate
1270
+ * long streams. Also classifies `timeout` on fire.
1271
+ *
1272
+ * Each wire attempt (including the same-model retry) gets a fresh budget.
1273
+ * Default: none — today's unbounded behavior, additive and opt-in; a
1274
+ * consumer-side bounds scan (tt-intel s114 shape) can then require it
1275
+ * mechanically. A hang recorded before this existed reads as model
1276
+ * latency in brain telemetry — transport stalls were indistinguishable
1277
+ * from slow models.
1278
+ */
1279
+ attemptTimeoutMs?: number;
1280
+ /**
1281
+ * alpha.78 — caller-owned cancellation (route-level deadline propagation).
1282
+ * Composed per-attempt with `attemptTimeoutMs` via `AbortSignal.any`.
1283
+ * Aborting classifies terminal `aborted` and stops the WHOLE chain walk —
1284
+ * the caller cancelled, so walking would spend money on an answer nobody
1285
+ * is waiting for. Distinct from a timeout, which walks.
1286
+ */
1287
+ abortSignal?: AbortSignal;
1288
+ toolRelevanceThreshold?: number;
1289
+ compressHistoryAfter?: number;
1290
+ /** Override API keys (defaults: process.env). */
1291
+ apiKeys?: ApiKeys;
1292
+ /** Provider-specific request fields shallow-merged into the lowered request. */
1293
+ providerOverrides?: ProviderOverrides;
1294
+ /** Override fetch (for tests). */
1295
+ fetchImpl?: typeof fetch;
1296
+ /** Disable retry/fallback walk on retryable errors. Default: enabled. */
1297
+ noFallback?: boolean;
1298
+ /**
1299
+ * alpha.10. Disable the silent auto-filter of unreachable models from the
1300
+ * fallback walk. Default: false (filter ON). Opt-out exists for tests +
1301
+ * the rare consumer that wants the legacy "fail at execute() with auth
1302
+ * error" behavior. When ON (default), models whose provider has no
1303
+ * resolvable API key are dropped from `targetsToTry` before the first
1304
+ * network call; if the chain empties entirely, throws CallError with
1305
+ * `lastErrorCode = 'no_reachable_models'`.
1306
+ *
1307
+ * Reachability source: `apiKeys` (this CallOptions) + `process.env` (via
1308
+ * `PROVIDER_ENV_KEYS`). Override env via env.ts's `ReachabilityOpts.envSource`
1309
+ * is not exposed here — `call()` always uses process.env. Use
1310
+ * `getDefaultFallbackChain({ reachability: { envSource } })` upstream
1311
+ * for hermetic test runs.
1312
+ */
1313
+ noAutoFilter?: boolean;
1314
+ /**
1315
+ * alpha.34. When provided AND the chosen provider supports streaming
1316
+ * (`profile.streaming === true`) AND `noStream` is not set, kgauto
1317
+ * enables provider-native SSE streaming at the wire layer and invokes
1318
+ * `onChunk(delta)` once per provider stream event. `delta` is the text
1319
+ * since the previous `onChunk` call (NOT cumulative) — provider
1320
+ * stream-shape headache normalized in kgauto's lowering layer.
1321
+ *
1322
+ * `CallResult.response.text` is still populated with the full assembled
1323
+ * response — kgauto buffers internally regardless of the callback.
1324
+ * Consumers can use either the streaming side-effect OR the final
1325
+ * `response.text`, doesn't matter.
1326
+ *
1327
+ * Tool calls + finish reason + usage are collected from stream events
1328
+ * and returned in the final `CallResult` exactly as the non-streaming
1329
+ * path would shape them. Brain telemetry latency = time-to-stream-end.
1330
+ *
1331
+ * Chain-walk semantics: if the streaming target fails mid-stream
1332
+ * (network error, retryable provider error), kgauto walks to the next
1333
+ * fallback target and restarts streaming from its first chunk —
1334
+ * `onChunk` fires fresh from the new target. Consumer detects via the
1335
+ * post-call `CallResult.fellOverFrom`. To opt out of fallback for
1336
+ * streaming, set `noFallback: true` alongside `onChunk`.
1337
+ *
1338
+ * Filed by playbacksam s42 (2026-05-22) as `streaming-output-callback-
1339
+ * on-callresult` for ComposeDrawer SSE; perceived-latency UX win
1340
+ * during 10-30s draft assembly.
1341
+ */
1342
+ onChunk?: (chunk: string) => void;
1343
+ /**
1344
+ * alpha.34. Explicit opt-out of streaming even when `onChunk` is
1345
+ * provided. Default: false (streaming enabled when `onChunk` is set).
1346
+ * Use when the consumer wants to pass `onChunk` conditionally without
1347
+ * branching the call site (e.g., capture chunks for instrumentation
1348
+ * without engaging streaming wire format).
1349
+ */
1350
+ noStream?: boolean;
1351
+ /**
1352
+ * alpha.62 (eval spine) — golden-set capture. When the resolved sample rate
1353
+ * is > 0, a successful non-empty call's FULL PromptIR + served output is
1354
+ * stored to the brain's `kgauto_golden_irs` replay corpus (raw prompt —
1355
+ * explicit opt-in; see golden.ts consent posture). `sampleRate` here
1356
+ * overrides the `KGAUTO_GOLDEN_CAPTURE` env var; when neither is set,
1357
+ * capture never fires.
1358
+ */
1359
+ goldenCapture?: GoldenCaptureOptions;
1360
+ /**
1361
+ * alpha.66 — one same-model retry on a structured-output contract violation
1362
+ * (`max_tokens_on_structured_output` / `structured_output_parse_failed`)
1363
+ * BEFORE chain-walking to a lower-ranked tier. Filed by playbacksam
1364
+ * 2026-07-18 (`structured-output-truncation-retry-same-model`): on PB's
1365
+ * summarize surface the walk destination is measured quality-inferior
1366
+ * (golden eval run #3), so one more attempt at the leader beats walking.
1367
+ *
1368
+ * Consent posture (matches KGAUTO_AUTO_PROMOTE / KGAUTO_GOLDEN_CAPTURE):
1369
+ * default OFF; `true` wins, explicit `false` beats the env, undefined
1370
+ * falls through to the `KGAUTO_SAME_MODEL_RETRY` env var ('1'/'true').
1371
+ * Default-off because the retry adds one full model round-trip of latency
1372
+ * in the failure class — the consumer decides whether their fallback tier
1373
+ * is bad enough to pay that.
1374
+ *
1375
+ * Bounds: at most ONE retry per call(), on whichever target hits the
1376
+ * contract violation first. Truncation-class retries additionally raise
1377
+ * any explicit wire output cap sitting below the profile's
1378
+ * `maxOutputTokens` (e.g. a terse-clamp or providerOverrides cap);
1379
+ * requests already at the profile ceiling re-roll unchanged.
1380
+ */
1381
+ sameModelRetry?: boolean;
1382
+ }
1383
+ interface CallAttempt {
1384
+ model: string;
1385
+ status: 'success' | 'retryable' | 'terminal';
1386
+ errorCode?: string;
1387
+ message?: string;
1388
+ /**
1389
+ * alpha.66 — true on the attempt row that was the one-shot same-model
1390
+ * retry (CallOptions.sameModelRetry). The preceding row for the same model
1391
+ * carries the contract violation that triggered it.
1392
+ */
1393
+ sameModelRetry?: boolean;
1394
+ }
1395
+ /**
1396
+ * Why fallback fired. Normalized for `CallResult.fallbackReason` (alpha.9).
1397
+ *
1398
+ * - `rate_limit` provider returned 429
1399
+ * - `provider_error` 5xx, network, or other retryable upstream issue
1400
+ * - `cost_cap` preflight policy.maxCostPerCallUsd rejected target
1401
+ * - `cliff` alpha.8 contract violation (MAX_TOKENS on
1402
+ * structured output, parse-failed JSON)
1403
+ * - `contract_violation` other compile-time-contract failures (reserved
1404
+ * for alpha.10+ — e.g. mid-stream policy rejects)
1405
+ * - `provider_auth_failed` alpha.14 — initial provider returned 401/403
1406
+ * (upstream key revocation, malformed-but-truthy
1407
+ * key). The chain walks to the next
1408
+ * non-same-provider target instead of
1409
+ * short-circuiting; same-provider remaining
1410
+ * entries skip with errorCode='auth_inferred'.
1411
+ * - `provider_billing_exhausted` alpha.77 — the provider's ACCOUNT is out
1412
+ * of credits (Anthropic "credit balance is too
1413
+ * low" 400, OpenAI insufficient_quota 429,
1414
+ * Google billing-disabled, DeepSeek 402). Walks
1415
+ * like auth and skips same-provider siblings
1416
+ * (errorCode='billing_exhausted_inferred').
1417
+ * Distinct from auth AND from rate_limit because
1418
+ * its disposition is distinct: it never clears
1419
+ * on its own — a human funds the account.
1420
+ */
1421
+ type FallbackReason = 'rate_limit' | 'provider_error' | 'cost_cap' | 'cliff' | 'contract_violation' | 'provider_auth_failed' | 'provider_billing_exhausted';
1422
+ interface CallResult {
1423
+ /** Compile handle (still valid for record() if consumer wants to add oracle scores later). */
1424
+ handle: string;
1425
+ /** The model that ACTUALLY served the response (post-fallback). */
1426
+ actualModel: string;
1427
+ /** What compile() originally targeted. */
1428
+ requestedModel: string;
1429
+ provider: Provider;
1430
+ response: NormalizedResponse;
1431
+ latencyMs: number;
1432
+ /** Mutations that fired during compile (informational, mirrors CompileResult.mutationsApplied). */
1433
+ mutationsApplied: MutationApplied[];
1434
+ /** One entry per provider attempt — observability for retry/fallback walks. */
1435
+ attempts: CallAttempt[];
1436
+ /**
1437
+ * Alpha.9 normalization of fallback-walk telemetry. When the chain
1438
+ * succeeded on the first attempt, these collapse to:
1439
+ * - `servedBy === requestedModel`
1440
+ * - `fellOverFrom` undefined
1441
+ * - `fallbackReason` undefined
1442
+ *
1443
+ * When fallback fired:
1444
+ * - `servedBy` = `actualModel` (the model that produced the response)
1445
+ * - `fellOverFrom` = `requestedModel` (what the caller / compile() asked for)
1446
+ * - `fallbackReason` = normalized cause derived from the first
1447
+ * non-success attempt's `errorCode`
1448
+ *
1449
+ * Consumer UX use: show "Claude was busy; we used Pro for this answer"
1450
+ * when `fellOverFrom` is set (master plan §3.6).
1451
+ */
1452
+ /** Model that actually answered. Equal to `actualModel`; kept distinct for clarity. */
1453
+ servedBy: string;
1454
+ /** Set only when fallback fired. Equal to `requestedModel` in that case. */
1455
+ fellOverFrom?: string;
1456
+ /** Set only when fallback fired. Normalized cause. */
1457
+ fallbackReason?: FallbackReason;
1458
+ /**
1459
+ * alpha.71 — the PRECISE `errorCode` of the attempt that drove the chain,
1460
+ * unnormalized. `fallbackReason` is a six-value summary and one of its
1461
+ * values, `'cliff'`, absorbs two failures with OPPOSITE remedies:
1462
+ *
1463
+ * - `max_tokens_on_structured_output` — the output budget was too small
1464
+ * for the requested shape ⇒ raise the cap / add an output-budget cliff.
1465
+ * - `structured_output_parse_failed` — the model emitted non-JSON ⇒ its
1466
+ * declared `structuredOutput` capability is wrong for this shape.
1467
+ *
1468
+ * The code was already computed in `attempts[]` and thrown away at record
1469
+ * time, so a surface could accumulate a dozen identical `'cliff'` rows that
1470
+ * still could not tell an operator which fix applies. (Found diagnosing
1471
+ * claude-haiku-4-5's 12-of-12 failures on playbacksam/summarize, where the
1472
+ * measured-failure gate correctly stopped the bleeding but the evidence
1473
+ * could not name the cause.)
1474
+ */
1475
+ fellOverErrorCode?: string;
1476
+ /**
1477
+ * alpha.66 — true when the one-shot same-model retry
1478
+ * (CallOptions.sameModelRetry) fired during this call, regardless of
1479
+ * whether the retry rescued the call or the chain walked afterwards.
1480
+ * Cross-check `attempts[]` (the `sameModelRetry: true` row) and
1481
+ * `fellOverFrom` to distinguish rescued-by-retry from retried-then-walked.
1482
+ */
1483
+ retriedSameModel?: boolean;
1484
+ /**
1485
+ * alpha.10. Models that auto-filter dropped from the fallback walk because
1486
+ * their provider had no reachable API key. Empty when nothing was filtered
1487
+ * (the common case once consumers have all the keys they need). Surfaces
1488
+ * silent self-heal so consumers can log/audit what happened without
1489
+ * defeating the "kgauto just gets" UX.
1490
+ *
1491
+ * Empty array (not undefined) when filter ran but dropped nothing —
1492
+ * distinguishes "filter ran cleanly" from "filter was disabled" (`undefined`
1493
+ * when `noAutoFilter: true`).
1494
+ */
1495
+ unreachableFiltered?: string[];
1496
+ /**
1497
+ * alpha.16. Models that policy.blockedModels filtering dropped from the
1498
+ * fallback walk. Defense-in-depth at the call() boundary — compile()'s
1499
+ * passScoreTargets already excludes blocked entries from the initial
1500
+ * target + fallbackChain, but if a consumer re-shapes the chain and
1501
+ * threads policy through only partially, this filter catches the gap.
1502
+ *
1503
+ * Resolves TT-40 follow-on `policy-block-not-enforced-on-fallback-chain`
1504
+ * (2026-05-15) where mutations_applied recorded the block intent but
1505
+ * the call walker landed on the blocked model anyway.
1506
+ *
1507
+ * Undefined when no filter ran (no blockedModels set). Populated only
1508
+ * when filter ran AND dropped at least one entry — empty drops are
1509
+ * stored as `undefined` to keep brain telemetry quiet on the common
1510
+ * case.
1511
+ */
1512
+ policyBlockedFiltered?: string[];
1513
+ /**
1514
+ * alpha.17. Unique identifier for this call() invocation, generated at
1515
+ * call() entry via crypto.randomUUID(). Returned on success and emitted
1516
+ * as the routing key for Glass-Box observability events
1517
+ * (compile.start, compile.done, execute.attempt, execute.success,
1518
+ * fallback.walked, advisory.fired). Pass the same id to
1519
+ * `subscribe(traceId)` from `@warmdrift/kgauto-compiler/glassbox` to
1520
+ * tap the in-flight event stream.
1521
+ *
1522
+ * Always present on success. Additive, non-breaking.
1523
+ */
1524
+ traceId: string;
1525
+ /**
1526
+ * alpha.44. Best-practice advisories emitted by the compile that actually
1527
+ * served the response (mirrors `CompileResult.advisories`; uses the SERVED
1528
+ * compile on fallback, same as `mutationsApplied`). Empty array when no
1529
+ * rules fired.
1530
+ *
1531
+ * Closes IC's `callForAgent` side-finding (2026-05-26): call() consumers
1532
+ * (agent paths — sweep / dyad / session-title) previously had no way to
1533
+ * `logAdvisories('[kgauto-v2 ...]', advisories)` like compile() consumers
1534
+ * (chat / intake) do, because the advisories were consumed internally
1535
+ * (trace events, brain record) but never returned. Additive, non-breaking.
1536
+ */
1537
+ advisories: BestPracticeAdvisory[];
1538
+ }
1539
+ /**
1540
+ * Thrown when call() exhausts the fallback chain without success.
1541
+ * `attempts` carries every model tried + classification.
1542
+ */
1543
+ declare class CallError extends Error {
1544
+ readonly attempts: CallAttempt[];
1545
+ readonly lastErrorCode?: string;
1546
+ readonly lastStatus?: number;
1547
+ constructor(message: string, attempts: CallAttempt[], lastStatus?: number, lastErrorCode?: string);
1548
+ }
1549
+ interface OracleScore {
1550
+ /** 0..1 overall quality. */
1551
+ score: number;
1552
+ /** Optional per-dimension breakdown. */
1553
+ dimensions?: Record<string, number>;
1554
+ /** Free-form explanation for debugging. */
1555
+ rationale?: string;
1556
+ }
1557
+ /**
1558
+ * alpha.86 — who originated an outcome row (migration 056).
1559
+ *
1560
+ * Absent/undefined means ORGANIC: a real consumer call on behalf of a real
1561
+ * user. Consumers change nothing. Synthetic writers — canaries, smoke
1562
+ * gates, eval replays, probe harnesses — self-mark, so liveness and volume
1563
+ * rules can compute over consumer-originated rows only.
1564
+ *
1565
+ * The incident this closes (cost-watch 2026-08-06 → 08-08): playbacksam's
1566
+ * daily canary wrote exactly 8 rows/day for 13 straight days while PB's
1567
+ * organic traffic was near-zero, and the liveness rule scored PB the
1568
+ * healthiest consumer in the portfolio. On 08-08 EVERY row the brain
1569
+ * received in 24h was canary, and no rule could say so — distinguishing a
1570
+ * replay from real traffic took a hand-reconstructed time-window +
1571
+ * token-fingerprint argument, three mornings running. A consumer whose
1572
+ * only rows are written by kgauto's own machinery is dark, not healthy;
1573
+ * this column is what lets a rule print that sentence.
1574
+ */
1575
+ type OutcomeSource = 'canary' | 'smoke' | 'eval' | 'probe' | 'synthetic';
1576
+ interface RecordInput {
1577
+ /** Handle from CompileResult. */
1578
+ handle: string;
1579
+ /**
1580
+ * alpha.86 — self-mark for synthetic writers (migration 056). OMIT for
1581
+ * real traffic; never write an explicit "organic" value. See
1582
+ * {@link OutcomeSource}.
1583
+ */
1584
+ source?: OutcomeSource;
1585
+ /**
1586
+ * alpha.89 (migration 065) — per-end-user metering. Your own OPAQUE
1587
+ * customer id (hash it upstream if your ids are sensitive; kgauto stores
1588
+ * it verbatim, truncated to 128 chars). With it, the brain becomes a
1589
+ * per-customer COGS ledger: `kgauto_end_user_ledger_v` aggregates calls /
1590
+ * tokens / cost_usd per (app, end user, month) over organic traffic only —
1591
+ * the substrate for usage-based billing and per-customer quota checks.
1592
+ * Omit for traffic that has no end user.
1593
+ */
1594
+ endUserId?: string;
1595
+ /** Actual tokens consumed (post-call). */
1596
+ tokensIn: number;
1597
+ tokensOut: number;
1598
+ /** Wall-clock latency in ms. */
1599
+ latencyMs: number;
1600
+ /** True iff the call returned a usable response. */
1601
+ success: boolean;
1602
+ /** True iff the call returned 0 output tokens despite success. */
1603
+ emptyResponse?: boolean;
1604
+ /** Provider error code if any. */
1605
+ errorType?: string;
1606
+ /** Tools actually invoked by the model. */
1607
+ toolsCalled?: string[];
1608
+ /** Oracle quality score — required for learning to fire. */
1609
+ oracleScore?: OracleScore;
1610
+ /** Optional: scrubbed prompt/response previews for debugging. */
1611
+ promptPreview?: string;
1612
+ responsePreview?: string;
1613
+ /**
1614
+ * The model that ACTUALLY RAN. Set this when consumer-side fallback ran
1615
+ * a different model than v2 compile() targeted. Brain stores this as
1616
+ * `model` (the truth) and the original target as `requested_model`.
1617
+ *
1618
+ * Omit when no fallback occurred — brain stores compile target as `model`
1619
+ * (still the truth in that case) and `requested_model` stays NULL.
1620
+ *
1621
+ * s11 fix: prevents the brain from misattributing fallback traffic to
1622
+ * the originally-requested model.
1623
+ */
1624
+ actualModel?: string;
1625
+ /**
1626
+ * Override `mutations_applied` for this outcome. Set by `call()` when
1627
+ * fallback fires — the served compile's mutations (which actually shaped
1628
+ * the request that went on the wire) replace the initial compile's
1629
+ * mutations (registered against the handle). Without this override, fallback
1630
+ * traffic is attributed to the initial compile's mutations and the brain's
1631
+ * mutation effectiveness stats become misleading.
1632
+ *
1633
+ * alpha.4: extends s11 truth-in-logging to mutations.
1634
+ */
1635
+ mutationsApplied?: string[];
1636
+ /**
1637
+ * Cache read input tokens, when supported by the provider.
1638
+ * - Anthropic: `usage.cache_read_input_tokens`
1639
+ * - Google (implicit caching): `usageMetadata.cachedContentTokenCount`
1640
+ * - OpenAI: `usage.prompt_tokens_details.cached_tokens`
1641
+ *
1642
+ * Powers the cost-and-efficiency-watcher (interfaces/kgauto.md, alpha.4):
1643
+ * `tokens_in - cache_read_input_tokens` is the un-cached new context per call.
1644
+ */
1645
+ cacheReadInputTokens?: number;
1646
+ /**
1647
+ * Cache creation input tokens (Anthropic-specific).
1648
+ * `usage.cache_creation_input_tokens`. The first call that pays the 25%
1649
+ * upcharge to write a cache marker; subsequent calls hit `cacheRead`.
1650
+ */
1651
+ cacheCreationInputTokens?: number;
1652
+ /**
1653
+ * Time to first token (ms). Optional; populated when the provider/SDK
1654
+ * surfaces it. Distinct from `latencyMs` (end-to-end wall clock).
1655
+ */
1656
+ ttftMs?: number;
1657
+ /**
1658
+ * alpha.57 (data-first) — the reasoning-effort tier this call actually ran
1659
+ * at. Overrides the compile-declared `constraints.effort` when both are
1660
+ * present (input wins — same precedence as `mutationsApplied`/`advisories`).
1661
+ * When omitted, record() auto-enriches from the registry-cached compile
1662
+ * declaration. Undefined end-to-end → the `effort` key is absent from the
1663
+ * outcome payload entirely (safe against pre-migration-033 brains).
1664
+ */
1665
+ effort?: EffortLevel;
1666
+ /**
1667
+ * alpha.20 — advisories fired at compile() time. Persisted to the brain's
1668
+ * `compile_outcome_advisories` sibling table via a second POST that fires
1669
+ * AFTER the primary outcome insert succeeds. Best-effort: a failed
1670
+ * advisory POST is logged via onError but does NOT throw or roll back the
1671
+ * primary outcome row.
1672
+ *
1673
+ * Pass `result.advisories` from the CompileResult directly. The brain
1674
+ * uses these to compute the `empty_rate_clean` comparator (rows with
1675
+ * zero advisories fired) so consumers can distinguish "model is bad"
1676
+ * from "client sent a bloated/uncached/malformed request."
1677
+ *
1678
+ * Empty array / undefined → no second POST fires.
1679
+ */
1680
+ advisories?: BestPracticeAdvisory[];
1681
+ /**
1682
+ * alpha.28 — Glass-Box renderer substrate fields (migration 018).
1683
+ *
1684
+ * All optional. When omitted, brain stores NULL and the renderer falls
1685
+ * back to "—" / hidden rows. Library callers (`call.ts`) populate what
1686
+ * they observe; adapter / SDK consumers can populate the rest from their
1687
+ * own provider response surface.
1688
+ */
1689
+ /**
1690
+ * Provider finish reason. Captured from NormalizedResponse.finishReason
1691
+ * (Anthropic `stop_reason`, Google `finishReason`, OpenAI `finish_reason`).
1692
+ * Lower-case canonicalization is the brain's job; consumers can pass
1693
+ * raw provider strings.
1694
+ */
1695
+ finishReason?: string;
1696
+ /**
1697
+ * End-to-end wall-clock latency in ms. Distinct from `latencyMs` only
1698
+ * insofar as `latencyMs` was the historical name for the same metric;
1699
+ * `totalMs` is the new column on `compile_outcomes` (migration 018).
1700
+ * When omitted, brain mirrors `latency_ms`.
1701
+ */
1702
+ totalMs?: number;
1703
+ /** Tools kept after the tool-relevance pass. */
1704
+ toolsCount?: number;
1705
+ /** Number of history messages at compile time. */
1706
+ historyDepth?: number;
1707
+ /** Rendered system prompt size in characters. */
1708
+ systemPromptChars?: number;
1709
+ /** Model originally targeted when a fallback fired. */
1710
+ fellOverFrom?: string;
1711
+ /**
1712
+ * Why the fallback fired — the SAME closed set as CallResult.fallbackReason
1713
+ * (one derivation; the alpha.77 DTS break caught this field's inline copy
1714
+ * drifting when the canonical union grew a member).
1715
+ */
1716
+ fallbackReason?: FallbackReason;
1717
+ /**
1718
+ * alpha.66 — true when the one-shot same-model retry fired during this
1719
+ * call (migration 039 `retried_same_model`). Powers the offline rollup:
1720
+ * retry rate on the cliff class, and — joined with `fell_over_from` —
1721
+ * how often the retry rescued the call vs the chain walking anyway.
1722
+ */
1723
+ retriedSameModel?: boolean;
1724
+ /**
1725
+ * alpha.71 — the precise `errorCode` of the attempt that drove the chain
1726
+ * (migration 044). Set automatically by `call()`; a consumer driving
1727
+ * `record()` by hand may pass it. See `CallResult.fellOverErrorCode` for
1728
+ * why the normalized `fallbackReason` is insufficient on its own.
1729
+ */
1730
+ fellOverErrorCode?: string;
1731
+ }
1732
+ /**
1733
+ * alpha.20 Entry 4: kinds of consumer-declared outcomes feeding the quality
1734
+ * loop. Surfaces in `recordOutcome()` as the verdict the consumer's UX is
1735
+ * forwarding to the brain.
1736
+ *
1737
+ * - `approved` user explicitly approved (thumbs up, "looks good", accepted)
1738
+ * - `rejected` user explicitly rejected (thumbs down, "redo", discarded)
1739
+ * - `partial` accepted with edits or partial use (mixed signal)
1740
+ * - `engaged` user engaged with the output (copy/scroll/dwell)
1741
+ * - `abandoned` user abandoned the response (closed, navigated away)
1742
+ * - `unknown` verdict could not be inferred — recorded for completeness
1743
+ */
1744
+ type OutcomeKind = 'approved' | 'rejected' | 'partial' | 'engaged' | 'abandoned' | 'unknown';
1745
+ /**
1746
+ * Input to `recordOutcome()` — consumer's verdict on a previously-compiled
1747
+ * call. Joins to the original `compile_outcomes` row via outcomeId,
1748
+ * enabling per-(model, archetype) approve-rate measurement once N ≥ 10
1749
+ * outcomes accumulate.
1750
+ */
1751
+ interface RecordOutcomeInput {
1752
+ /**
1753
+ * Joins to compile_outcomes.id. Pass the compile `handle` string
1754
+ * (CompileResult.handle / CallResult.handle) — consumer proxies resolve
1755
+ * handle → compile_outcomes.id via the `compile_outcomes.handle` column
1756
+ * (reference implementations: PB `api/kgauto-v2/compile_outcome_quality.js`,
1757
+ * tt-intel/IC `app/api/kgauto/v2/compile_outcome_quality/route.ts`). A
1758
+ * numeric compile_outcomes.id also works if the caller already has it.
1759
+ * (There is no `CompileResult.outcomeId` field — the brain assigns the row
1760
+ * id at insert; `handle` is the consumer-visible correlator.)
1761
+ */
1762
+ outcomeId: number | string;
1763
+ /** What did the user / system do with this output? */
1764
+ outcome: OutcomeKind;
1765
+ /** Optional 1-5 user rating (e.g., thumbs up/down with intensity, NPS-style). */
1766
+ rating?: 1 | 2 | 3 | 4 | 5;
1767
+ /** Optional free-text reason (e.g., user-typed feedback, system-inferred cause). */
1768
+ reason?: string;
1769
+ /**
1770
+ * Optional model-reported confidence at compile time (0..1). Used for
1771
+ * Brier-score calibration in later phases (alpha.21+) — pair this with
1772
+ * the actual `outcome` to compute calibration error.
1773
+ */
1774
+ observedConfidence?: number;
1775
+ }
1776
+ /**
1777
+ * Return shape of `recordOutcome()`. Never throws — persistence failures
1778
+ * surface as `ok: false` with a stable `reason` string.
1779
+ */
1780
+ interface OutcomeResult {
1781
+ /**
1782
+ * `true` — the POST was acknowledged 2xx (sync mode only).
1783
+ * `'queued'` (alpha.61) — fire-and-forget mode: the write was handed to the
1784
+ * runtime but NOT acknowledged; delivery is unknown at return time. Read
1785
+ * `brainHealth()` or set `BrainConfig.sync` for delivery evidence.
1786
+ * `false` — brain not configured, or the POST failed (sync mode).
1787
+ * Both truthy values pass `if (result.ok)`; pre-alpha.61 code that treated
1788
+ * the fire-and-forget `true` as an ack was reading a false signal.
1789
+ */
1790
+ ok: boolean | 'queued';
1791
+ /** Stable reason code when ok=false. One of: 'brain_not_configured' | 'persistence_failed'. */
1792
+ reason?: string;
1793
+ }
1794
+ /**
1795
+ * alpha.21 (s78 Entry 1): provenance label on a chain entry. Surfaces WHY
1796
+ * an entry sits where it sits so consumers can distinguish:
1797
+ *
1798
+ * - 'measured' brain has N>=10 rows with a measurable quality
1799
+ * outcome backing this placement. The number lives on
1800
+ * `ChainEntry.n`.
1801
+ * - 'capability-fact' inclusion or exclusion driven by a published or
1802
+ * measured CAPABILITY (L-040 cliff, ctx window cap,
1803
+ * structured-output support). Not an opinion — a
1804
+ * fact about what the model can/can't do.
1805
+ * - 'judgment' engineer's pick, no measured backing yet. Cold-start
1806
+ * prior; entirely valid until evidence accumulates.
1807
+ *
1808
+ * "Judgment" is HONEST, not a downgrade. Most of `STARTER_CHAINS` lands here
1809
+ * in alpha.21 — that's the point: consumers can SEE the grounding gap and
1810
+ * prioritize the measurement work that would graduate them to 'measured'.
1811
+ */
1812
+ type Grounding = 'measured' | 'capability-fact' | 'judgment';
1813
+ /**
1814
+ * alpha.21 (s78 Entry 1): a single position in a fallback chain, carrying its
1815
+ * provenance label and an optional human-readable reason. The shape replaces
1816
+ * the old `string[]` representation everywhere chains are surfaced externally.
1817
+ *
1818
+ * `n` is REQUIRED when `grounding === 'measured'` — the runtime helper
1819
+ * `makeMeasuredEntry()` enforces this. For 'capability-fact' and 'judgment'
1820
+ * entries, `n` is undefined.
1821
+ */
1822
+ interface ChainEntry {
1823
+ /** Canonical model id (post-alias). */
1824
+ id: string;
1825
+ /** Why this entry sits in this position. */
1826
+ grounding: Grounding;
1827
+ /**
1828
+ * Optional one-liner explaining the grounding decision. The inline comments
1829
+ * that historically lived next to STARTER_CHAINS entries are now expressed
1830
+ * here as machine-readable text.
1831
+ */
1832
+ reason?: string;
1833
+ /**
1834
+ * When `grounding === 'measured'`, the brain row count that backs this
1835
+ * placement. Undefined for 'capability-fact' and 'judgment' entries.
1836
+ */
1837
+ n?: number;
1838
+ }
1839
+ /**
1840
+ * alpha.21 introspection shape — a per-archetype chain with grounding on
1841
+ * every position. Consumers reading this never see naked string ids;
1842
+ * everything carries provenance.
1843
+ */
1844
+ interface ChainWithGrounding {
1845
+ archetype: IntentArchetypeName;
1846
+ /** Ordered: position 0 = primary, rising index = fallback positions. */
1847
+ entries: ChainEntry[];
1848
+ }
1849
+ /** alpha.23 (s78 Phase 3): per-axis metrics returned by the brain RPC. */
1850
+ interface PerAxisMetrics {
1851
+ appId: string;
1852
+ archetype: string;
1853
+ model: string;
1854
+ windowDays: number;
1855
+ /** Total brain rows for this tuple in the window. */
1856
+ nRows: number;
1857
+ /** Subset of nRows with zero advisories fired — the "clean signal" comparator. */
1858
+ nRowsClean: number;
1859
+ /** Count of compile_outcome_quality entries joining to this tuple's outcomes. */
1860
+ nQualityOutcomes: number;
1861
+ /** Approve rate from quality outcomes. null when nQualityOutcomes === 0. */
1862
+ magicRate: number | null;
1863
+ /** Whether magicRate >= consumer-declared qualityFloor. null when no floor declared OR no outcomes. */
1864
+ qualityFloorMet: boolean | null;
1865
+ costEfficiency: {
1866
+ avgCostUsd: number | null;
1867
+ avgCostUsdClean: number | null;
1868
+ avgInputTokens: number | null;
1869
+ avgOutputTokens: number | null;
1870
+ inputTokenRatio: number | null;
1871
+ };
1872
+ timeEfficiency: {
1873
+ avgLatencyMs: number | null;
1874
+ avgTtftMs: number | null;
1875
+ };
1876
+ reliability: {
1877
+ successRate: number | null;
1878
+ successRateClean: number | null;
1879
+ emptyRate: number | null;
1880
+ emptyRateClean: number | null;
1881
+ };
1882
+ evidenceFreshnessDays: number | null;
1883
+ }
1884
+ /** Per-axis metrics keyed by model — used for chain-comparison views. */
1885
+ type PerAxisMetricsByModel = Record<string, PerAxisMetrics>;
1886
+
1887
+ export { type ApiKeys as A, type BestPracticeAdvisory as B, type CompilePolicy as C, type ToolDefinition as D, type EffortLevel as E, type FallbackReason as F, type Grounding as G, type HistoryCachePolicy as H, type IntentDeclaration as I, captureGoldenIr as J, hasMutation as K, mutationId as L, type Message as M, type NormalizedResponse as N, type OutcomeResult as O, type ProviderOverrides as P, parseGoldenCaptureRate as Q, type RecordInput as R, type SystemModelMessage as S, type ToolCall as T, resolveGoldenCaptureRate as U, shouldCaptureGolden as V, type CompiledRequest as a, type PromptIR as b, type CallOptions as c, type CallResult as d, type CompileResult as e, type SectionRewrite as f, type RecordOutcomeInput as g, type OutcomeSource as h, type OracleScore as i, type Adapter as j, type PerAxisMetrics as k, type Provider as l, type ChainEntry as m, type CallAttempt as n, CallError as o, type ChainModelEntry as p, type ChainWithGrounding as q, type Constraints as r, type GoldenCaptureOptions as s, type MutationApplied as t, type NormalizedTokens as u, type OutcomeKind as v, type PerAxisMetricsByModel as w, type PromptSection as x, type SectionKind as y, type ShadowProbeConfig as z };