dsh-subscriptions 0.2.0 → 0.2.2

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 (99) hide show
  1. package/README.md +212 -180
  2. package/lib/auth/claude-code-creds.js +31 -9
  3. package/lib/auth/device-flow.js +16 -9
  4. package/lib/auth/oauth-flow.js +13 -10
  5. package/lib/auth/rpc.js +74 -23
  6. package/lib/auth/store.js +80 -31
  7. package/lib/client/CursorCard.d.ts +1 -1
  8. package/lib/client/CursorCard.js +50 -16
  9. package/lib/client/ExternalUsageCards.d.ts +1 -1
  10. package/lib/client/ExternalUsageCards.js +37 -16
  11. package/lib/client/ImageGallery.d.ts +2 -2
  12. package/lib/client/ImageGallery.js +92 -33
  13. package/lib/client/ImageGenerateToolview.js +50 -14
  14. package/lib/client/OllamaCard.d.ts +1 -1
  15. package/lib/client/OllamaCard.js +26 -8
  16. package/lib/client/OllamaSettingsManager.js +113 -30
  17. package/lib/client/ProviderAccountManager.d.ts +1 -1
  18. package/lib/client/ProviderAccountManager.js +164 -49
  19. package/lib/client/ProviderModelEditor.js +107 -26
  20. package/lib/client/SpeedSelect.d.ts +62 -14
  21. package/lib/client/SpeedSelect.js +257 -46
  22. package/lib/client/SubscriptionUsageBadge.d.ts +4 -10
  23. package/lib/client/SubscriptionUsageBadge.js +300 -112
  24. package/lib/client/SubscriptionsSection.js +258 -85
  25. package/lib/client/UsageBadgeDisplaySetting.js +35 -12
  26. package/lib/client/UsageMeter.d.ts +1 -1
  27. package/lib/client/UsageMeter.js +62 -24
  28. package/lib/client/VideoGenerateToolview.js +69 -17
  29. package/lib/client/account-preferences.d.ts +8 -0
  30. package/lib/client/account-preferences.js +20 -5
  31. package/lib/client/host-contract.d.ts +11 -2
  32. package/lib/client/host-contract.js +20 -3
  33. package/lib/client/index.d.ts +5 -5
  34. package/lib/client/index.js +34 -12
  35. package/lib/client/locales.d.ts +10 -0
  36. package/lib/client/locales.js +61 -51
  37. package/lib/client/ollama-draft.js +21 -12
  38. package/lib/client/reset-action.js +6 -4
  39. package/lib/client/reset-credits-view.d.ts +1 -1
  40. package/lib/client/reset-credits-view.js +66 -17
  41. package/lib/client/subscription-card-styles.js +94 -24
  42. package/lib/client/usage-color-preferences.js +4 -2
  43. package/lib/client/usage-pace.js +30 -12
  44. package/lib/client.js +287 -47
  45. package/lib/client.js.map +1 -1
  46. package/lib/http.js +1 -1
  47. package/lib/index.d.ts +3 -3
  48. package/lib/index.js +118 -64
  49. package/lib/model-defaults.js +4 -2
  50. package/lib/provider-settings.d.ts +12 -0
  51. package/lib/provider-settings.js +58 -16
  52. package/lib/providers/account-preferences.js +51 -22
  53. package/lib/providers/accounts.js +15 -9
  54. package/lib/providers/antigravity-oauth-client.js +2 -2
  55. package/lib/providers/antigravity.js +141 -68
  56. package/lib/providers/catalog-store.js +120 -41
  57. package/lib/providers/claude-cli-version.js +3 -3
  58. package/lib/providers/claude.d.ts +9 -2
  59. package/lib/providers/claude.js +153 -90
  60. package/lib/providers/codex-client-version.js +3 -3
  61. package/lib/providers/codex-search.js +40 -13
  62. package/lib/providers/codex.js +196 -112
  63. package/lib/providers/common.js +71 -39
  64. package/lib/providers/copilot.js +143 -82
  65. package/lib/providers/cursor-adapter.js +42 -18
  66. package/lib/providers/cursor-auth.d.ts +1 -1
  67. package/lib/providers/cursor-auth.js +69 -24
  68. package/lib/providers/cursor-model-settings.js +14 -5
  69. package/lib/providers/cursor-usage.js +27 -13
  70. package/lib/providers/external-usage-controller.js +14 -5
  71. package/lib/providers/external-usage.js +59 -23
  72. package/lib/providers/grok.js +152 -89
  73. package/lib/providers/image-pool.js +29 -11
  74. package/lib/providers/minimax-usage.js +31 -11
  75. package/lib/providers/npm-cli-version.js +10 -3
  76. package/lib/providers/ollama-settings.js +24 -8
  77. package/lib/providers/ollama-web.js +29 -11
  78. package/lib/providers/ollama.js +129 -66
  79. package/lib/providers/pool-family.js +11 -3
  80. package/lib/providers/pool-health.js +28 -8
  81. package/lib/providers/pool-usage.js +16 -9
  82. package/lib/providers/pool.d.ts +8 -0
  83. package/lib/providers/pool.js +81 -42
  84. package/lib/providers/rate-limit.js +8 -2
  85. package/lib/providers/replay.js +40 -17
  86. package/lib/providers/reset-redemption.js +18 -6
  87. package/lib/tools/image-generate.js +123 -65
  88. package/lib/tools/registration.js +5 -2
  89. package/lib/tools/video-generate.js +51 -31
  90. package/lib/tools/x-search.js +50 -22
  91. package/lib/translate/anthropic.js +85 -37
  92. package/lib/translate/antigravity-schema.js +71 -28
  93. package/lib/translate/antigravity-thinking.js +22 -9
  94. package/lib/translate/antigravity.js +141 -50
  95. package/lib/translate/chat-completions.js +67 -23
  96. package/lib/translate/resolved.js +55 -21
  97. package/lib/translate/responses.js +69 -29
  98. package/lib/translate/sse.js +4 -1
  99. package/package.json +26 -4
@@ -4,11 +4,11 @@
4
4
  * the Anthropic Messages API with the Claude Code identity headers.
5
5
  */
6
6
  import { execFileSync } from 'node:child_process';
7
- import { EMPTY_RESPONSE_CODE, errorChain, LlmAdapter, LlmError, ReasoningEffortId } from '@deepseek-ai/dsh-llm';
8
- import { hostSupportsImageOffload, IMAGE_OFFLOAD_REQUIRED, requiredImageOffloadCount, resolveImages } from '../translate/resolved.js';
7
+ import { EMPTY_RESPONSE_CODE, errorChain, LlmAdapter, LlmError, ReasoningEffortId, } from '@deepseek-ai/dsh-llm';
8
+ import { hostSupportsImageOffload, IMAGE_OFFLOAD_REQUIRED, requiredImageOffloadCount, resolveImages, } from '../translate/resolved.js';
9
9
  import { markMessageCache, streamAnthropic, toAnthropicMessages, toAnthropicSystem, toAnthropicTools, } from '../translate/anthropic.js';
10
10
  import { httpLlmError, idleWatchdog, mapFetchFailure, mergeReasoning, ModelCatalogCache, discoverAcrossAccounts, discoverOrRetryAuth, isDiscoveryAborted, isMissingOrInvalidCredential, parseProviderJson, oauthEndpointError, OAuthEndpointError, } from './common.js';
11
- import { AccountTokenManager, DISCOVERY_TIMEOUT_MS, unionAccountCatalogs } from './accounts.js';
11
+ import { AccountTokenManager, DISCOVERY_TIMEOUT_MS, unionAccountCatalogs, } from './accounts.js';
12
12
  import { hostFetch } from '../http.js';
13
13
  import { compareVersions } from './npm-cli-version.js';
14
14
  import { DEFAULT_RATE_LIMIT_WAIT, DEFAULT_RETRY, earliestReset, jsonBody, resetFromFields, resetInstantFromHeader, subscriptionRetryPolicy, } from './rate-limit.js';
@@ -29,7 +29,12 @@ export const CLAUDE_PREEMPT_MS = 5 * 60_000;
29
29
  * Body fields Anthropic uses to name a reset instant, read when the unified
30
30
  * headers are absent.
31
31
  */
32
- const CLAUDE_RESET_FIELDS = ['resets_at', 'resetsAt', 'reset_at', 'retry_after'];
32
+ const CLAUDE_RESET_FIELDS = [
33
+ 'resets_at',
34
+ 'resetsAt',
35
+ 'reset_at',
36
+ 'retry_after',
37
+ ];
33
38
  /**
34
39
  * Reads the reset instant of the Anthropic window that rejected a request.
35
40
  *
@@ -153,15 +158,24 @@ async function fetchClaudeProfile(accessToken) {
153
158
  });
154
159
  if (!response.ok)
155
160
  return {};
156
- const profile = await response.json();
161
+ const profile = (await response.json());
157
162
  const account = typeof profile.account === 'object' && profile.account !== null
158
163
  ? profile.account
159
164
  : {};
160
- const email = profile.emailAddress ?? profile.email ?? account.email_address ?? account.email;
161
- const subscription = profile.subscriptionType ?? profile.subscription_type ?? account.subscription_type;
165
+ const email = profile.emailAddress ??
166
+ profile.email ??
167
+ account.email_address ??
168
+ account.email;
169
+ const subscription = profile.subscriptionType ??
170
+ profile.subscription_type ??
171
+ account.subscription_type;
162
172
  return {
163
- ...typeof email === 'string' && email.length > 0 ? { emailAddress: email } : {},
164
- ...typeof subscription === 'string' && subscription.length > 0 ? { subscriptionType: subscription } : {},
173
+ ...(typeof email === 'string' && email.length > 0
174
+ ? { emailAddress: email }
175
+ : {}),
176
+ ...(typeof subscription === 'string' && subscription.length > 0
177
+ ? { subscriptionType: subscription }
178
+ : {}),
165
179
  };
166
180
  }
167
181
  catch {
@@ -171,7 +185,8 @@ async function fetchClaudeProfile(accessToken) {
171
185
  }
172
186
  /** Build a session from a token response. */
173
187
  async function claudeSession(tokens, fallbackRefreshToken, withProfile) {
174
- if (typeof tokens.access_token !== 'string' || tokens.access_token.length === 0) {
188
+ if (typeof tokens.access_token !== 'string' ||
189
+ tokens.access_token.length === 0) {
175
190
  throw new Error('claude token endpoint returned no access token');
176
191
  }
177
192
  const refreshToken = tokens.refresh_token ?? fallbackRefreshToken;
@@ -180,7 +195,9 @@ async function claudeSession(tokens, fallbackRefreshToken, withProfile) {
180
195
  if (typeof tokens.expires_in !== 'number' || tokens.expires_in <= 0) {
181
196
  throw new Error('claude token endpoint returned no usable expiry');
182
197
  }
183
- const profile = withProfile ? await fetchClaudeProfile(tokens.access_token) : {};
198
+ const profile = withProfile
199
+ ? await fetchClaudeProfile(tokens.access_token)
200
+ : {};
184
201
  return {
185
202
  accessToken: tokens.access_token,
186
203
  refreshToken,
@@ -212,7 +229,7 @@ export async function exchangeClaudeCode(code, verifier, redirectUri, state) {
212
229
  });
213
230
  if (!response.ok)
214
231
  throw await oauthEndpointError(response, 'claude');
215
- return claudeSession(await parseProviderJson(response, 'claude token exchange'), undefined, true);
232
+ return claudeSession((await parseProviderJson(response, 'claude token exchange')), undefined, true);
216
233
  }
217
234
  /**
218
235
  * Refresh a claude session (JSON grant echoing the issued scope).
@@ -232,11 +249,15 @@ export async function refreshClaude(session) {
232
249
  });
233
250
  if (!response.ok)
234
251
  throw await oauthEndpointError(response, 'claude');
235
- const next = await claudeSession(await parseProviderJson(response, 'claude token refresh'), session.refreshToken, false);
252
+ const next = await claudeSession((await parseProviderJson(response, 'claude token refresh')), session.refreshToken, false);
236
253
  return {
237
254
  ...next,
238
- ...session.emailAddress === undefined ? {} : { emailAddress: session.emailAddress },
239
- ...session.subscriptionType === undefined ? {} : { subscriptionType: session.subscriptionType },
255
+ ...(session.emailAddress === undefined
256
+ ? {}
257
+ : { emailAddress: session.emailAddress }),
258
+ ...(session.subscriptionType === undefined
259
+ ? {}
260
+ : { subscriptionType: session.subscriptionType }),
240
261
  };
241
262
  }
242
263
  /**
@@ -245,8 +266,8 @@ export async function refreshClaude(session) {
245
266
  * @returns true when re-login is the only fix.
246
267
  */
247
268
  export function isClaudePermanentRefreshError(error) {
248
- return error instanceof OAuthEndpointError
249
- && (error.oauthCode === 'invalid_grant' || error.oauthCode === 'invalid_token');
269
+ return (error instanceof OAuthEndpointError &&
270
+ (error.oauthCode === 'invalid_grant' || error.oauthCode === 'invalid_token'));
250
271
  }
251
272
  export const CLAUDE_USAGE_URL = 'https://api.anthropic.com/api/oauth/usage';
252
273
  /** ISO-8601 or numeric epoch (seconds below 1e10 in magnitude, otherwise ms). */
@@ -261,7 +282,9 @@ function claudeResetsAt(value) {
261
282
  else {
262
283
  return undefined;
263
284
  }
264
- return Number.isFinite(parsed) && Number.isFinite(new Date(parsed).getTime()) ? parsed : undefined;
285
+ return Number.isFinite(parsed) && Number.isFinite(new Date(parsed).getTime())
286
+ ? parsed
287
+ : undefined;
265
288
  }
266
289
  /**
267
290
  * Read the observed cedar_ember shape from ItsJazii/pane, not an official
@@ -285,20 +308,28 @@ function claudeResetCredits(value, now) {
285
308
  if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
286
309
  continue;
287
310
  const grant = raw;
288
- const { id, resets_total: total, resets_left: left, usable_now: usable, paused } = grant;
311
+ const { id, resets_total: total, resets_left: left, usable_now: usable, paused, } = grant;
289
312
  const expiresAt = claudeResetsAt(grant.ends_at);
290
- if (typeof id !== 'string' || id.trim().length === 0
291
- || typeof total !== 'number' || !Number.isSafeInteger(total) || total < 1
292
- || typeof left !== 'number' || !Number.isSafeInteger(left)
293
- || expiresAt === undefined
294
- || (usable !== undefined && typeof usable !== 'boolean')
295
- || (paused !== undefined && typeof paused !== 'boolean'))
313
+ if (typeof id !== 'string' ||
314
+ id.trim().length === 0 ||
315
+ typeof total !== 'number' ||
316
+ !Number.isSafeInteger(total) ||
317
+ total < 1 ||
318
+ typeof left !== 'number' ||
319
+ !Number.isSafeInteger(left) ||
320
+ expiresAt === undefined ||
321
+ (usable !== undefined && typeof usable !== 'boolean') ||
322
+ (paused !== undefined && typeof paused !== 'boolean'))
296
323
  continue;
297
324
  const resetsLeft = Math.max(0, Math.min(left, total));
298
325
  if (resetsLeft === 0)
299
326
  continue;
300
- const claimable = !selected && id === ember.next_grant_id && usable === true
301
- && paused !== true && !cooling && expiresAt > now;
327
+ const claimable = !selected &&
328
+ id === ember.next_grant_id &&
329
+ usable === true &&
330
+ paused !== true &&
331
+ !cooling &&
332
+ expiresAt > now;
302
333
  if (claimable)
303
334
  selected = true;
304
335
  credits.push({
@@ -306,33 +337,37 @@ function claudeResetCredits(value, now) {
306
337
  expiresAt,
307
338
  resetsTotal: total,
308
339
  resetsLeft,
309
- ...usable === undefined ? {} : { usableNow: usable },
310
- ...paused === undefined ? {} : { paused },
340
+ ...(usable === undefined ? {} : { usableNow: usable }),
341
+ ...(paused === undefined ? {} : { paused }),
311
342
  claimable,
312
- ...cooldownUntil === undefined ? {} : { cooldownUntil },
343
+ ...(cooldownUntil === undefined ? {} : { cooldownUntil }),
313
344
  });
314
345
  }
315
346
  return credits.length > 0 ? credits : undefined;
316
347
  }
317
348
  /** Claude session and weekly buckets reset as whole five-hour/seven-day windows. */
318
349
  function claudeWindowTiming(kind) {
319
- return kind === 'session' ? { windowDurationMs: 5 * 60 * 60_000, fixedWindow: true }
320
- : kind === 'weekly' ? { windowDurationMs: 7 * 24 * 60 * 60_000, fixedWindow: true } : {};
350
+ return kind === 'session'
351
+ ? { windowDurationMs: 5 * 60 * 60_000, fixedWindow: true }
352
+ : kind === 'weekly'
353
+ ? { windowDurationMs: 7 * 24 * 60 * 60_000, fixedWindow: true }
354
+ : {};
321
355
  }
322
356
  /** Map one legacy `{utilization, resets_at}` bucket; undefined when null or unusable. */
323
357
  function claudeLegacyWindow(value, kind, scope) {
324
358
  if (typeof value !== 'object' || value === null)
325
359
  return undefined;
326
360
  const bucket = value;
327
- if (typeof bucket.utilization !== 'number' || !Number.isFinite(bucket.utilization))
361
+ if (typeof bucket.utilization !== 'number' ||
362
+ !Number.isFinite(bucket.utilization))
328
363
  return undefined;
329
364
  const resetsAt = claudeResetsAt(bucket.resets_at);
330
365
  return {
331
366
  kind,
332
- ...scope === undefined ? {} : { scope },
367
+ ...(scope === undefined ? {} : { scope }),
333
368
  ...claudeWindowTiming(kind),
334
369
  usedPercent: bucket.utilization,
335
- ...resetsAt === undefined ? {} : { resetsAt },
370
+ ...(resetsAt === undefined ? {} : { resetsAt }),
336
371
  };
337
372
  }
338
373
  /** Map the modern `limits` array; empty when absent or carrying nothing usable. */
@@ -348,15 +383,17 @@ function claudeLimitsWindows(value) {
348
383
  continue;
349
384
  const kind = entry.kind === 'session'
350
385
  ? 'session'
351
- : entry.kind === 'weekly_all' || entry.kind === 'weekly_scoped' ? 'weekly' : 'other';
386
+ : entry.kind === 'weekly_all' || entry.kind === 'weekly_scoped'
387
+ ? 'weekly'
388
+ : 'other';
352
389
  const scope = entry.scope?.model?.display_name;
353
390
  const resetsAt = claudeResetsAt(entry.resets_at);
354
391
  windows.push({
355
392
  kind,
356
- ...typeof scope === 'string' && scope.length > 0 ? { scope } : {},
393
+ ...(typeof scope === 'string' && scope.length > 0 ? { scope } : {}),
357
394
  ...claudeWindowTiming(kind),
358
395
  usedPercent: entry.percent,
359
- ...resetsAt === undefined ? {} : { resetsAt },
396
+ ...(resetsAt === undefined ? {} : { resetsAt }),
360
397
  });
361
398
  }
362
399
  return windows;
@@ -380,14 +417,14 @@ export async function fetchClaudeUsage(session, fetchFn = hostFetch, signal, cli
380
417
  const userAgent = claudeCliUserAgent(await cliVersion());
381
418
  const init = {
382
419
  headers: {
383
- 'authorization': `Bearer ${session.accessToken}`,
420
+ authorization: `Bearer ${session.accessToken}`,
384
421
  'anthropic-beta': 'oauth-2025-04-20',
385
422
  // Unrecognized clients are aggressively rate-limited on this endpoint,
386
423
  // so it presents as the CLI like every other subscription request.
387
424
  'user-agent': userAgent,
388
- 'accept': 'application/json',
425
+ accept: 'application/json',
389
426
  },
390
- ...signal === undefined ? {} : { signal },
427
+ ...(signal === undefined ? {} : { signal }),
391
428
  };
392
429
  let response = await fetchFn(`${CLAUDE_USAGE_URL}?cedar_ember=1`, init);
393
430
  let resetCreditsError;
@@ -398,11 +435,13 @@ export async function fetchClaudeUsage(session, fetchFn = hostFetch, signal, cli
398
435
  }
399
436
  if (!response.ok)
400
437
  throw await oauthEndpointError(response, 'claude usage');
401
- const payload = await parseProviderJson(response, 'claude usage');
402
- const resetCredits = resetCreditsError === undefined ? claudeResetCredits(payload.cedar_ember, Date.now()) : undefined;
438
+ const payload = (await parseProviderJson(response, 'claude usage'));
439
+ const resetCredits = resetCreditsError === undefined
440
+ ? claudeResetCredits(payload.cedar_ember, Date.now())
441
+ : undefined;
403
442
  const resets = {
404
- ...resetCredits === undefined ? {} : { resetCredits },
405
- ...resetCreditsError === undefined ? {} : { resetCreditsError },
443
+ ...(resetCredits === undefined ? {} : { resetCredits }),
444
+ ...(resetCreditsError === undefined ? {} : { resetCreditsError }),
406
445
  };
407
446
  const modern = claudeLimitsWindows(payload.limits);
408
447
  if (modern.length > 0)
@@ -434,14 +473,17 @@ function claudeReasoning(capabilities) {
434
473
  const effort = capabilities?.effort;
435
474
  if (effort?.supported !== true)
436
475
  return undefined;
437
- const efforts = CLAUDE_EFFORT_LEVELS
438
- .filter(level => effort[level]?.supported === true)
439
- .map(level => ({ id: ReasoningEffortId(level), name: level[0].toUpperCase() + level.slice(1) }));
476
+ const efforts = CLAUDE_EFFORT_LEVELS.filter((level) => effort[level]?.supported === true).map((level) => ({
477
+ id: ReasoningEffortId(level),
478
+ name: level[0].toUpperCase() + level.slice(1),
479
+ }));
440
480
  return efforts.length > 0 ? { efforts } : undefined;
441
481
  }
442
482
  /** A token limit the endpoint disclosed, or undefined when absent or malformed. */
443
483
  function positiveTokenCount(value) {
444
- return typeof value === 'number' && Number.isSafeInteger(value) && value > 0 ? value : undefined;
484
+ return typeof value === 'number' && Number.isSafeInteger(value) && value > 0
485
+ ? value
486
+ : undefined;
445
487
  }
446
488
  /**
447
489
  * Fetch the live model catalog from the subscription endpoint. `signal`
@@ -452,17 +494,17 @@ export async function fetchClaudeModels(session, fetchFn = hostFetch, signal, cl
452
494
  const userAgent = claudeCliUserAgent(await cliVersion());
453
495
  const response = await fetchFn(CLAUDE_MODELS_URL, {
454
496
  headers: {
455
- 'authorization': `Bearer ${session.accessToken}`,
497
+ authorization: `Bearer ${session.accessToken}`,
456
498
  'anthropic-version': '2023-06-01',
457
499
  'user-agent': userAgent,
458
500
  'anthropic-dangerous-direct-browser-access': 'true',
459
- 'accept': 'application/json',
501
+ accept: 'application/json',
460
502
  },
461
- ...signal === undefined ? {} : { signal },
503
+ ...(signal === undefined ? {} : { signal }),
462
504
  });
463
505
  if (!response.ok)
464
506
  throw await httpLlmError(response, 'claude models API');
465
- const payload = await parseProviderJson(response, 'claude models API');
507
+ const payload = (await parseProviderJson(response, 'claude models API'));
466
508
  if (!Array.isArray(payload.data)) {
467
509
  throw new Error('claude models API returned an invalid catalog');
468
510
  }
@@ -478,10 +520,10 @@ export async function fetchClaudeModels(session, fetchFn = hostFetch, signal, cl
478
520
  return {
479
521
  id: m.id,
480
522
  name: m.display_name ?? m.id,
481
- ...contextWindow === undefined ? {} : { contextWindow },
482
- ...maxOutputTokens === undefined ? {} : { maxOutputTokens },
483
- ...thinkingType === undefined ? {} : { thinkingType },
484
- ...reasoning === undefined ? {} : { reasoning },
523
+ ...(contextWindow === undefined ? {} : { contextWindow }),
524
+ ...(maxOutputTokens === undefined ? {} : { maxOutputTokens }),
525
+ ...(thinkingType === undefined ? {} : { thinkingType }),
526
+ ...(reasoning === undefined ? {} : { reasoning }),
485
527
  };
486
528
  });
487
529
  if (models.length === 0) {
@@ -496,7 +538,9 @@ export async function fetchClaudeModels(session, fetchFn = hostFetch, signal, cl
496
538
  function claudeMaxTokens(configured, disc) {
497
539
  const outputLimit = disc?.maxOutputTokens;
498
540
  const preferred = configured?.maxTokens ?? outputLimit ?? CLAUDE_DEFAULT_MAX_TOKENS;
499
- return outputLimit === undefined ? preferred : Math.min(preferred, outputLimit);
541
+ return outputLimit === undefined
542
+ ? preferred
543
+ : Math.min(preferred, outputLimit);
500
544
  }
501
545
  /**
502
546
  * Anthropic rejects any image edge over 2000px once a request carries many
@@ -530,8 +574,9 @@ const CLAUDE_MODALITIES = ['text', 'image'];
530
574
  * @param effort - the reasoning effort, when the model advertises efforts.
531
575
  * @param cacheTtl - lifetime of every cache breakpoint; defaults to five minutes.
532
576
  * Requests that carry a `purpose` (compaction, session titles) always use five
533
- * minutes to preserve their existing auxiliary-request behavior. A five-minute
534
- * mark can still read an entry a one-hour mark wrote.
577
+ * minutes to preserve their existing auxiliary-request behavior — the override
578
+ * changes the TTL for ordinary turns only, never the auxiliary floor. A
579
+ * five-minute mark can still read an entry a one-hour mark wrote.
535
580
  * @returns the JSON body to POST.
536
581
  */
537
582
  export function claudeRequestBody(options, messages, maxTokens, thinking, effort, cacheTtl) {
@@ -546,13 +591,15 @@ export function claudeRequestBody(options, messages, maxTokens, thinking, effort
546
591
  max_tokens: maxTokens,
547
592
  system: toAnthropicSystem(options.system, messages, ttl),
548
593
  messages: anthropicMessages,
549
- ...options.tools !== undefined && options.tools.length > 0
594
+ ...(options.tools !== undefined && options.tools.length > 0
550
595
  ? { tools: toAnthropicTools(options.tools) }
551
- : {},
552
- ...thinking === undefined ? {} : { thinking },
553
- ...effort === undefined ? {} : { output_config: { effort } },
596
+ : {}),
597
+ ...(thinking === undefined ? {} : { thinking }),
598
+ ...(effort === undefined ? {} : { output_config: { effort } }),
554
599
  stream: true,
555
- ...options.sessionId !== undefined ? { metadata: { user_id: String(options.sessionId) } } : {},
600
+ ...(options.sessionId !== undefined
601
+ ? { metadata: { user_id: String(options.sessionId) } }
602
+ : {}),
556
603
  };
557
604
  }
558
605
  /** Claude wire adapter: one instance serves the `claude` provider route. */
@@ -577,7 +624,9 @@ export class ClaudeAdapter extends LlmAdapter {
577
624
  this.accountCatalogs.clear();
578
625
  else
579
626
  this.accountCatalogs.delete(account);
580
- if (account === undefined || this.catalogOwner === account || this.catalogOwner === undefined) {
627
+ if (account === undefined ||
628
+ this.catalogOwner === account ||
629
+ this.catalogOwner === undefined) {
581
630
  this.catalogOwner = undefined;
582
631
  this.catalog.invalidate();
583
632
  }
@@ -606,16 +655,16 @@ export class ClaudeAdapter extends LlmAdapter {
606
655
  // A named account is looked up alone: its window must not be borrowed
607
656
  // from another account's catalog.
608
657
  const accounts = account === undefined
609
- ? (await this.options.tokens.list()).map(entry => entry.key)
658
+ ? (await this.options.tokens.list()).map((entry) => entry.key)
610
659
  : [account];
611
660
  return discoverAcrossAccounts(accounts, async (account) => {
612
661
  const catalog = await this.catalogFor(account);
613
662
  const models = await catalog.resolve(() => this.fetchCatalog(account));
614
- return models?.find(entry => entry.id === model);
663
+ return models?.find((entry) => entry.id === model);
615
664
  });
616
665
  }
617
666
  staticModels(provider) {
618
- return this.options.models.map(model => ({
667
+ return this.options.models.map((model) => ({
619
668
  provider,
620
669
  id: model.id,
621
670
  name: model.name ?? model.id,
@@ -634,27 +683,30 @@ export class ClaudeAdapter extends LlmAdapter {
634
683
  if (pool === undefined)
635
684
  return own;
636
685
  const extra = await pool.modelsForProvider(provider);
637
- const seen = new Set(own.map(model => model.id));
686
+ const seen = new Set(own.map((model) => model.id));
638
687
  // Account pools reuse the catalog row; only configured tiers are extra.
639
- return [...own, ...extra.filter(model => !seen.has(model.id))];
688
+ return [...own, ...extra.filter((model) => !seen.has(model.id))];
640
689
  }
641
690
  /** The provider's own catalog: union of every account, or one account when named. */
642
691
  async listOwnModels(provider, account, signal) {
643
692
  if (account === undefined) {
644
- const accounts = (await this.options.tokens.list()).map(entry => entry.key);
693
+ const accounts = (await this.options.tokens.list()).map((entry) => entry.key);
645
694
  if (accounts.length === 0)
646
695
  return [];
647
- return unionAccountCatalogs(accounts, (key, accountSignal) => this.listOwnModels(provider, key, accountSignal), { timeoutMs: DISCOVERY_TIMEOUT_MS, ...signal === undefined ? {} : { signal } });
696
+ return unionAccountCatalogs(accounts, (key, accountSignal) => this.listOwnModels(provider, key, accountSignal), {
697
+ timeoutMs: DISCOVERY_TIMEOUT_MS,
698
+ ...(signal === undefined ? {} : { signal }),
699
+ });
648
700
  }
649
- if (!await this.options.tokens.hasSession(account)) {
701
+ if (!(await this.options.tokens.hasSession(account))) {
650
702
  return [];
651
703
  }
652
704
  if (!this.options.discovery)
653
705
  return this.staticModels(provider);
654
706
  const catalog = await this.catalogFor(account);
655
707
  try {
656
- const models = await discoverOrRetryAuth(force => this.options.tokens.session(account, force), catalog, () => catalog.get(() => this.fetchCatalog(account, signal)));
657
- return models.map(model => ({
708
+ const models = await discoverOrRetryAuth((force) => this.options.tokens.session(account, force), catalog, () => catalog.get(() => this.fetchCatalog(account, signal)));
709
+ return models.map((model) => ({
658
710
  provider,
659
711
  id: model.id,
660
712
  name: model.name,
@@ -672,7 +724,8 @@ export class ClaudeAdapter extends LlmAdapter {
672
724
  }
673
725
  async resolveModel(provider, model) {
674
726
  const pool = this.options.pool?.();
675
- if (pool !== undefined && await pool.owns(provider, model)) {
727
+ if (pool !== undefined &&
728
+ (await pool.owns(provider, model))) {
676
729
  return pool.resolveModel(provider, model);
677
730
  }
678
731
  return this.resolveOwnModel(provider, model);
@@ -680,7 +733,7 @@ export class ClaudeAdapter extends LlmAdapter {
680
733
  /** Capability resolution of the provider's own models (the pool resolves members here). */
681
734
  async resolveOwnModel(provider, model, account) {
682
735
  const disc = await this.discovered(model, account);
683
- const configured = this.options.models.find(entry => entry.id === model);
736
+ const configured = this.options.models.find((entry) => entry.id === model);
684
737
  const limits = this.contextLimitsFor(disc, configured);
685
738
  const reasoning = mergeReasoning(this.options.defaultEffortOf?.(model), disc?.reasoning);
686
739
  return {
@@ -688,7 +741,9 @@ export class ClaudeAdapter extends LlmAdapter {
688
741
  id: model,
689
742
  name: disc?.name ?? configured?.name ?? model,
690
743
  inputModalities: configured?.inputModalities ?? CLAUDE_MODALITIES,
691
- context: { contextWindow: Math.min(this.options.contextWindowOf?.(model) ?? limits.default, limits.max) },
744
+ context: {
745
+ contextWindow: Math.min(this.options.contextWindowOf?.(model) ?? limits.default, limits.max),
746
+ },
692
747
  defaultMaxTokens: claudeMaxTokens(configured, disc),
693
748
  ...(reasoning === undefined ? {} : { reasoning }),
694
749
  };
@@ -700,11 +755,12 @@ export class ClaudeAdapter extends LlmAdapter {
700
755
  }
701
756
  /** Account-specific bounds the Settings editor shows next to a model's override input. */
702
757
  async contextLimits(model, account) {
703
- return this.contextLimitsFor(await this.discovered(model, account), this.options.models.find(entry => entry.id === model));
758
+ return this.contextLimitsFor(await this.discovered(model, account), this.options.models.find((entry) => entry.id === model));
704
759
  }
705
760
  async *stream(options) {
706
761
  const pool = this.options.pool?.();
707
- if (pool !== undefined && await pool.owns(options.provider, options.model)) {
762
+ if (pool !== undefined &&
763
+ (await pool.owns(options.provider, options.model))) {
708
764
  yield* pool.stream(options);
709
765
  return;
710
766
  }
@@ -726,13 +782,17 @@ export class ClaudeAdapter extends LlmAdapter {
726
782
  if (!response.ok) {
727
783
  throw await httpLlmError(response, 'claude API', {
728
784
  rateLimitReset: claudeRateLimitReset,
729
- ...this.options.onWarn === undefined ? {} : { onWarn: this.options.onWarn },
785
+ ...(this.options.onWarn === undefined
786
+ ? {}
787
+ : { onWarn: this.options.onWarn }),
730
788
  });
731
789
  }
732
790
  if (response.body === null) {
733
791
  throw new LlmError('claude API returned no response body', EMPTY_RESPONSE_CODE);
734
792
  }
735
- yield* streamAnthropic(response.body, () => { watchdog.pulse(); });
793
+ yield* streamAnthropic(response.body, () => {
794
+ watchdog.pulse();
795
+ });
736
796
  }
737
797
  catch (error) {
738
798
  throw mapFetchFailure('claude API', error, watchdog, options.signal);
@@ -765,29 +825,32 @@ export class ClaudeAdapter extends LlmAdapter {
765
825
  const offloadImages = requiredImageOffloadCount(messages, CLAUDE_REQUEST_IMAGE_BUDGET);
766
826
  if (offloadImages > 0) {
767
827
  // The supported 0.1.7-rc.2 floor reads `offloadImages`; older option types lack it.
768
- throw new LlmError(`claude request images exceed the ${CLAUDE_REQUEST_IMAGE_BUDGET}-byte base64 budget; `
769
- + `${offloadImages} more oldest image(s) must be offloaded`, IMAGE_OFFLOAD_REQUIRED, { offloadImages });
828
+ throw new LlmError(`claude request images exceed the ${CLAUDE_REQUEST_IMAGE_BUDGET}-byte base64 budget; ` +
829
+ `${offloadImages} more oldest image(s) must be offloaded`, IMAGE_OFFLOAD_REQUIRED, { offloadImages });
770
830
  }
771
831
  }
772
832
  const disc = await this.discovered(options.model);
773
- const maxTokens = options.maxTokens
774
- ?? claudeMaxTokens(this.options.models.find(entry => entry.id === options.model), disc);
833
+ const maxTokens = options.maxTokens ??
834
+ claudeMaxTokens(this.options.models.find((entry) => entry.id === options.model), disc);
775
835
  const thinking = this.thinkingParam(disc?.thinkingType, maxTokens);
776
836
  const effort = options.reasoningEffort !== undefined && disc?.reasoning !== undefined
777
837
  ? String(options.reasoningEffort)
778
838
  : undefined;
779
- const body = claudeRequestBody(options, messages, maxTokens, thinking, effort, this.options.promptCacheTtl);
839
+ // Read the Settings override per request: a saved change must apply from
840
+ // the next turn, and a cached value here would go stale.
841
+ const cacheTtl = this.options.promptCacheTtlOverride?.() ?? this.options.promptCacheTtl;
842
+ const body = claudeRequestBody(options, messages, maxTokens, thinking, effort, cacheTtl);
780
843
  const cliVersion = await (this.options.resolveCliVersion ?? localClaudeCliVersion)();
781
844
  return hostFetch(CLAUDE_API_URL, {
782
845
  method: 'POST',
783
846
  headers: {
784
- 'authorization': `Bearer ${session.accessToken}`,
847
+ authorization: `Bearer ${session.accessToken}`,
785
848
  'anthropic-version': '2023-06-01',
786
849
  'anthropic-beta': CLAUDE_BETA_FLAGS,
787
850
  'user-agent': claudeCliUserAgent(cliVersion),
788
851
  'x-app': 'cli',
789
852
  'anthropic-dangerous-direct-browser-access': 'true',
790
- 'accept': 'text/event-stream',
853
+ accept: 'text/event-stream',
791
854
  'content-type': 'application/json',
792
855
  },
793
856
  body: JSON.stringify(body),
@@ -9,9 +9,9 @@ export class CodexClientVersionCache extends NpmCliVersionCache {
9
9
  url: CODEX_VERSION_URL,
10
10
  label: 'Codex',
11
11
  floor: () => ({ version: CODEX_CLIENT_VERSION, source: 'fallback' }),
12
- ...fetchFn === undefined ? {} : { fetchFn },
13
- ...now === undefined ? {} : { now },
14
- ...timeoutMs === undefined ? {} : { timeoutMs },
12
+ ...(fetchFn === undefined ? {} : { fetchFn }),
13
+ ...(now === undefined ? {} : { now }),
14
+ ...(timeoutMs === undefined ? {} : { timeoutMs }),
15
15
  });
16
16
  }
17
17
  }