@warmdrift/kgauto-compiler 2.0.0-alpha.64 → 2.0.0-alpha.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1718,6 +1718,156 @@ var PROFILES_RAW = [
1718
1718
  classify: 8
1719
1719
  // overkill
1720
1720
  }
1721
+ },
1722
+ // ── Z.ai (GLM) ──
1723
+ // alpha.65 (2026-07-18): first Z.ai profile, provider `zai` added same
1724
+ // release. Step-zero sources (L-049/L-081, all fetched 2026-07-18):
1725
+ // docs.z.ai/guides/llm/glm-5.2 (capabilities), docs.z.ai api-reference
1726
+ // chat-completion (wire params: thinking.type enabled|disabled default
1727
+ // enabled, reasoning_effort max..none, tool_stream, max output 131072),
1728
+ // docs.z.ai/guides/overview/pricing ($1.4 in / $4.4 out, cached input
1729
+ // $0.26, cache storage limited-time free), vLLM recipe (native ctx
1730
+ // 1,048,576). NO AI-trained numbers.
1731
+ {
1732
+ id: "glm-5.2",
1733
+ verifiedAgainstDocs: "2026-07-18",
1734
+ provider: "zai",
1735
+ status: "preview",
1736
+ maxContextTokens: 1048576,
1737
+ maxOutputTokens: 131072,
1738
+ // No documented tool-count cap — conservative default pending measured
1739
+ // evidence; matches the deepseek posture, not a doc number.
1740
+ maxTools: 16,
1741
+ // Hosted-API docs document tools + tool_choice + tool_stream but say
1742
+ // NOTHING about multiple tool_calls per response. Conservative false so
1743
+ // hunt-shaped ranking doesn't over-promise; flip on doc or probe
1744
+ // evidence (L-081: undocumented ≠ true).
1745
+ parallelToolCalls: false,
1746
+ structuredOutput: "native",
1747
+ systemPromptMode: "inline",
1748
+ streaming: true,
1749
+ cliffs: [],
1750
+ costInputPer1m: 1.4,
1751
+ costOutputPer1m: 4.4,
1752
+ lowering: {
1753
+ system: { mode: "inline" },
1754
+ // Automatic prefix caching ("intelligent caching mechanism") — no wire
1755
+ // marker to emit, DeepSeek-style. Cache-hit input $0.26/M (~0.19× of
1756
+ // miss) tracked in notes; storage limited-time free as of 2026-07-18.
1757
+ cache: { strategy: "unsupported" },
1758
+ tools: { format: "openai" },
1759
+ // thinking.type: 'enabled' (server default) | 'disabled'. lowerZai
1760
+ // emits an explicit disable only on forceThinkingZero cliffs.
1761
+ thinking: { field: "thinking.type", default: "auto" }
1762
+ },
1763
+ recovery: [
1764
+ { signal: "rate_limit", action: "escalate", reason: "429 \u2014 escalate" },
1765
+ { signal: "model_not_found", action: "escalate", reason: "Deprecated \u2014 escalate (L-061)" }
1766
+ ],
1767
+ strengths: ["cost", "1m_context", "code", "tool_use", "json_output", "reasoning"],
1768
+ weaknesses: ["parallel_tools"],
1769
+ notes: "GLM-5.2 (Z.ai, 2026-06): agentic-coding flagship. 1,048,576 ctx / 131,072 max out. $1.40/$4.40 per 1M; cached input $0.26/M (automatic caching, no marker; storage limited-time free as of 2026-07-18). Thinking on by default (`thinking.type`), `reasoning_effort` max..none. Parallel tool calls UNDOCUMENTED on the hosted API \u2014 profile says false until doc or probe evidence. status:preview \u2014 no brain evidence yet; earns placement via the machinery.",
1770
+ // Starter hypotheses — verify with telemetry/probes; NO brain evidence
1771
+ // yet. Anchored on the vendor's agentic-coding positioning at a
1772
+ // mid-cost tier, discounted for the unproven parallel-tool story.
1773
+ archetypePerf: {
1774
+ generate: 7,
1775
+ // agentic-coding positioning — starter hypothesis
1776
+ transform: 7,
1777
+ // starter hypothesis
1778
+ plan: 7,
1779
+ // starter hypothesis
1780
+ summarize: 7,
1781
+ // tolerant archetype — starter hypothesis
1782
+ ask: 6,
1783
+ extract: 6,
1784
+ critique: 6,
1785
+ classify: 6,
1786
+ hunt: 5
1787
+ // tools work; parallel unproven → discounted
1788
+ }
1789
+ },
1790
+ // ── Moonshot (Kimi) ──
1791
+ // alpha.65 (2026-07-18): first Moonshot profile, provider `moonshot` added
1792
+ // same release. Step-zero sources (all fetched 2026-07-18):
1793
+ // platform.kimi.ai/docs/pricing/chat-k3 ($3.00 cache-miss in / $0.30
1794
+ // cache-hit in / $15.00 out; 1,048,576 ctx; ToolCalls + JSON Mode +
1795
+ // Partial Mode), kimi-k3-quickstart (max_completion_tokens default
1796
+ // 131072, up to 1,048,576; base https://api.moonshot.ai/v1; vision via
1797
+ // base64 or ms://file-id, NO public image URLs), models-overview
1798
+ // (temperature FIXED 1.0, top_p FIXED 0.95, n FIXED 1, always reasons,
1799
+ // reasoning_effort 'max' only), tool-calls guide ("can choose to call
1800
+ // multiple tools at once … will tend to call them in parallel").
1801
+ {
1802
+ id: "kimi-k3",
1803
+ verifiedAgainstDocs: "2026-07-18",
1804
+ provider: "moonshot",
1805
+ status: "preview",
1806
+ maxContextTokens: 1048576,
1807
+ // Documented ceiling; default is 131,072 when unset.
1808
+ maxOutputTokens: 1048576,
1809
+ // No hard doc cap; docs recommend dynamic tool loading at "dozens or
1810
+ // hundreds" — 64 matches the frontier-agentic posture (sonnet-5 tier).
1811
+ maxTools: 64,
1812
+ parallelToolCalls: true,
1813
+ structuredOutput: "native",
1814
+ systemPromptMode: "inline",
1815
+ streaming: true,
1816
+ cliffs: [],
1817
+ costInputPer1m: 3,
1818
+ costOutputPer1m: 15,
1819
+ lowering: {
1820
+ system: { mode: "inline" },
1821
+ // Automatic context caching — no wire marker. Cache-hit input $0.30/M
1822
+ // (0.1× of miss) tracked in notes.
1823
+ cache: { strategy: "unsupported" },
1824
+ tools: { format: "openai" }
1825
+ // No thinking knob: reasoning is always-on and reasoning_effort
1826
+ // accepts only 'max' (= the server default). Nothing to lower.
1827
+ },
1828
+ recovery: [
1829
+ { signal: "rate_limit", action: "escalate", reason: "429 \u2014 escalate" },
1830
+ { signal: "model_not_found", action: "escalate", reason: "Deprecated \u2014 escalate (L-061)" }
1831
+ ],
1832
+ strengths: ["quality", "reasoning", "1m_context", "parallel_tools", "tool_use", "code", "vision"],
1833
+ // 'latency' → latencyTierOf derives 'slow': always-on max-effort
1834
+ // reasoner with no off switch; no measured p50 yet, the tag is the
1835
+ // honest prior (reasoner family precedent: deepseek-v4-pro ~48s).
1836
+ weaknesses: ["cost", "latency"],
1837
+ notes: "Kimi K3 (Moonshot, API live 2026-07-16): 2.8T-param open-weight flagship, native vision. 1,048,576 ctx; max_completion_tokens default 131,072, ceiling 1,048,576. $3.00/$15.00 per 1M, cache-hit input $0.30/M (automatic caching). Reasoning ALWAYS ON (reasoning_effort max only, cannot disable; responses may carry reasoning_content). Wire quirks: temperature fixed 1.0 / top_p 0.95 / n 1 (kgauto never emits those); vision input must be base64 or ms://file-id \u2014 public image URLs rejected. status:preview \u2014 earns placement via the machinery.",
1838
+ // Starter hypotheses — verify with telemetry/probes; NO brain evidence
1839
+ // yet. Anchored on documented long-horizon-agentic + parallel-tool
1840
+ // strengths; discounted on terse archetypes where always-on reasoning
1841
+ // bills $15/M output for thinking the archetype doesn't need.
1842
+ archetypePerf: {
1843
+ hunt: 8,
1844
+ // parallel tools + long-horizon agentic — starter hypothesis
1845
+ plan: 8,
1846
+ // starter hypothesis
1847
+ generate: 8,
1848
+ // starter hypothesis
1849
+ critique: 7,
1850
+ ask: 7,
1851
+ extract: 7,
1852
+ transform: 7,
1853
+ summarize: 6,
1854
+ // works, but $15/M output reasoning tax on a tolerant archetype
1855
+ classify: 5
1856
+ // always-on reasoning cost + reasoner hedge risk
1857
+ },
1858
+ // Diagnostics-only (no prompt rewrite): reasoner-family hedge risk on
1859
+ // decisive archetypes, carried as a warning until K3-specific probe
1860
+ // evidence exists. The deepseek-reasoner family showed 8/10 judge
1861
+ // rationales citing hedging on classify (exclusion-finding ID 20,
1862
+ // 2026-05-28); K3 is an always-on reasoner and inherits the risk class,
1863
+ // not the conviction.
1864
+ archetypeConventions: [
1865
+ {
1866
+ archetype: "classify",
1867
+ cliffWarning: "kimi-k3 is an always-on reasoner: decisive archetypes (classify) may hedge, and reasoning tokens bill at $15/M output. Family-prior risk (deepseek-reasoner precedent), not K3-measured \u2014 probe before relying on it for classify.",
1868
+ reason: "Reasoner-family prior (exclusion-finding ID 20, 2026-05-28, deepseek-v4-pro on tt-intel/classify). No K3-specific evidence yet \u2014 warning only, no prompt rewrite."
1869
+ }
1870
+ ]
1721
1871
  }
1722
1872
  ];
1723
1873
  var ALIASES = {
@@ -2972,6 +3122,10 @@ function lower(ir, profile, hints = {}) {
2972
3122
  return lowerOpenAI(ir, profile, hints);
2973
3123
  case "deepseek":
2974
3124
  return lowerDeepSeek(ir, profile);
3125
+ case "zai":
3126
+ return lowerZai(ir, profile, hints);
3127
+ case "moonshot":
3128
+ return lowerMoonshot(ir, profile);
2975
3129
  default:
2976
3130
  throw new Error(`No lowering implementation for provider: ${profile.provider}`);
2977
3131
  }
@@ -3260,6 +3414,68 @@ function lowerDeepSeek(ir, profile) {
3260
3414
  }
3261
3415
  };
3262
3416
  }
3417
+ function buildOpenAICompatibleParts(ir) {
3418
+ const ordered = sortSections(ir.sections);
3419
+ const systemText = ordered.map((s) => s.text).join("\n\n");
3420
+ const messages = systemText ? [{ role: "system", content: systemText }] : [];
3421
+ for (const m of ir.history ?? []) {
3422
+ if (m.role === "system") continue;
3423
+ messages.push({ role: m.role, content: m.parts ?? m.content });
3424
+ }
3425
+ if (ir.currentTurn && ir.currentTurn.role !== "system") {
3426
+ messages.push({
3427
+ role: ir.currentTurn.role,
3428
+ content: ir.currentTurn.parts ?? ir.currentTurn.content
3429
+ });
3430
+ }
3431
+ const history = (ir.history ?? []).filter((m) => m.role !== "system");
3432
+ const histMarkIndex = resolveHistoryMarkIndex(history.length, ir.historyCachePolicy);
3433
+ const historyCacheableTokens = histMarkIndex >= 0 ? sumHistoryTokens(history, histMarkIndex) : 0;
3434
+ return {
3435
+ messages,
3436
+ tools: ir.tools && ir.tools.length > 0 ? toOpenAITools(ir.tools) : void 0,
3437
+ response_format: ir.constraints?.structuredOutput ? { type: "json_object" } : void 0,
3438
+ historyCacheableTokens
3439
+ };
3440
+ }
3441
+ function lowerZai(ir, profile, hints) {
3442
+ const parts = buildOpenAICompatibleParts(ir);
3443
+ return {
3444
+ request: {
3445
+ provider: "zai",
3446
+ model: profile.id,
3447
+ messages: parts.messages,
3448
+ tools: parts.tools,
3449
+ response_format: parts.response_format,
3450
+ // Z.ai thinking defaults to enabled server-side; emit an explicit
3451
+ // disable only when a cliff forced thinking off (the GLM analogue of
3452
+ // Gemini's thinkingBudget=0).
3453
+ thinking: hints.forceThinkingZero ? { type: "disabled" } : void 0
3454
+ },
3455
+ diagnostics: {
3456
+ cacheableTokens: 0,
3457
+ historyCacheableTokens: parts.historyCacheableTokens,
3458
+ estimatedCacheSavingsUsd: 0
3459
+ }
3460
+ };
3461
+ }
3462
+ function lowerMoonshot(ir, profile) {
3463
+ const parts = buildOpenAICompatibleParts(ir);
3464
+ return {
3465
+ request: {
3466
+ provider: "moonshot",
3467
+ model: profile.id,
3468
+ messages: parts.messages,
3469
+ tools: parts.tools,
3470
+ response_format: parts.response_format
3471
+ },
3472
+ diagnostics: {
3473
+ cacheableTokens: 0,
3474
+ historyCacheableTokens: parts.historyCacheableTokens,
3475
+ estimatedCacheSavingsUsd: 0
3476
+ }
3477
+ };
3478
+ }
3263
3479
  function sortSections(sections) {
3264
3480
  return [...sections].sort((a, b) => {
3265
3481
  const wa = a.weight ?? 100;
@@ -5035,6 +5251,9 @@ function buildPayload(input, reg) {
5035
5251
  system_prompt_chars: input.systemPromptChars ?? reg?.systemPromptChars,
5036
5252
  fell_over_from: fellOverFrom,
5037
5253
  fallback_reason: fallbackReason,
5254
+ // alpha.66 — omitted-undefined keeps the key out of the JSON body
5255
+ // entirely for non-retry rows (pre-migration-039 brain safety).
5256
+ retried_same_model: input.retriedSameModel,
5038
5257
  // alpha.29 — translator activity (migration 019). Send NULL when no
5039
5258
  // rewrites fired so the brain's "did the translator do anything?"
5040
5259
  // queries can use `IS NOT NULL` cleanly.
@@ -5316,7 +5535,9 @@ var SUPPORTED_PROVIDERS = Object.freeze([
5316
5535
  "anthropic",
5317
5536
  "google",
5318
5537
  "openai",
5319
- "deepseek"
5538
+ "deepseek",
5539
+ "zai",
5540
+ "moonshot"
5320
5541
  ]);
5321
5542
  function isSupportedProvider(p) {
5322
5543
  return SUPPORTED_PROVIDERS.includes(p);
@@ -5329,7 +5550,15 @@ var PROVIDER_ENV_KEYS = Object.freeze({
5329
5550
  "GOOGLE_GENERATIVE_AI_API_KEY"
5330
5551
  ]),
5331
5552
  openai: Object.freeze(["OPENAI_API_KEY"]),
5332
- deepseek: Object.freeze(["DEEPSEEK_API_KEY"])
5553
+ deepseek: Object.freeze(["DEEPSEEK_API_KEY"]),
5554
+ // alpha.65 — Z.ai (GLM family). ZAI_API_KEY is the canonical name; the
5555
+ // Z_AI_API_KEY variant covers the underscore convention some tooling uses
5556
+ // for the brand's "Z.ai" spelling.
5557
+ zai: Object.freeze(["ZAI_API_KEY", "Z_AI_API_KEY"]),
5558
+ // alpha.65 — Moonshot AI (Kimi family). MOONSHOT_API_KEY is canonical
5559
+ // (api.moonshot.ai); KIMI_API_KEY covers the platform.kimi.ai rebrand
5560
+ // surface so consumers keyed under either name resolve.
5561
+ moonshot: Object.freeze(["MOONSHOT_API_KEY", "KIMI_API_KEY"])
5333
5562
  });
5334
5563
  function defaultEnv() {
5335
5564
  return typeof process !== "undefined" && process.env ? process.env : {};
@@ -5381,6 +5610,10 @@ function readBrainReadEnv(envSource = defaultEnv()) {
5381
5610
  if (!anonKey) missingEnv.push("KGAUTO_V2_BRAIN_ANON_KEY");
5382
5611
  return { endpoint, jwt, anonKey, missingEnv };
5383
5612
  }
5613
+ function isSameModelRetryEnabledFromEnv(envSource = defaultEnv()) {
5614
+ const raw = (envSource.KGAUTO_SAME_MODEL_RETRY ?? "").trim().toLowerCase();
5615
+ return raw === "1" || raw === "true";
5616
+ }
5384
5617
 
5385
5618
  // src/streaming.ts
5386
5619
  var ANTHROPIC_URL = "https://api.anthropic.com/v1/messages";
@@ -5673,6 +5906,8 @@ function retryableError(status, code, message, raw) {
5673
5906
  var ANTHROPIC_URL2 = "https://api.anthropic.com/v1/messages";
5674
5907
  var OPENAI_URL = "https://api.openai.com/v1/chat/completions";
5675
5908
  var DEEPSEEK_URL = "https://api.deepseek.com/chat/completions";
5909
+ var ZAI_URL = "https://api.z.ai/api/paas/v4/chat/completions";
5910
+ var MOONSHOT_URL = "https://api.moonshot.ai/v1/chat/completions";
5676
5911
  async function execute(request, opts = {}) {
5677
5912
  const merged = applyOverrides(request, opts.providerOverrides);
5678
5913
  switch (merged.provider) {
@@ -5684,6 +5919,18 @@ async function execute(request, opts = {}) {
5684
5919
  return executeOpenAI(merged, opts);
5685
5920
  case "deepseek":
5686
5921
  return executeDeepSeek(merged, opts);
5922
+ case "zai":
5923
+ return executeOpenAICompatible(
5924
+ merged,
5925
+ opts,
5926
+ { provider: "zai", url: ZAI_URL, missingKeyMessage: "ZAI_API_KEY missing" }
5927
+ );
5928
+ case "moonshot":
5929
+ return executeOpenAICompatible(
5930
+ merged,
5931
+ opts,
5932
+ { provider: "moonshot", url: MOONSHOT_URL, missingKeyMessage: "MOONSHOT_API_KEY missing" }
5933
+ );
5687
5934
  default: {
5688
5935
  const _exhaustive = merged;
5689
5936
  throw new Error(`execute(): no executor for provider: ${JSON.stringify(_exhaustive)}`);
@@ -5833,6 +6080,34 @@ async function executeDeepSeek(request, opts) {
5833
6080
  if (!res.ok) return classifyHttpError2(res.status, json);
5834
6081
  return { ok: true, status: res.status, response: normalizeOpenAILike(json) };
5835
6082
  }
6083
+ async function executeOpenAICompatible(request, opts, spec) {
6084
+ const apiKey = resolveProviderKey(spec.provider, { apiKeys: opts.apiKeys });
6085
+ if (!apiKey) {
6086
+ return terminalError(401, "auth", spec.missingKeyMessage);
6087
+ }
6088
+ if (opts.onChunk) {
6089
+ return streamOpenAILike(spec.url, request, apiKey, spec.provider, {
6090
+ onChunk: opts.onChunk,
6091
+ fetchImpl: opts.fetchImpl
6092
+ });
6093
+ }
6094
+ const { provider: _provider, ...body } = request;
6095
+ const fetchFn = opts.fetchImpl ?? fetch;
6096
+ let res;
6097
+ let json;
6098
+ try {
6099
+ res = await fetchFn(spec.url, {
6100
+ method: "POST",
6101
+ headers: { authorization: `Bearer ${apiKey}`, "content-type": "application/json" },
6102
+ body: JSON.stringify(body)
6103
+ });
6104
+ json = await res.json().catch(() => ({}));
6105
+ } catch (err) {
6106
+ return retryableError2(0, "network_error", String(err), null);
6107
+ }
6108
+ if (!res.ok) return classifyHttpError2(res.status, json);
6109
+ return { ok: true, status: res.status, response: normalizeOpenAILike(json) };
6110
+ }
5836
6111
  function normalizeOpenAILike(raw) {
5837
6112
  const r = raw;
5838
6113
  const choice = r.choices?.[0];
@@ -6690,6 +6965,8 @@ async function call(ir, opts = {}) {
6690
6965
  let activeCompile = initial;
6691
6966
  let lastErr;
6692
6967
  const failedProviders = /* @__PURE__ */ new Set();
6968
+ const sameModelRetryEnabled = opts.sameModelRetry ?? isSameModelRetryEnabledFromEnv();
6969
+ let retriedSameModel = false;
6693
6970
  for (let i = 0; i < targetsToTry.length; i++) {
6694
6971
  const targetModel = targetsToTry[i];
6695
6972
  const targetProfile = tryGetProfile(targetModel);
@@ -6727,15 +7004,37 @@ async function call(ir, opts = {}) {
6727
7004
  );
6728
7005
  const targetSupportsStreaming = targetProfile?.streaming === true;
6729
7006
  const streamingOnChunk = opts.onChunk && !opts.noStream && targetSupportsStreaming ? opts.onChunk : void 0;
6730
- const exec = await execute(activeCompile.request, {
7007
+ const execOpts = {
6731
7008
  apiKeys: opts.apiKeys,
6732
7009
  fetchImpl: opts.fetchImpl,
6733
7010
  providerOverrides: opts.providerOverrides,
6734
7011
  onChunk: streamingOnChunk
6735
- });
6736
- const validated = exec.ok ? validateStructuredContract(exec, ir) : exec;
7012
+ };
7013
+ const exec = await execute(activeCompile.request, execOpts);
7014
+ let validated = exec.ok ? validateStructuredContract(exec, ir) : exec;
7015
+ let servedByRetry = false;
7016
+ if (!validated.ok && isStructuredContractViolation(validated.errorCode) && sameModelRetryEnabled && !retriedSameModel) {
7017
+ retriedSameModel = true;
7018
+ attempts.push({
7019
+ model: targetModel,
7020
+ status: validated.errorType,
7021
+ errorCode: validated.errorCode,
7022
+ message: validated.message
7023
+ });
7024
+ const retryRequest = validated.errorCode === "max_tokens_on_structured_output" ? raiseOutputBudget(activeCompile.request, targetProfile) : activeCompile.request;
7025
+ safeEmit(
7026
+ () => emitExecuteAttempt(traceId, ir.appId, { model: targetModel, attemptIndex: i })
7027
+ );
7028
+ const retryExec = await execute(retryRequest, execOpts);
7029
+ validated = retryExec.ok ? validateStructuredContract(retryExec, ir) : retryExec;
7030
+ servedByRetry = true;
7031
+ }
6737
7032
  if (validated.ok) {
6738
- attempts.push({ model: targetModel, status: "success" });
7033
+ attempts.push({
7034
+ model: targetModel,
7035
+ status: "success",
7036
+ ...servedByRetry ? { sameModelRetry: true } : {}
7037
+ });
6739
7038
  const latencyMs2 = Date.now() - start;
6740
7039
  safeEmit(
6741
7040
  () => emitExecuteSuccess(traceId, ir.appId, {
@@ -6772,7 +7071,10 @@ async function call(ir, opts = {}) {
6772
7071
  finishReason: validated.response.finishReason,
6773
7072
  totalMs: latencyMs2,
6774
7073
  fellOverFrom: fellOver ? initial.target : void 0,
6775
- fallbackReason
7074
+ fallbackReason,
7075
+ // alpha.66 — migration 039. Omitted (not false) when no retry fired
7076
+ // so pre-039 brains never see the key.
7077
+ retriedSameModel: retriedSameModel || void 0
6776
7078
  });
6777
7079
  if (fellOver) {
6778
7080
  const firstFailed = attempts.find((a) => a.status !== "success");
@@ -6830,6 +7132,7 @@ async function call(ir, opts = {}) {
6830
7132
  servedBy: targetModel,
6831
7133
  fellOverFrom: fellOver ? initial.target : void 0,
6832
7134
  fallbackReason,
7135
+ retriedSameModel: retriedSameModel || void 0,
6833
7136
  unreachableFiltered,
6834
7137
  policyBlockedFiltered,
6835
7138
  traceId,
@@ -6844,7 +7147,8 @@ async function call(ir, opts = {}) {
6844
7147
  model: targetModel,
6845
7148
  status: validated.errorType,
6846
7149
  errorCode: validated.errorCode,
6847
- message: validated.message
7150
+ message: validated.message,
7151
+ ...servedByRetry ? { sameModelRetry: true } : {}
6848
7152
  });
6849
7153
  lastErr = validated;
6850
7154
  if (validated.errorType === "terminal" || opts.noFallback) {
@@ -6855,15 +7159,20 @@ async function call(ir, opts = {}) {
6855
7159
  break;
6856
7160
  }
6857
7161
  }
7162
+ const lastAttempted = [...attempts].reverse().find(
7163
+ (a) => a.status !== "success" && a.errorCode !== "auth_inferred" && a.errorCode !== "compile_error"
7164
+ );
6858
7165
  const latencyMs = Date.now() - start;
6859
7166
  await record({
6860
7167
  handle: initial.handle,
6861
- tokensIn: 0,
6862
- tokensOut: 0,
7168
+ tokensIn: lastErr?.tokens?.input ?? 0,
7169
+ tokensOut: lastErr?.tokens?.output ?? 0,
6863
7170
  latencyMs,
6864
7171
  success: false,
6865
7172
  errorType: lastErr?.errorCode,
6866
- promptPreview: extractPromptPreview(ir)
7173
+ promptPreview: extractPromptPreview(ir),
7174
+ actualModel: lastAttempted?.model,
7175
+ retriedSameModel: retriedSameModel || void 0
6867
7176
  });
6868
7177
  const filteredNote = unreachableFiltered && unreachableFiltered.length > 0 ? ` (also auto-filtered: [${unreachableFiltered.join(", ")}] \u2014 no API key)` : "";
6869
7178
  const blockedNote = policyBlockedFiltered && policyBlockedFiltered.length > 0 ? ` (also policy-blocked: [${policyBlockedFiltered.join(", ")}])` : "";
@@ -7108,7 +7417,10 @@ function validateStructuredContract(exec, ir) {
7108
7417
  errorType: "retryable",
7109
7418
  errorCode: "max_tokens_on_structured_output",
7110
7419
  message: `Provider returned finishReason="${exec.response.finishReason}" on a structured-output call \u2014 output truncated mid-token, JSON cannot be valid`,
7111
- raw: exec.response.raw
7420
+ raw: exec.response.raw,
7421
+ // alpha.66 — the provider DID serve this call; carry the real usage so
7422
+ // exhausted-chain failure rows aren't stamped tokens_in=0 (PB 05-18 ask 4).
7423
+ tokens: { input: exec.response.tokens.input, output: exec.response.tokens.output }
7112
7424
  };
7113
7425
  }
7114
7426
  if (!exec.response.text) {
@@ -7124,10 +7436,35 @@ function validateStructuredContract(exec, ir) {
7124
7436
  errorType: "retryable",
7125
7437
  errorCode: "structured_output_parse_failed",
7126
7438
  message: err instanceof Error ? err.message : String(err),
7127
- raw: exec.response.raw
7439
+ raw: exec.response.raw,
7440
+ tokens: { input: exec.response.tokens.input, output: exec.response.tokens.output }
7128
7441
  };
7129
7442
  }
7130
7443
  }
7444
+ function isStructuredContractViolation(code) {
7445
+ return code === "max_tokens_on_structured_output" || code === "structured_output_parse_failed";
7446
+ }
7447
+ function raiseOutputBudget(request, profile) {
7448
+ if (!profile) return request;
7449
+ if (request.provider === "anthropic") {
7450
+ if (typeof request.max_tokens === "number" && request.max_tokens < profile.maxOutputTokens) {
7451
+ return { ...request, max_tokens: profile.maxOutputTokens };
7452
+ }
7453
+ return request;
7454
+ }
7455
+ if (request.provider === "google") {
7456
+ const gc = request.generationConfig;
7457
+ const cap = gc?.maxOutputTokens;
7458
+ if (typeof cap === "number" && cap < profile.maxOutputTokens) {
7459
+ return {
7460
+ ...request,
7461
+ generationConfig: { ...gc, maxOutputTokens: profile.maxOutputTokens }
7462
+ };
7463
+ }
7464
+ return request;
7465
+ }
7466
+ return request;
7467
+ }
7131
7468
  function normalizeFallbackReason(attempts) {
7132
7469
  const first = attempts.find((a) => a.status !== "success");
7133
7470
  if (!first) return void 0;
@@ -7160,6 +7497,8 @@ function safeEmit(fn) {
7160
7497
  var _internal = {
7161
7498
  compileAndRegister,
7162
7499
  validateStructuredContract,
7500
+ isStructuredContractViolation,
7501
+ raiseOutputBudget,
7163
7502
  extractPromptPreview,
7164
7503
  normalizeFallbackReason,
7165
7504
  generateTraceId,
@@ -7948,7 +8287,7 @@ function createBrainForwardRoutes(config) {
7948
8287
  }
7949
8288
 
7950
8289
  // src/version.ts
7951
- var LIBRARY_VERSION = "2.0.0-alpha.64";
8290
+ var LIBRARY_VERSION = "2.0.0-alpha.66";
7952
8291
 
7953
8292
  // src/key-health.ts
7954
8293
  var JSON_HEADERS2 = { "Content-Type": "application/json" };
@@ -7997,6 +8336,14 @@ var PROBE_SPECS = [
7997
8336
  return Number.isFinite(n) ? n : void 0;
7998
8337
  }
7999
8338
  },
8339
+ {
8340
+ provider: "moonshot",
8341
+ canonicalEnvName: "MOONSHOT_API_KEY",
8342
+ buildRequest: (key) => ({
8343
+ url: "https://api.moonshot.ai/v1/models",
8344
+ headers: { Authorization: `Bearer ${key}` }
8345
+ })
8346
+ },
8000
8347
  {
8001
8348
  provider: "google",
8002
8349
  canonicalEnvName: "GEMINI_API_KEY",
@@ -8028,6 +8375,11 @@ function createKeyHealthRoute(config) {
8028
8375
  const aiSdk = envKey(env, "GOOGLE_GENERATIVE_AI_API_KEY");
8029
8376
  key = gemini ?? google ?? aiSdk;
8030
8377
  envName = gemini ? "GEMINI_API_KEY" : google ? "GOOGLE_API_KEY" : aiSdk ? "GOOGLE_GENERATIVE_AI_API_KEY" : "GEMINI_API_KEY";
8378
+ } else if (spec.provider === "moonshot") {
8379
+ const moonshot = envKey(env, "MOONSHOT_API_KEY");
8380
+ const kimi = envKey(env, "KIMI_API_KEY");
8381
+ key = moonshot ?? kimi;
8382
+ envName = moonshot ? "MOONSHOT_API_KEY" : kimi ? "KIMI_API_KEY" : "MOONSHOT_API_KEY";
8031
8383
  } else {
8032
8384
  key = envKey(env, spec.canonicalEnvName);
8033
8385
  }