@juspay/neurolink 12.20.1 → 12.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -175,6 +175,22 @@ declare function trackUpstreamReadableStream(source: ReadableStream<Uint8Array>)
175
175
  * diagnostics the operator wants.
176
176
  */
177
177
  declare function redactHeadersForBorrower(headers: Record<string, string>): Record<string, string>;
178
+ /**
179
+ * The model that actually served a fallback turn.
180
+ *
181
+ * One request can leave more than one leg's model in metadata: the Codex leg
182
+ * records its model on the failure path before the chain moves on, and the
183
+ * configured chain tries Codex first. Choosing by key precedence therefore
184
+ * billed a Vertex-served turn as the Codex model — the exact misattribution
185
+ * this file's pricing fix exists to prevent. The account type that reached
186
+ * `logFinalRequest` is the only authoritative signal for which leg answered,
187
+ * so select on that and ignore the other leg's leftovers.
188
+ */
189
+ export declare function resolveServedFallbackModel(metadata: {
190
+ codexFallbackModel?: unknown;
191
+ vertexFallbackModel?: unknown;
192
+ }, accountType: string | undefined): string | undefined;
193
+ declare function clearClaudeFallbackTerminalEvidence(ctx: ServerContext): void;
178
194
  declare function executeClaudeFallbackTranslation(args: {
179
195
  ctx: ServerContext;
180
196
  body: ClaudeRequest;
@@ -462,6 +478,7 @@ export declare function isTransientHttpFailure(status: number, errBody: string):
462
478
  export declare function isUpstreamOverload(status: number, errBody: string): boolean;
463
479
  export declare function redactProviderErrorMessage(message: string): string;
464
480
  export declare const __testHooks: {
481
+ clearClaudeFallbackTerminalEvidence: typeof clearClaudeFallbackTerminalEvidence;
465
482
  normalizeQuotaForAccounts: typeof normalizeQuotaForAccounts;
466
483
  resolveHomeIndex: typeof resolveHomeIndex;
467
484
  maybeResetPrimaryToHome: typeof maybeResetPrimaryToHome;
@@ -100,6 +100,16 @@ function resolveRequestLogAccountIdentity(accountLabel, accountType) {
100
100
  provider: "anthropic",
101
101
  };
102
102
  }
103
+ if (accountType === "vertex") {
104
+ // A native fallback leg has no OAuth login — the account IS the served
105
+ // model, e.g. "vertex/claude-opus-4-6". Without this branch `provider`
106
+ // stays unset, `annotateRequestPricing` falls back to
107
+ // "openai-compatible", and that resolves to a cross-provider scan which
108
+ // prices the turn from the *requested* alias in the Anthropic table
109
+ // instead of the served model in the Vertex one — a 2.5x under-report on
110
+ // the only leg that bills in real currency.
111
+ return { accountKey: accountLabel, provider: "vertex" };
112
+ }
103
113
  return {};
104
114
  }
105
115
  // ---------------------------------------------------------------------------
@@ -3567,11 +3577,29 @@ async function loadClaudeProxyAccounts(args) {
3567
3577
  isClaudeClientRequest: isLikelyClaudeClient(clientHeaders, clientSnapshotBody),
3568
3578
  };
3569
3579
  }
3580
+ /**
3581
+ * The model that actually served a fallback turn.
3582
+ *
3583
+ * One request can leave more than one leg's model in metadata: the Codex leg
3584
+ * records its model on the failure path before the chain moves on, and the
3585
+ * configured chain tries Codex first. Choosing by key precedence therefore
3586
+ * billed a Vertex-served turn as the Codex model — the exact misattribution
3587
+ * this file's pricing fix exists to prevent. The account type that reached
3588
+ * `logFinalRequest` is the only authoritative signal for which leg answered,
3589
+ * so select on that and ignore the other leg's leftovers.
3590
+ */
3591
+ export function resolveServedFallbackModel(metadata, accountType) {
3592
+ const served = accountType === "vertex"
3593
+ ? metadata.vertexFallbackModel
3594
+ : metadata.codexFallbackModel;
3595
+ return typeof served === "string" && served.length > 0 ? served : undefined;
3596
+ }
3570
3597
  function clearClaudeFallbackTerminalEvidence(ctx) {
3571
3598
  for (const key of [
3572
3599
  "codexFallbackAccount",
3573
3600
  "codexFallbackAccountType",
3574
3601
  "codexFallbackModel",
3602
+ "vertexFallbackModel",
3575
3603
  "codexFallbackFailureUsage",
3576
3604
  "codexFallbackDeferredFailure",
3577
3605
  "sdkFallbackFailure",
@@ -4054,6 +4082,19 @@ async function tryConfiguredClaudeFallbackChain(args) {
4054
4082
  // the caller's pending tool call, which strands every agentic turn.
4055
4083
  const vertexModel = fallback.model;
4056
4084
  const vertexAccount = `vertex/${vertexModel}`;
4085
+ // A Codex leg that already failed left its own terminal evidence
4086
+ // behind — its model, account, deferred-failure flag and failure
4087
+ // usage. Every one of those is read again when this turn finalizes:
4088
+ // the model would be logged and priced in place of the Vertex one,
4089
+ // the deferred-failure branch would re-attribute the tracer to
4090
+ // "openai" after the Vertex leg set it, and the failure usage would
4091
+ // merge its reasoning tokens into this turn's counts. Start the leg
4092
+ // clean, the same way a fresh upstream dispatch does.
4093
+ clearClaudeFallbackTerminalEvidence(ctx);
4094
+ // Billing follows the model that served the turn. The request log
4095
+ // otherwise records `body.model` — the alias the client asked for —
4096
+ // and prices Opus-on-Vertex at Sonnet rates.
4097
+ ctx.metadata.vertexFallbackModel = vertexModel;
4057
4098
  response = await executeVertexAnthropicFallback({
4058
4099
  body,
4059
4100
  model: vertexModel,
@@ -6212,18 +6253,24 @@ function createClaudeRequestRuntimeContext(args) {
6212
6253
  recordFinalSuccess(finalAccountLabel, finalAccountType);
6213
6254
  }
6214
6255
  const traceCtx = tracer?.getTraceContext();
6256
+ const servedModel = resolveServedFallbackModel(ctx.metadata, finalAccountType);
6215
6257
  logRequest({
6216
6258
  timestamp: new Date().toISOString(),
6217
6259
  requestId: ctx.requestId,
6218
6260
  method: ctx.method,
6219
6261
  path: ctx.path,
6220
- model: typeof ctx.metadata.codexFallbackModel === "string"
6221
- ? ctx.metadata.codexFallbackModel
6222
- : body.model,
6223
- ...(typeof ctx.metadata.codexFallbackModel === "string"
6262
+ model: servedModel ?? body.model,
6263
+ ...(servedModel
6224
6264
  ? {
6225
6265
  requestedModel: body.model,
6226
- inputIncludesCachedTokens: false,
6266
+ // Codex reports input inclusive of cached tokens; the Vertex
6267
+ // Anthropic wire reports them separately, exactly as the native
6268
+ // path does, so the flag stays Codex-only. Keyed off the serving
6269
+ // account for the same reason `servedModel` is: a failed Codex leg
6270
+ // leaves its model behind on a turn Vertex went on to serve.
6271
+ ...(finalAccountType === "vertex"
6272
+ ? {}
6273
+ : { inputIncludesCachedTokens: false }),
6227
6274
  }
6228
6275
  : {}),
6229
6276
  ...(ctx.metadata.contextPreflight
@@ -8777,6 +8824,7 @@ export function redactProviderErrorMessage(message) {
8777
8824
  // spinning up a full proxy. Keep this surface small.
8778
8825
  // ---------------------------------------------------------------------------
8779
8826
  export const __testHooks = {
8827
+ clearClaudeFallbackTerminalEvidence,
8780
8828
  normalizeQuotaForAccounts,
8781
8829
  resolveHomeIndex,
8782
8830
  maybeResetPrimaryToHome,
@@ -296,6 +296,10 @@ export type ClassifierRouterInput = {
296
296
  requiresVision?: boolean;
297
297
  thinkingLevel?: string;
298
298
  sessionId?: string;
299
+ /** Whether this request is tied to a session, independent of the (withheld) session id itself. */
300
+ sessionBound?: boolean;
301
+ /** Number of prior conversation messages the caller supplied, when known. */
302
+ priorMessageCount?: number;
299
303
  };
300
304
  /** Enriched per-model metadata used while ranking pool members. */
301
305
  export type ClassifierModelMeta = {
@@ -1296,6 +1296,18 @@ export type AutoresearchInitArgs = {
1296
1296
  provider?: string;
1297
1297
  model?: string;
1298
1298
  };
1299
+ /** Arguments for `neurolink decide` — the third inference type's CLI surface. */
1300
+ export type CliDecideArgs = {
1301
+ state?: string;
1302
+ stateFile?: string;
1303
+ questions?: string;
1304
+ questionsFile?: string;
1305
+ provider?: string;
1306
+ model?: string;
1307
+ timeout?: number;
1308
+ format?: "text" | "json";
1309
+ debug?: boolean;
1310
+ };
1299
1311
  /** Base options shared across all `neurolink evaluate` subcommands. */
1300
1312
  export type BaseEvaluateArgs = {
1301
1313
  json?: boolean;
@@ -2281,6 +2281,14 @@ export type VertexAnthropicTool = {
2281
2281
  };
2282
2282
  cache_control?: VertexAnthropicCacheControl;
2283
2283
  };
2284
+ /**
2285
+ * Supplies the bearer token for a Vertex publisher call.
2286
+ *
2287
+ * Exists so a test can stand in for the Google credential lookup: gaxios
2288
+ * resolves its transport to node-fetch rather than `globalThis.fetch`, so a
2289
+ * test that swaps the global cannot intercept the token exchange.
2290
+ */
2291
+ export type VertexAccessTokenProvider = () => Promise<string | null | undefined>;
2284
2292
  /** Input to `applyVertexAnthropicCacheBreakpoints`. */
2285
2293
  export type VertexAnthropicCacheInput = {
2286
2294
  system?: string;
@@ -42,10 +42,27 @@ export type ModelRouterInterface = {
42
42
  getModelMappings?: () => ModelMapping[];
43
43
  getPassthroughModels?: () => string[];
44
44
  };
45
+ /**
46
+ * Anthropic prompt-cache breakpoint marker.
47
+ *
48
+ * Anthropic caching is explicit: the prefix up to each marker is cached, and a
49
+ * request carrying none is cached not at all. Four markers is the hard ceiling.
50
+ */
51
+ export type ClaudeCacheControl = {
52
+ type: "ephemeral";
53
+ /**
54
+ * How long the marked prefix stays cached. The API accepts a 5-minute or a
55
+ * 1-hour window, priced at 1.25x and 2x base input respectively; omitting it
56
+ * means 5 minutes. Without this field a caller cannot express the 1-hour
57
+ * marker at all.
58
+ */
59
+ ttl?: "5m" | "1h";
60
+ };
45
61
  /** A single text block in a Claude content array. */
46
62
  export type ClaudeTextBlock = {
47
63
  type: "text";
48
64
  text: string;
65
+ cache_control?: ClaudeCacheControl;
49
66
  };
50
67
  /** A single image block in a Claude content array. */
51
68
  export type ClaudeImageBlock = {
@@ -69,6 +86,7 @@ export type ClaudeToolResultBlock = {
69
86
  type: "tool_result";
70
87
  tool_use_id: string;
71
88
  content: string | ClaudeContentBlock[];
89
+ cache_control?: ClaudeCacheControl;
72
90
  };
73
91
  /** A thinking/reasoning block in a Claude content array. */
74
92
  export type ClaudeThinkingBlock = {
@@ -86,6 +104,7 @@ export type ClaudeTool = {
86
104
  name: string;
87
105
  description?: string;
88
106
  input_schema: Record<string, unknown>;
107
+ cache_control?: ClaudeCacheControl;
89
108
  };
90
109
  /** Metadata attached to a Claude Messages API request. */
91
110
  export type ClaudeMetadata = {
@@ -99,10 +118,7 @@ export type ClaudeRequest = {
99
118
  model: string;
100
119
  messages: ClaudeMessage[];
101
120
  max_tokens: number;
102
- system?: string | Array<{
103
- type: "text";
104
- text: string;
105
- }>;
121
+ system?: string | ClaudeTextBlock[];
106
122
  temperature?: number;
107
123
  top_p?: number;
108
124
  top_k?: number;
@@ -1,4 +1,4 @@
1
- import type { VertexAnthropicCacheInput, VertexAnthropicCacheOutput, VertexAnthropicMessage } from "../types/index.js";
1
+ import type { ClaudeRequest, VertexAnthropicCacheInput, VertexAnthropicCacheOutput, VertexAnthropicMessage } from "../types/index.js";
2
2
  /** Anthropic allows at most four `cache_control` breakpoints per request. */
3
3
  export declare const ANTHROPIC_MAX_CACHE_BREAKPOINTS = 4;
4
4
  /**
@@ -41,3 +41,24 @@ export declare function countAnthropicCacheMarkers(input: {
41
41
  * the input array is cloned, never mutated.
42
42
  */
43
43
  export declare function applyAnthropicHistoryCacheBreakpoints(input: ReadonlyArray<VertexAnthropicMessage>, budget: number): VertexAnthropicMessage[];
44
+ /**
45
+ * Breakpoint placement for a Claude Messages request the proxy built itself,
46
+ * rather than one a client authored.
47
+ *
48
+ * A request translated from another wire format arrives with no markers at
49
+ * all, because the format it came from has no way to express one: OpenAI-shaped
50
+ * callers rely on automatic prefix caching, which Anthropic does not do. A
51
+ * faithful translation is therefore cached at 0% on every turn, and a caller
52
+ * that fell back here from an exhausted pool burns the replacement quota faster
53
+ * than the one it was spared.
54
+ *
55
+ * Placement follows Anthropic's `tools → system → messages` rendering: one
56
+ * marker on the last system block covers tools *and* system in a single
57
+ * breakpoint, and the rest roll along the tail of the history so a growing
58
+ * conversation keeps its settled prefix cached. Markers the caller supplied are
59
+ * counted first and never displaced — this only spends what is left of the
60
+ * four.
61
+ *
62
+ * Pure: inputs are cloned, never mutated.
63
+ */
64
+ export declare function applyClaudeRequestCacheBreakpoints(request: ClaudeRequest): ClaudeRequest;
@@ -148,3 +148,116 @@ function markLastContentBlock(messages, i) {
148
148
  messages[i] = { ...message, content };
149
149
  return true;
150
150
  }
151
+ /**
152
+ * Breakpoint placement for a Claude Messages request the proxy built itself,
153
+ * rather than one a client authored.
154
+ *
155
+ * A request translated from another wire format arrives with no markers at
156
+ * all, because the format it came from has no way to express one: OpenAI-shaped
157
+ * callers rely on automatic prefix caching, which Anthropic does not do. A
158
+ * faithful translation is therefore cached at 0% on every turn, and a caller
159
+ * that fell back here from an exhausted pool burns the replacement quota faster
160
+ * than the one it was spared.
161
+ *
162
+ * Placement follows Anthropic's `tools → system → messages` rendering: one
163
+ * marker on the last system block covers tools *and* system in a single
164
+ * breakpoint, and the rest roll along the tail of the history so a growing
165
+ * conversation keeps its settled prefix cached. Markers the caller supplied are
166
+ * counted first and never displaced — this only spends what is left of the
167
+ * four.
168
+ *
169
+ * Pure: inputs are cloned, never mutated.
170
+ */
171
+ export function applyClaudeRequestCacheBreakpoints(request) {
172
+ const existing = countAnthropicCacheMarkers({
173
+ system: request.system,
174
+ tools: request.tools,
175
+ messages: [],
176
+ });
177
+ const inMessages = request.messages.reduce((n, m) => n +
178
+ (Array.isArray(m.content)
179
+ ? m.content.filter((b) => "cache_control" in b && b.cache_control)
180
+ .length
181
+ : 0), 0);
182
+ let budget = MAX_BREAKPOINTS - existing - inMessages;
183
+ if (budget <= 0) {
184
+ return request;
185
+ }
186
+ const out = { ...request };
187
+ // One marker on the stable head. The last system block is the better anchor
188
+ // than the last tool, because system renders after tools and so a single
189
+ // marker there caches both.
190
+ if (Array.isArray(out.system) && out.system.length > 0 && existing === 0) {
191
+ const system = out.system.map((b) => ({ ...b }));
192
+ system[system.length - 1] = {
193
+ ...system[system.length - 1],
194
+ cache_control: CLAUDE_EPHEMERAL,
195
+ };
196
+ out.system = system;
197
+ budget--;
198
+ }
199
+ else if ((!out.system || (Array.isArray(out.system) && out.system.length === 0)) &&
200
+ out.tools &&
201
+ out.tools.length > 0 &&
202
+ existing === 0) {
203
+ const tools = out.tools.map((t) => ({ ...t }));
204
+ tools[tools.length - 1] = {
205
+ ...tools[tools.length - 1],
206
+ cache_control: CLAUDE_EPHEMERAL,
207
+ };
208
+ out.tools = tools;
209
+ budget--;
210
+ }
211
+ if (budget > 0) {
212
+ out.messages = applyClaudeHistoryBreakpoints(out.messages, budget);
213
+ }
214
+ return out;
215
+ }
216
+ const CLAUDE_EPHEMERAL = { type: "ephemeral" };
217
+ /**
218
+ * Roll `budget` markers backwards along the history, marking the last content
219
+ * block of each message that can carry one. Only text and tool_result blocks
220
+ * accept `cache_control`; a message ending in anything else is skipped without
221
+ * spending budget.
222
+ *
223
+ * A message whose content is a plain string is promoted to the single text
224
+ * block the marker has to live on. Skipping strings instead would have left an
225
+ * ordinary conversation with no history breakpoint at all, since the OpenAI
226
+ * bridge emits every plain-text turn as a string — which is precisely the
227
+ * growing history this is here to keep cached.
228
+ */
229
+ function applyClaudeHistoryBreakpoints(input, budget) {
230
+ const messages = input.map((m) => ({ ...m }));
231
+ let remaining = Math.max(0, Math.min(budget, MAX_BREAKPOINTS));
232
+ for (let i = messages.length - 1; i >= 0 && remaining > 0; i--) {
233
+ const content = messages[i].content;
234
+ if (typeof content === "string") {
235
+ if (content.length === 0) {
236
+ continue;
237
+ }
238
+ messages[i] = {
239
+ ...messages[i],
240
+ content: [
241
+ { type: "text", text: content, cache_control: CLAUDE_EPHEMERAL },
242
+ ],
243
+ };
244
+ remaining--;
245
+ continue;
246
+ }
247
+ if (!Array.isArray(content) || content.length === 0) {
248
+ continue;
249
+ }
250
+ const last = content[content.length - 1];
251
+ if (last.type !== "text" && last.type !== "tool_result") {
252
+ continue;
253
+ }
254
+ if (last.cache_control) {
255
+ continue;
256
+ }
257
+ const blocks = content.map((b) => ({ ...b }));
258
+ blocks[blocks.length - 1] = { ...last, cache_control: CLAUDE_EPHEMERAL };
259
+ messages[i] = { ...messages[i], content: blocks };
260
+ remaining--;
261
+ }
262
+ return messages;
263
+ }
@@ -19,6 +19,7 @@ declare class NeuroLinkLogger {
19
19
  private logs;
20
20
  private maxLogs;
21
21
  private isDebugMode;
22
+ private diagnosticsToStderr;
22
23
  private eventEmitter?;
23
24
  constructor();
24
25
  /**
@@ -49,6 +50,14 @@ declare class NeuroLinkLogger {
49
50
  * @param level - The minimum log level to process ("debug", "info", "warn", or "error")
50
51
  */
51
52
  setLogLevel(level: LogLevel): void;
53
+ /**
54
+ * Routes debug and info output to stderr instead of stdout. The CLI turns
55
+ * this on for `--format json`, where stdout carries the payload and any
56
+ * diagnostic line in it would make the output unparseable.
57
+ *
58
+ * @param enabled - True to send debug/info to stderr; false restores stdout
59
+ */
60
+ setDiagnosticsToStderr(enabled: boolean): void;
52
61
  /**
53
62
  * Determines whether a message with the given log level should be processed.
54
63
  * This method considers both the configured log level and the current debug mode.
@@ -222,6 +231,7 @@ export declare const logger: {
222
231
  table: (data: unknown) => void;
223
232
  shouldLog: (level: LogLevel) => boolean;
224
233
  setLogLevel: (level: LogLevel) => void;
234
+ setDiagnosticsToStderr: (enabled: boolean) => void;
225
235
  getLogs: (level?: LogLevel) => LogEntry[];
226
236
  clearLogs: () => void;
227
237
  setEventEmitter: (emitter: {
@@ -36,11 +36,24 @@ const UPPERCASE_LOG_LEVELS = {
36
36
  warn: "WARN",
37
37
  error: "ERROR",
38
38
  };
39
+ const FORMAT_FLAGS = ["--format", "-f", "--output-format"];
40
+ /**
41
+ * True when the process was started with JSON output requested. Read from
42
+ * argv on every write, like the `--debug` check in `shouldLog`: modules log
43
+ * while they are still being imported, before the CLI middleware that calls
44
+ * `setDiagnosticsToStderr` has run.
45
+ */
46
+ function argvRequestsJsonOutput() {
47
+ const argv = process.argv;
48
+ return argv.some((arg, index) => (FORMAT_FLAGS.includes(arg) && argv[index + 1] === "json") ||
49
+ FORMAT_FLAGS.some((flag) => arg === `${flag}=json`));
50
+ }
39
51
  class NeuroLinkLogger {
40
52
  logLevel = "info";
41
53
  logs = [];
42
54
  maxLogs = 1000;
43
55
  isDebugMode;
56
+ diagnosticsToStderr = false;
44
57
  eventEmitter;
45
58
  constructor() {
46
59
  // Cache debug mode check to avoid repeated array searches
@@ -86,6 +99,16 @@ class NeuroLinkLogger {
86
99
  setLogLevel(level) {
87
100
  this.logLevel = level;
88
101
  }
102
+ /**
103
+ * Routes debug and info output to stderr instead of stdout. The CLI turns
104
+ * this on for `--format json`, where stdout carries the payload and any
105
+ * diagnostic line in it would make the output unparseable.
106
+ *
107
+ * @param enabled - True to send debug/info to stderr; false restores stdout
108
+ */
109
+ setDiagnosticsToStderr(enabled) {
110
+ this.diagnosticsToStderr = enabled;
111
+ }
89
112
  /**
90
113
  * Determines whether a message with the given log level should be processed.
91
114
  * This method considers both the configured log level and the current debug mode.
@@ -209,9 +232,10 @@ class NeuroLinkLogger {
209
232
  * @param data - Optional additional data to log.
210
233
  */
211
234
  outputToConsole(level, prefix, message, data) {
235
+ const toStderr = this.diagnosticsToStderr || argvRequestsJsonOutput();
212
236
  const logMethod = {
213
- debug: console.debug,
214
- info: console.info,
237
+ debug: toStderr ? console.error : console.debug,
238
+ info: toStderr ? console.error : console.info,
215
239
  warn: console.warn,
216
240
  error: console.error,
217
241
  }[level];
@@ -467,6 +491,7 @@ export const logger = {
467
491
  shouldLog: (level) => neuroLinkLogger.shouldLog(level),
468
492
  // Expose structured logging methods
469
493
  setLogLevel: (level) => neuroLinkLogger.setLogLevel(level),
494
+ setDiagnosticsToStderr: (enabled) => neuroLinkLogger.setDiagnosticsToStderr(enabled),
470
495
  getLogs: (level) => neuroLinkLogger.getLogs(level),
471
496
  clearLogs: () => neuroLinkLogger.clearLogs(),
472
497
  setEventEmitter: (emitter) => neuroLinkLogger.setEventEmitter(emitter),
@@ -78,8 +78,28 @@ const PRICING = {
78
78
  // Anthropic (direct API) — updated March 2026
79
79
  anthropic: {
80
80
  // Claude 5 family. Rates from platform.claude.com/docs/en/about-claude/pricing
81
- // (checked 2026-08-21). Cache multipliers are the documented ones: a 5-minute
82
- // cache write is 1.25x base input, a cache hit 0.1x.
81
+ // (checked 2026-09-23). A 5-minute cache write is 1.25x base input. The cache
82
+ // hit multiplier is 0.1x for most models but 0.05x on Opus 5.5 and 0.025x on
83
+ // Fable/Mythos 5.1, so those three are listed in full rather than left to the
84
+ // longest-prefix fallback — which would silently quote the 5.0 family's rates.
85
+ "claude-fable-5-1": {
86
+ input: 10.0 / 1_000_000,
87
+ output: 50.0 / 1_000_000,
88
+ cacheRead: 0.25 / 1_000_000,
89
+ cacheCreation: 12.5 / 1_000_000,
90
+ },
91
+ "claude-mythos-5-1": {
92
+ input: 10.0 / 1_000_000,
93
+ output: 50.0 / 1_000_000,
94
+ cacheRead: 0.25 / 1_000_000,
95
+ cacheCreation: 12.5 / 1_000_000,
96
+ },
97
+ "claude-opus-5-5": {
98
+ input: 4.0 / 1_000_000,
99
+ output: 20.0 / 1_000_000,
100
+ cacheRead: 0.2 / 1_000_000,
101
+ cacheCreation: 5.0 / 1_000_000,
102
+ },
83
103
  "claude-fable-5": {
84
104
  input: 10.0 / 1_000_000,
85
105
  output: 50.0 / 1_000_000,
@@ -226,6 +246,53 @@ const PRICING = {
226
246
  },
227
247
  // Google Vertex AI — Claude models on Vertex (same pricing, @ date suffix)
228
248
  vertex: {
249
+ // Claude 5 family. Absent here until now, so a Vertex leg serving any of
250
+ // them resolved to no rate at all and booked the turn at $0 — on the one
251
+ // provider in the chain that bills in real currency.
252
+ // `isVertexAnthropicModel` admits every `claude-*` id, so the whole
253
+ // family needs an entry, not only the ones the chain names today.
254
+ "claude-fable-5-1": {
255
+ input: 10.0 / 1_000_000,
256
+ output: 50.0 / 1_000_000,
257
+ cacheRead: 0.25 / 1_000_000,
258
+ cacheCreation: 12.5 / 1_000_000,
259
+ },
260
+ "claude-mythos-5-1": {
261
+ input: 10.0 / 1_000_000,
262
+ output: 50.0 / 1_000_000,
263
+ cacheRead: 0.25 / 1_000_000,
264
+ cacheCreation: 12.5 / 1_000_000,
265
+ },
266
+ "claude-fable-5": {
267
+ input: 10.0 / 1_000_000,
268
+ output: 50.0 / 1_000_000,
269
+ cacheRead: 1.0 / 1_000_000,
270
+ cacheCreation: 12.5 / 1_000_000,
271
+ },
272
+ "claude-mythos-5": {
273
+ input: 10.0 / 1_000_000,
274
+ output: 50.0 / 1_000_000,
275
+ cacheRead: 1.0 / 1_000_000,
276
+ cacheCreation: 12.5 / 1_000_000,
277
+ },
278
+ "claude-opus-5-5": {
279
+ input: 4.0 / 1_000_000,
280
+ output: 20.0 / 1_000_000,
281
+ cacheRead: 0.2 / 1_000_000,
282
+ cacheCreation: 5.0 / 1_000_000,
283
+ },
284
+ "claude-opus-5": {
285
+ input: 5.0 / 1_000_000,
286
+ output: 25.0 / 1_000_000,
287
+ cacheRead: 0.5 / 1_000_000,
288
+ cacheCreation: 6.25 / 1_000_000,
289
+ },
290
+ "claude-sonnet-5": {
291
+ input: 2.0 / 1_000_000,
292
+ output: 10.0 / 1_000_000,
293
+ cacheRead: 0.2 / 1_000_000,
294
+ cacheCreation: 2.5 / 1_000_000,
295
+ },
229
296
  "claude-sonnet-4-6": {
230
297
  input: 3.0 / 1_000_000,
231
298
  output: 15.0 / 1_000_000,