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
package/lib/http.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Host-managed network routing for subscription requests. */
2
- import { getDefaultAutoSelectFamilyAttemptTimeout, setDefaultAutoSelectFamilyAttemptTimeout } from 'node:net';
2
+ import { getDefaultAutoSelectFamilyAttemptTimeout, setDefaultAutoSelectFamilyAttemptTimeout, } from 'node:net';
3
3
  /** Keep a sufficient per-address connection budget on high-latency networks. */
4
4
  export const MIN_CONNECT_ATTEMPT_TIMEOUT_MS = 1500;
5
5
  export function ensureConnectAttemptTimeout(minMs = MIN_CONNECT_ATTEMPT_TIMEOUT_MS) {
package/lib/index.d.ts CHANGED
@@ -13,13 +13,13 @@ import type { ModelEntry } from './providers/common.js';
13
13
  import type { RateLimitConfig } from './providers/rate-limit.js';
14
14
  import type { PromptCacheTtl } from './translate/anthropic.js';
15
15
  import type { CliVersion, NpmCliVersionCache } from './providers/npm-cli-version.js';
16
- export type { AccountPreferences, ProviderPreferences } from './provider-settings.js';
16
+ export type { AccountPreferences, ProviderPreferences, } from './provider-settings.js';
17
17
  import type { PoolMemberRef } from './providers/pool-family.js';
18
18
  import type { AntigravityRuntimeConfig } from './providers/antigravity.js';
19
- export type { ModelEntry, ProviderUsage, UsageWindow } from './providers/common.js';
19
+ export type { ModelEntry, ProviderUsage, UsageWindow, } from './providers/common.js';
20
20
  export type { RateLimitConfig, RateLimitWait } from './providers/rate-limit.js';
21
21
  export type { ProviderStatus } from './auth/rpc.js';
22
- export type { AntigravitySession, ClaudeSession, CodexSession, CopilotSession, GrokSession, ProviderId } from './auth/store.js';
22
+ export type { AntigravitySession, ClaudeSession, CodexSession, CopilotSession, GrokSession, ProviderId, } from './auth/store.js';
23
23
  export declare const name = "dsh-subscriptions";
24
24
  export declare const inject: string[];
25
25
  /** Default maximum provider idle time while one stream read is outstanding. */
package/lib/index.js CHANGED
@@ -1205,7 +1205,7 @@ var DeviceFlowManager = class {
1205
1205
  const response = await fetchFn(spec.deviceCodeUrl, {
1206
1206
  method: "POST",
1207
1207
  headers: {
1208
- "accept": "application/json",
1208
+ accept: "application/json",
1209
1209
  "content-type": "application/x-www-form-urlencoded"
1210
1210
  },
1211
1211
  body: new URLSearchParams({
@@ -1244,7 +1244,7 @@ var DeviceFlowManager = class {
1244
1244
  const pollResponse = await fetchFn(spec.tokenUrl, {
1245
1245
  method: "POST",
1246
1246
  headers: {
1247
- "accept": "application/json",
1247
+ accept: "application/json",
1248
1248
  "content-type": "application/x-www-form-urlencoded"
1249
1249
  },
1250
1250
  body: new URLSearchParams({
@@ -3064,8 +3064,8 @@ function withoutImages(messages) {
3064
3064
  }
3065
3065
  function discoveryHeaders(apiKey) {
3066
3066
  return {
3067
- "accept": "application/json",
3068
- ...apiKey === void 0 ? {} : { "authorization": `Bearer ${apiKey}` },
3067
+ accept: "application/json",
3068
+ ...apiKey === void 0 ? {} : { authorization: `Bearer ${apiKey}` },
3069
3069
  ...attributionHeaders()
3070
3070
  };
3071
3071
  }
@@ -3340,8 +3340,8 @@ var OllamaAdapter = class extends LlmAdapter {
3340
3340
  const response = await (this.options.fetchFn ?? hostFetch)(`${chatBase}/chat/completions`, {
3341
3341
  method: "POST",
3342
3342
  headers: {
3343
- "authorization": `Bearer ${apiKey}`,
3344
- "accept": "text/event-stream",
3343
+ authorization: `Bearer ${apiKey}`,
3344
+ accept: "text/event-stream",
3345
3345
  "content-type": "application/json",
3346
3346
  ...attributionHeaders()
3347
3347
  },
@@ -3377,7 +3377,7 @@ function valueOf(value) {
3377
3377
  const DEFAULT_REFS = {
3378
3378
  "opencode-go": "OPENCODE_GO_API_KEY",
3379
3379
  "kimi-code": "KIMI_CODING_API_KEY",
3380
- "minimax": "MINIMAX_API_KEY",
3380
+ minimax: "MINIMAX_API_KEY",
3381
3381
  "minimax-cn": "MINIMAX_CN_API_KEY",
3382
3382
  "ollama-cloud": DEFAULT_OLLAMA_API_KEY_REF
3383
3383
  };
@@ -6950,8 +6950,8 @@ async function postJson(url, apiKey, payload, options, signal) {
6950
6950
  method: "POST",
6951
6951
  headers: {
6952
6952
  "content-type": "application/json",
6953
- "accept": "application/json",
6954
- "authorization": `Bearer ${apiKey}`,
6953
+ accept: "application/json",
6954
+ authorization: `Bearer ${apiKey}`,
6955
6955
  ...attributionHeaders()
6956
6956
  },
6957
6957
  body: JSON.stringify(payload),
@@ -8008,13 +8008,39 @@ async function readCatalogFile(path) {
8008
8008
  return {};
8009
8009
  }
8010
8010
  }
8011
+ /**
8012
+ * Windows refuses to replace a file another handle holds open without
8013
+ * `FILE_SHARE_DELETE` (an indexer, antivirus scan, or a second dsh profile
8014
+ * reading the cache), surfacing as a transient `EPERM`/`EBUSY`/`EACCES` on
8015
+ * `rename` (V1ki/dsh-plugin-subscriptions#129). On Windows only, retry
8016
+ * briefly before giving the write up: on POSIX those codes are real
8017
+ * failures, so retrying would only delay surfacing them.
8018
+ */
8019
+ const RENAME_RETRY_CODES = new Set([
8020
+ "EPERM",
8021
+ "EBUSY",
8022
+ "EACCES"
8023
+ ]);
8024
+ const RENAME_RETRIES = 5;
8025
+ const RENAME_RETRY_DELAY_MS = 20;
8026
+ /** `rename` with a short bounded retry on Windows share-mode conflicts. */
8027
+ async function renameWithRetry(from, to) {
8028
+ for (let attempt = 0;; attempt++) try {
8029
+ await rename(from, to);
8030
+ return;
8031
+ } catch (error) {
8032
+ const code = error.code;
8033
+ if (process.platform !== "win32" || attempt >= RENAME_RETRIES || code === void 0 || !RENAME_RETRY_CODES.has(code)) throw error;
8034
+ await new Promise((resolve$2) => setTimeout(resolve$2, RENAME_RETRY_DELAY_MS * (attempt + 1)));
8035
+ }
8036
+ }
8011
8037
  /** Persist the whole file atomically (tmp file + rename). */
8012
8038
  async function writeCatalogFile(store, path) {
8013
8039
  await mkdir(dirname(path), { recursive: true });
8014
8040
  const tmp = `${path}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
8015
8041
  try {
8016
8042
  await writeFile(tmp, JSON.stringify(store, null, 2));
8017
- await rename(tmp, path);
8043
+ await renameWithRetry(tmp, path);
8018
8044
  } catch (error) {
8019
8045
  await rm(tmp, { force: true });
8020
8046
  throw error;
@@ -8550,10 +8576,10 @@ async function fetchClaudeUsage(session, fetchFn = hostFetch, signal, cliVersion
8550
8576
  const userAgent = claudeCliUserAgent(await cliVersion());
8551
8577
  const init = {
8552
8578
  headers: {
8553
- "authorization": `Bearer ${session.accessToken}`,
8579
+ authorization: `Bearer ${session.accessToken}`,
8554
8580
  "anthropic-beta": "oauth-2025-04-20",
8555
8581
  "user-agent": userAgent,
8556
- "accept": "application/json"
8582
+ accept: "application/json"
8557
8583
  },
8558
8584
  ...signal === void 0 ? {} : { signal }
8559
8585
  };
@@ -8625,11 +8651,11 @@ async function fetchClaudeModels(session, fetchFn = hostFetch, signal, cliVersio
8625
8651
  const userAgent = claudeCliUserAgent(await cliVersion());
8626
8652
  const response = await fetchFn(CLAUDE_MODELS_URL, {
8627
8653
  headers: {
8628
- "authorization": `Bearer ${session.accessToken}`,
8654
+ authorization: `Bearer ${session.accessToken}`,
8629
8655
  "anthropic-version": "2023-06-01",
8630
8656
  "user-agent": userAgent,
8631
8657
  "anthropic-dangerous-direct-browser-access": "true",
8632
- "accept": "application/json"
8658
+ accept: "application/json"
8633
8659
  },
8634
8660
  ...signal === void 0 ? {} : { signal }
8635
8661
  });
@@ -8697,8 +8723,9 @@ const CLAUDE_MODALITIES = ["text", "image"];
8697
8723
  * @param effort - the reasoning effort, when the model advertises efforts.
8698
8724
  * @param cacheTtl - lifetime of every cache breakpoint; defaults to five minutes.
8699
8725
  * Requests that carry a `purpose` (compaction, session titles) always use five
8700
- * minutes to preserve their existing auxiliary-request behavior. A five-minute
8701
- * mark can still read an entry a one-hour mark wrote.
8726
+ * minutes to preserve their existing auxiliary-request behavior — the override
8727
+ * changes the TTL for ordinary turns only, never the auxiliary floor. A
8728
+ * five-minute mark can still read an entry a one-hour mark wrote.
8702
8729
  * @returns the JSON body to POST.
8703
8730
  */
8704
8731
  function claudeRequestBody(options, messages, maxTokens, thinking, effort, cacheTtl) {
@@ -8913,18 +8940,18 @@ var ClaudeAdapter = class extends LlmAdapter {
8913
8940
  }
8914
8941
  const disc = await this.discovered(options.model);
8915
8942
  const maxTokens = options.maxTokens ?? claudeMaxTokens(this.options.models.find((entry) => entry.id === options.model), disc);
8916
- const body = claudeRequestBody(options, messages, maxTokens, this.thinkingParam(disc?.thinkingType, maxTokens), options.reasoningEffort !== void 0 && disc?.reasoning !== void 0 ? String(options.reasoningEffort) : void 0, this.options.promptCacheTtl);
8943
+ const body = claudeRequestBody(options, messages, maxTokens, this.thinkingParam(disc?.thinkingType, maxTokens), options.reasoningEffort !== void 0 && disc?.reasoning !== void 0 ? String(options.reasoningEffort) : void 0, this.options.promptCacheTtlOverride?.() ?? this.options.promptCacheTtl);
8917
8944
  const cliVersion = await (this.options.resolveCliVersion ?? localClaudeCliVersion)();
8918
8945
  return hostFetch(CLAUDE_API_URL, {
8919
8946
  method: "POST",
8920
8947
  headers: {
8921
- "authorization": `Bearer ${session.accessToken}`,
8948
+ authorization: `Bearer ${session.accessToken}`,
8922
8949
  "anthropic-version": "2023-06-01",
8923
8950
  "anthropic-beta": CLAUDE_BETA_FLAGS,
8924
8951
  "user-agent": claudeCliUserAgent(cliVersion),
8925
8952
  "x-app": "cli",
8926
8953
  "anthropic-dangerous-direct-browser-access": "true",
8927
- "accept": "text/event-stream",
8954
+ accept: "text/event-stream",
8928
8955
  "content-type": "application/json"
8929
8956
  },
8930
8957
  body: JSON.stringify(body),
@@ -9653,10 +9680,10 @@ function codexUsageWindow(value, fallbackKind) {
9653
9680
  async function fetchCodexUsage(session, fetchFn = hostFetch, signal) {
9654
9681
  const response = await fetchFn(CODEX_USAGE_URL, {
9655
9682
  headers: {
9656
- "authorization": `Bearer ${session.accessToken}`,
9683
+ authorization: `Bearer ${session.accessToken}`,
9657
9684
  "chatgpt-account-id": session.accountId,
9658
- "originator": "codex_cli_rs",
9659
- "accept": "application/json",
9685
+ originator: "codex_cli_rs",
9686
+ accept: "application/json",
9660
9687
  ...attributionHeaders()
9661
9688
  },
9662
9689
  ...signal === void 0 ? {} : { signal }
@@ -9707,10 +9734,10 @@ async function fetchCodexModels(session, fetchFn = hostFetch, signal, clientVers
9707
9734
  const version = codexClientVersion(clientVersion);
9708
9735
  const response = await fetchFn(`${CODEX_MODELS_URL}?client_version=${encodeURIComponent(version)}`, {
9709
9736
  headers: {
9710
- "authorization": `Bearer ${session.accessToken}`,
9737
+ authorization: `Bearer ${session.accessToken}`,
9711
9738
  "chatgpt-account-id": session.accountId,
9712
- "originator": "codex_cli_rs",
9713
- "accept": "application/json",
9739
+ originator: "codex_cli_rs",
9740
+ accept: "application/json",
9714
9741
  ...attributionHeaders()
9715
9742
  },
9716
9743
  ...signal === void 0 ? {} : { signal }
@@ -10101,11 +10128,11 @@ var CodexAdapter = class extends LlmAdapter {
10101
10128
  return hostFetch(CODEX_API_URL, {
10102
10129
  method: "POST",
10103
10130
  headers: {
10104
- "authorization": `Bearer ${session.accessToken}`,
10131
+ authorization: `Bearer ${session.accessToken}`,
10105
10132
  "chatgpt-account-id": session.accountId,
10106
- "originator": "codex_cli_rs",
10133
+ originator: "codex_cli_rs",
10107
10134
  "session-id": deterministicSessionId(options.sessionId),
10108
- "accept": "text/event-stream",
10135
+ accept: "text/event-stream",
10109
10136
  "content-type": "application/json",
10110
10137
  ...attributionHeaders()
10111
10138
  },
@@ -10185,10 +10212,10 @@ var CodexWebSearchProvider = class {
10185
10212
  response = await fetchFn(CODEX_SEARCH_URL, {
10186
10213
  method: "POST",
10187
10214
  headers: {
10188
- "authorization": `Bearer ${session.accessToken}`,
10215
+ authorization: `Bearer ${session.accessToken}`,
10189
10216
  "chatgpt-account-id": session.accountId,
10190
10217
  "content-type": "application/json",
10191
- "originator": "codex_cli_rs",
10218
+ originator: "codex_cli_rs",
10192
10219
  ...attributionHeaders()
10193
10220
  },
10194
10221
  body: JSON.stringify(body),
@@ -10769,7 +10796,7 @@ var PoolAdapter = class extends LlmAdapter {
10769
10796
  if (definition === void 0) throw new LlmError(`unknown pool model "${options.model}"`, "NO_ADAPTER");
10770
10797
  const members = await this.concrete(definition.members);
10771
10798
  const candidates = await this.select(options.model, members, options.sessionId);
10772
- if (candidates.length === 0) throw this.exhausted(options.model, members);
10799
+ if (candidates.length === 0) throw this.exhaustedWithWarn(options.model, members);
10773
10800
  const failures = [];
10774
10801
  for (const member of candidates) {
10775
10802
  const adapter = this.options.adapters[member.provider];
@@ -10804,7 +10831,7 @@ var PoolAdapter = class extends LlmAdapter {
10804
10831
  }
10805
10832
  return;
10806
10833
  }
10807
- throw this.exhausted(options.model, members, failures);
10834
+ throw this.exhaustedWithWarn(options.model, members, failures);
10808
10835
  }
10809
10836
  /**
10810
10837
  * Order the candidates for one request. Health filters both strategies;
@@ -10845,6 +10872,18 @@ var PoolAdapter = class extends LlmAdapter {
10845
10872
  }
10846
10873
  this.sticky.set(key, memberKey(member.provider, member.account, member.model));
10847
10874
  }
10875
+ /**
10876
+ * Build the terminal exhaustion error and log it. Per-member failovers are
10877
+ * already warned above; without this line dsh.log shows the failover chain
10878
+ * but not the exhaustion that ends it. The message carries only the
10879
+ * LlmError's own sanitized text and code — raw provider payloads never
10880
+ * reach here (see the note in {@link exhausted}).
10881
+ */
10882
+ exhaustedWithWarn(model, pool, failures = []) {
10883
+ const error = this.exhausted(model, pool, failures);
10884
+ this.options.onWarn(`pool "${model}": exhausted (${error.code}: ${error.message})`);
10885
+ return error;
10886
+ }
10848
10887
  /** Report attempt failures or this pool's active cooldowns, never unrelated health records. */
10849
10888
  exhausted(model, pool, failures = []) {
10850
10889
  if (pool.length === 0) return new LlmError(`pool "${model}" has no usable member: no account could be resolved`, "NO_ADAPTER");
@@ -11647,9 +11686,9 @@ function grokResetsAt(value) {
11647
11686
  async function fetchGrokUsage(session, fetchFn = hostFetch, signal) {
11648
11687
  const response = await fetchFn(GROK_BILLING_URL, {
11649
11688
  headers: {
11650
- "authorization": `Bearer ${session.accessToken}`,
11689
+ authorization: `Bearer ${session.accessToken}`,
11651
11690
  "x-xai-token-auth": "xai-grok-cli",
11652
- "accept": "application/json",
11691
+ accept: "application/json",
11653
11692
  ...attributionHeaders()
11654
11693
  },
11655
11694
  ...signal === void 0 ? {} : { signal }
@@ -11726,9 +11765,9 @@ function grokCliReasoning(entry) {
11726
11765
  async function fetchGrokCliCatalog(session, fetchFn = hostFetch, signal) {
11727
11766
  const response = await fetchFn(GROK_CLI_MODELS_URL, {
11728
11767
  headers: {
11729
- "authorization": `Bearer ${session.accessToken}`,
11768
+ authorization: `Bearer ${session.accessToken}`,
11730
11769
  "x-xai-token-auth": "xai-grok-cli",
11731
- "accept": "application/json",
11770
+ accept: "application/json",
11732
11771
  ...attributionHeaders()
11733
11772
  },
11734
11773
  ...signal === void 0 ? {} : { signal }
@@ -11792,8 +11831,8 @@ async function fetchGrokModels(session, fetchFn = hostFetch, onWarn, previous, s
11792
11831
  const previousById = previous === void 0 || previous.length === 0 ? void 0 : new Map(previous.map((model) => [model.id, model]));
11793
11832
  const [response, cliCatalog] = await Promise.all([fetchFn(GROK_MODELS_URL, {
11794
11833
  headers: {
11795
- "authorization": `Bearer ${session.accessToken}`,
11796
- "accept": "application/json",
11834
+ authorization: `Bearer ${session.accessToken}`,
11835
+ accept: "application/json",
11797
11836
  ...attributionHeaders()
11798
11837
  },
11799
11838
  ...signal === void 0 ? {} : { signal }
@@ -12017,8 +12056,8 @@ var GrokAdapter = class extends LlmAdapter {
12017
12056
  return hostFetch(GROK_API_URL, {
12018
12057
  method: "POST",
12019
12058
  headers: {
12020
- "authorization": `Bearer ${session.accessToken}`,
12021
- "accept": "text/event-stream",
12059
+ authorization: `Bearer ${session.accessToken}`,
12060
+ accept: "text/event-stream",
12022
12061
  "content-type": "application/json",
12023
12062
  ...attributionHeaders()
12024
12063
  },
@@ -12131,8 +12170,8 @@ function copilotHeaders(hasVision = false, vscodeVersion = FALLBACK_VSCODE_VERSI
12131
12170
  */
12132
12171
  async function exchangeCopilotToken(githubToken, fetchFn = hostFetch) {
12133
12172
  const response = await fetchFn(COPILOT_TOKEN_URL, { headers: {
12134
- "authorization": `Bearer ${githubToken}`,
12135
- "accept": "application/json",
12173
+ authorization: `Bearer ${githubToken}`,
12174
+ accept: "application/json",
12136
12175
  ...copilotHeaders(false, await latestVsCodeVersion(fetchFn))
12137
12176
  } });
12138
12177
  if (!response.ok) throw await oauthEndpointError(response, "copilot");
@@ -12155,8 +12194,8 @@ async function completeCopilotLogin(githubToken, fetchFn = hostFetch) {
12155
12194
  let account;
12156
12195
  try {
12157
12196
  const response = await fetchFn(GITHUB_USER_URL, { headers: {
12158
- "authorization": `Bearer ${githubToken}`,
12159
- "accept": "application/json",
12197
+ authorization: `Bearer ${githubToken}`,
12198
+ accept: "application/json",
12160
12199
  "user-agent": "GitHubCopilotChat/0.35.0"
12161
12200
  } });
12162
12201
  if (response.ok) {
@@ -12240,8 +12279,8 @@ function copilotReasoning(entry) {
12240
12279
  async function fetchCopilotModels(session, fetchFn = hostFetch, signal) {
12241
12280
  const response = await fetchFn(COPILOT_MODELS_URL, {
12242
12281
  headers: {
12243
- "authorization": `Bearer ${session.accessToken}`,
12244
- "accept": "application/json",
12282
+ authorization: `Bearer ${session.accessToken}`,
12283
+ accept: "application/json",
12245
12284
  ...copilotHeaders(false, await latestVsCodeVersion(fetchFn))
12246
12285
  },
12247
12286
  ...signal === void 0 ? {} : { signal }
@@ -12757,8 +12796,8 @@ var CopilotAdapter = class CopilotAdapter extends LlmAdapter {
12757
12796
  return hostFetch(wire === "responses" ? COPILOT_RESPONSES_URL : COPILOT_API_URL, {
12758
12797
  method: "POST",
12759
12798
  headers: {
12760
- "authorization": `Bearer ${session.accessToken}`,
12761
- "accept": "text/event-stream",
12799
+ authorization: `Bearer ${session.accessToken}`,
12800
+ accept: "text/event-stream",
12762
12801
  "content-type": "application/json",
12763
12802
  ...copilotHeaders(hasVision, await latestVsCodeVersion(this.options.fetchFn ?? hostFetch))
12764
12803
  },
@@ -13432,7 +13471,7 @@ function antigravityFlow(oauth) {
13432
13471
  /** Shared Antigravity API headers. */
13433
13472
  function antigravityHeaders(accessToken, userAgent = ANTIGRAVITY_DEFAULT_USER_AGENT) {
13434
13473
  return {
13435
- "authorization": `Bearer ${accessToken}`,
13474
+ authorization: `Bearer ${accessToken}`,
13436
13475
  "content-type": "application/json",
13437
13476
  "user-agent": userAgent
13438
13477
  };
@@ -13951,9 +13990,9 @@ function createXSearchTool(options) {
13951
13990
  const response = await (options.fetchFn ?? hostFetch)(X_SEARCH_URL, {
13952
13991
  method: "POST",
13953
13992
  headers: {
13954
- "authorization": `Bearer ${session.accessToken}`,
13993
+ authorization: `Bearer ${session.accessToken}`,
13955
13994
  "content-type": "application/json",
13956
- "accept": "application/json"
13995
+ accept: "application/json"
13957
13996
  },
13958
13997
  body: JSON.stringify({
13959
13998
  model: X_SEARCH_MODEL,
@@ -14033,7 +14072,7 @@ const GROK_ASPECT_RATIOS = {
14033
14072
  "1024x1024": "1:1",
14034
14073
  "1024x1536": "2:3",
14035
14074
  "1536x1024": "3:2",
14036
- "auto": "auto"
14075
+ auto: "auto"
14037
14076
  };
14038
14077
  /**
14039
14078
  * Assemble the grok request body from the same tool arguments: `size` maps
@@ -14319,11 +14358,11 @@ function createImageGenerateTool(options) {
14319
14358
  send: (session) => fetchFn(references === void 0 ? IMAGE_GENERATE_URL : IMAGE_EDIT_URL, {
14320
14359
  method: "POST",
14321
14360
  headers: {
14322
- "authorization": `Bearer ${session.accessToken}`,
14361
+ authorization: `Bearer ${session.accessToken}`,
14323
14362
  "chatgpt-account-id": session.accountId,
14324
- "originator": "codex_cli_rs",
14363
+ originator: "codex_cli_rs",
14325
14364
  "content-type": "application/json",
14326
- "accept": "application/json"
14365
+ accept: "application/json"
14327
14366
  },
14328
14367
  body: JSON.stringify({
14329
14368
  ...buildImageGenerateBody(args),
@@ -14341,9 +14380,9 @@ function createImageGenerateTool(options) {
14341
14380
  send: (session) => fetchFn(references === void 0 ? GROK_IMAGE_GENERATE_URL : GROK_IMAGE_EDIT_URL, {
14342
14381
  method: "POST",
14343
14382
  headers: {
14344
- "authorization": `Bearer ${session.accessToken}`,
14383
+ authorization: `Bearer ${session.accessToken}`,
14345
14384
  "content-type": "application/json",
14346
- "accept": "application/json"
14385
+ accept: "application/json"
14347
14386
  },
14348
14387
  body: JSON.stringify({
14349
14388
  ...buildGrokImageGenerateBody(args),
@@ -14602,8 +14641,8 @@ function createVideoGenerateTool(options) {
14602
14641
  const session = await options.tokens.session();
14603
14642
  const fetchFn = options.fetchFn ?? hostFetch;
14604
14643
  const headers = {
14605
- "authorization": `Bearer ${session.accessToken}`,
14606
- "accept": "application/json"
14644
+ authorization: `Bearer ${session.accessToken}`,
14645
+ accept: "application/json"
14607
14646
  };
14608
14647
  const submit = await fetchFn(VIDEO_GENERATE_URL, {
14609
14648
  method: "POST",
@@ -14705,6 +14744,11 @@ function validatePreferences(provider, input) {
14705
14744
  result.contextWindows[model] = value;
14706
14745
  }
14707
14746
  }
14747
+ if (raw.promptCacheTtl !== void 0) {
14748
+ if (provider !== "claude") throw new Error("the prompt-cache TTL override is supported only for Claude");
14749
+ if (raw.promptCacheTtl !== "5m" && raw.promptCacheTtl !== "1h") throw new Error("promptCacheTtl must be \"5m\" or \"1h\"");
14750
+ result.promptCacheTtl = raw.promptCacheTtl;
14751
+ }
14708
14752
  if (raw.tools !== void 0) {
14709
14753
  if (!raw.tools || typeof raw.tools !== "object" || Array.isArray(raw.tools)) throw new Error("tools must be an object");
14710
14754
  result.tools = {};
@@ -14753,6 +14797,14 @@ var ProviderSettingsStore = class {
14753
14797
  const windows = this.current.providers[provider]?.contextWindows;
14754
14798
  return windows && Object.hasOwn(windows, model) ? windows[model] : void 0;
14755
14799
  }
14800
+ /**
14801
+ * The Claude route's prompt-cache TTL override, read live so a saved change
14802
+ * takes effect from the next request without a restart. Absent means follow
14803
+ * the `claudePromptCacheTtl` config.
14804
+ */
14805
+ promptCacheTtl() {
14806
+ return this.current.providers.claude?.promptCacheTtl;
14807
+ }
14756
14808
  /** Creation-time policy survives restarts and never changes an existing session. */
14757
14809
  toolEnabled(provider, tool, createdAt = Date.now()) {
14758
14810
  let enabled = true;
@@ -15064,7 +15116,7 @@ var SubscriptionsAuthController = class {
15064
15116
  * cannot be read off the flow manager.
15065
15117
  */
15066
15118
  claims = /* @__PURE__ */ new Map();
15067
- constructor(flows, deviceFlows, onAuthChanged, resolveAttachments, usageFetchers = {}, readClaudeCreds = readClaudeCodeCredentials, poolUsage = void 0, antigravityConfig = {}, clientVersions = {}, resetRedemption) {
15119
+ constructor(flows, deviceFlows, onAuthChanged, resolveAttachments, usageFetchers = {}, readClaudeCreds = readClaudeCodeCredentials, poolUsage = void 0, antigravityConfig = {}, clientVersions = {}, resetRedemption, onUsageForced) {
15068
15120
  this.flows = flows;
15069
15121
  this.deviceFlows = deviceFlows;
15070
15122
  this.onAuthChanged = onAuthChanged;
@@ -15075,6 +15127,7 @@ var SubscriptionsAuthController = class {
15075
15127
  this.antigravityConfig = antigravityConfig;
15076
15128
  this.clientVersions = clientVersions;
15077
15129
  this.resetRedemption = resetRedemption;
15130
+ this.onUsageForced = onUsageForced;
15078
15131
  }
15079
15132
  prepareReset(account, signal) {
15080
15133
  if (!this.resetRedemption) throw new BadRequest("Reset redemption unavailable");
@@ -15085,6 +15138,7 @@ var SubscriptionsAuthController = class {
15085
15138
  await this.resetRedemption.redeem(account, ticket, AbortSignal.any([signal, AbortSignal.timeout(2e4)]));
15086
15139
  }
15087
15140
  usage(provider, account, signal, force = false) {
15141
+ if (force) this.onUsageForced?.(provider, account);
15088
15142
  const fetcher = this.usageFetchers[provider];
15089
15143
  if (fetcher === void 0) return Promise.resolve({ supported: false });
15090
15144
  if (this.poolUsage === void 0) return fetcher(account, signal);
@@ -15397,8 +15451,7 @@ function apply(ctx, config) {
15397
15451
  };
15398
15452
  }
15399
15453
  };
15400
- let adapter;
15401
- adapter = new CodexAdapter({
15454
+ const adapter = new CodexAdapter({
15402
15455
  ...config.codexClientVersion === void 0 ? {} : { clientVersion: config.codexClientVersion },
15403
15456
  resolveClientVersion: () => codexVersion.resolve(),
15404
15457
  models: catalog.codex,
@@ -15449,6 +15502,7 @@ function apply(ctx, config) {
15449
15502
  contextWindowOf: (model) => preferences.contextWindow(model, "claude"),
15450
15503
  resolveCliVersion: () => claudeVersion.resolve(),
15451
15504
  promptCacheTtl: claudePromptCacheTtl,
15505
+ promptCacheTtlOverride: () => preferences.promptCacheTtl(),
15452
15506
  pool: () => poolAdapter
15453
15507
  });
15454
15508
  claudeAdapter = adapter;
@@ -15661,7 +15715,7 @@ function apply(ctx, config) {
15661
15715
  const catalog$1 = [];
15662
15716
  for (const provider of PROVIDER_IDS) {
15663
15717
  if (!visible.has(provider)) continue;
15664
- let models = [];
15718
+ let models;
15665
15719
  try {
15666
15720
  models = await ctx.llm.listModels(provider);
15667
15721
  } catch {
@@ -15776,7 +15830,7 @@ function apply(ctx, config) {
15776
15830
  source: "config"
15777
15831
  }) } : {},
15778
15832
  ...providers.includes("claude") ? { claude: presentedVersion(claudeVersion) } : {}
15779
- }, resetRedemption), speed, modelDefaults, {
15833
+ }, resetRedemption, (provider, account) => poolHealth?.clear(provider, account)), speed, modelDefaults, {
15780
15834
  async get(provider, force) {
15781
15835
  await loadModelDefaults();
15782
15836
  const adapter = adapters.get(provider);
@@ -163,7 +163,9 @@ export function defaultEffortOf(provider, model) {
163
163
  // plain index would inherit from Object.prototype for names like
164
164
  // `toString`, handing a *function* to mergeReasoning (which then throws and
165
165
  // breaks that model's resolution).
166
- return Object.prototype.hasOwnProperty.call(section, model) ? section[model] : undefined;
166
+ return Object.prototype.hasOwnProperty.call(section, model)
167
+ ? section[model]
168
+ : undefined;
167
169
  }
168
170
  /**
169
171
  * Detached snapshot for store assertions in tests; no production RPC uses it.
@@ -193,7 +195,7 @@ export function setDefaultEffort(provider, model, effort) {
193
195
  // shared chain.
194
196
  const run = writeChain.then(async () => {
195
197
  await ensureReady();
196
- const section = { ...sectionOf(current, provider) ?? {} };
198
+ const section = { ...(sectionOf(current, provider) ?? {}) };
197
199
  if (effort === undefined) {
198
200
  delete section[model];
199
201
  }
@@ -1,4 +1,5 @@
1
1
  import { type ProviderId } from './auth/store.js';
2
+ import type { PromptCacheTtl } from './translate/anthropic.js';
2
3
  export declare const PROVIDER_TOOLS: {
3
4
  readonly codex: readonly ["image_generate", "web_search"];
4
5
  readonly claude: readonly [];
@@ -22,6 +23,11 @@ export interface ProviderPreferences {
22
23
  visibleModels?: string[];
23
24
  contextWindows?: Record<string, number>;
24
25
  tools?: Partial<Record<SubscriptionTool, boolean>>;
26
+ /**
27
+ * Claude only: overrides the `claudePromptCacheTtl` plugin config for this
28
+ * route. Absent follows the configured TTL.
29
+ */
30
+ promptCacheTtl?: PromptCacheTtl;
25
31
  }
26
32
  export declare function validatePreferences(provider: ProviderId, input: unknown): ProviderPreferences;
27
33
  /** Durable user preferences, independent of expiring discovery caches. */
@@ -34,6 +40,12 @@ export declare class ProviderSettingsStore {
34
40
  visible(provider: ProviderId, model: string): boolean;
35
41
  /** The user's local context-window override for one model; absent follows the provider. */
36
42
  contextWindow(model: string, provider?: ProviderId): number | undefined;
43
+ /**
44
+ * The Claude route's prompt-cache TTL override, read live so a saved change
45
+ * takes effect from the next request without a restart. Absent means follow
46
+ * the `claudePromptCacheTtl` config.
47
+ */
48
+ promptCacheTtl(): PromptCacheTtl | undefined;
37
49
  /** Creation-time policy survives restarts and never changes an existing session. */
38
50
  toolEnabled(provider: ProviderId, tool: SubscriptionTool, createdAt?: number): boolean;
39
51
  set(provider: ProviderId, input: unknown): Promise<void>;