@gajae-code/ai 0.17.2 → 0.17.4

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 (41) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/dist/types/auth-gateway/server.d.ts +23 -1
  3. package/dist/types/auth-storage.d.ts +12 -1
  4. package/dist/types/providers/anthropic.d.ts +1 -1
  5. package/dist/types/providers/cursor.d.ts +10 -0
  6. package/dist/types/providers/openai-completions.d.ts +9 -1
  7. package/dist/types/types.d.ts +16 -0
  8. package/dist/types/utils/discovery/openai-compatible.d.ts +10 -0
  9. package/dist/types/utils/fallback-transport.d.ts +4 -0
  10. package/dist/types/utils/h2-fetch.d.ts +8 -7
  11. package/dist/types/utils/stream-repetition-guard.d.ts +107 -0
  12. package/dist/types/utils/tool-call-healing.d.ts +4 -0
  13. package/dist/types/utils/tool-fence-strip.d.ts +27 -0
  14. package/package.json +3 -3
  15. package/src/auth-gateway/server.ts +48 -9
  16. package/src/auth-storage.ts +185 -48
  17. package/src/model-pricing.ts +22 -0
  18. package/src/model-thinking.ts +40 -4
  19. package/src/models.json +191 -15
  20. package/src/providers/anthropic.d.ts +1 -1
  21. package/src/providers/anthropic.ts +1 -1
  22. package/src/providers/cursor.d.ts +10 -0
  23. package/src/providers/cursor.ts +144 -24
  24. package/src/providers/openai-completions.d.ts +9 -1
  25. package/src/providers/openai-completions.ts +371 -126
  26. package/src/stream.ts +7 -0
  27. package/src/types.d.ts +16 -0
  28. package/src/types.ts +17 -0
  29. package/src/utils/discovery/openai-compatible.ts +16 -2
  30. package/src/utils/fallback-transport.d.ts +4 -0
  31. package/src/utils/fallback-transport.ts +11 -0
  32. package/src/utils/h2-fetch.ts +65 -26
  33. package/src/utils/http-inspector.ts +2 -0
  34. package/src/utils/idle-iterator.ts +109 -96
  35. package/src/utils/json-parse.ts +12 -4
  36. package/src/utils/stream-repetition-guard.d.ts +107 -0
  37. package/src/utils/stream-repetition-guard.ts +290 -0
  38. package/src/utils/tool-call-healing.d.ts +4 -0
  39. package/src/utils/tool-call-healing.ts +4 -0
  40. package/src/utils/tool-fence-strip.d.ts +27 -0
  41. package/src/utils/tool-fence-strip.ts +64 -0
@@ -481,6 +481,10 @@ export interface AuthCredentialStore {
481
481
  deleteAuthCredentialsForProvider(provider: string, disabledCause: string): void;
482
482
  getCache(key: string, options?: { includeExpired?: boolean }): string | null;
483
483
  setCache(key: string, value: string, expiresAtSec: number): void;
484
+ /** Atomically claim a cross-process usage poll lease for a bounded interval. */
485
+ tryAcquireUsageFetchLease?(key: string, owner: string, nowMs: number, leaseMs: number): boolean | undefined;
486
+ /** Release a usage poll lease owned by this process. */
487
+ releaseUsageFetchLease?(key: string, owner: string): void;
484
488
  /** Atomically allocate a durable sequence for broker restart epochs. */
485
489
  allocateMonotonicSequence(key: string, expiresAtSec: number): number;
486
490
  deleteCachePrefix?(prefix: string): void;
@@ -929,6 +933,21 @@ const DEFAULT_RANKING_STRATEGIES = new Map<Provider, CredentialRankingStrategy>(
929
933
  ]);
930
934
 
931
935
  const USAGE_CACHE_PREFIX = "usage_cache:";
936
+ function hashUsageDiagnosticValue(value: unknown): string | undefined {
937
+ if (typeof value !== "string" || value.length === 0) return undefined;
938
+ return Bun.hash(value).toString(16);
939
+ }
940
+
941
+ function redactUsageDiagnosticUrl(value: string | undefined): string | undefined {
942
+ if (!value) return undefined;
943
+ try {
944
+ const parsed = new URL(value);
945
+ return `${parsed.protocol}//${parsed.host}`;
946
+ } catch {
947
+ return "[invalid-url]";
948
+ }
949
+ }
950
+
932
951
  // 5 min stale tolerance. Anthropic / OpenAI rate-limit /usage hard at the IP
933
952
  // level so we can't fetch all N credentials every cycle; with a long cache
934
953
  // each credential's last-known value sticks visible while peers retry. UI
@@ -945,6 +964,9 @@ const USAGE_FAILURE_BACKOFF_MS = 10_000;
945
964
  // Bumped from 3s — Anthropic model usage retries up to 3 times with exponential backoff
946
965
  // (~3.5s total worst case); a tight per-request budget aborts retries mid-cycle.
947
966
  const DEFAULT_USAGE_REQUEST_TIMEOUT_MS = 10_000;
967
+ /** Grace period after the configured provider timeout before peers retry a poll. */
968
+ const USAGE_FETCH_LEASE_GRACE_MS = 5_000;
969
+ const USAGE_FETCH_WAIT_POLL_MS = 25;
948
970
  const DEFAULT_OAUTH_REFRESH_TIMEOUT_MS = 10_000;
949
971
  /** Maximum provider ownership window; expiry recovers a crashed process without indefinite blocking. */
950
972
  const OAUTH_REFRESH_LEASE_MS = DEFAULT_OAUTH_REFRESH_TIMEOUT_MS + 5_000;
@@ -1407,6 +1429,7 @@ export class AuthStorage {
1407
1429
  #usageReportsInFlight: Map<string, Promise<UsageReport[] | null>> = new Map();
1408
1430
  #usageFetch: typeof fetch;
1409
1431
  #usageRequestTimeoutMs: number;
1432
+ #usageFetchLeaseMs: number;
1410
1433
  #credentialRankingMode: CredentialRankingMode = "balanced";
1411
1434
  #usageLogger?: UsageLogger;
1412
1435
  #fallbackResolver?: (provider: string) => string | undefined;
@@ -1459,6 +1482,13 @@ export class AuthStorage {
1459
1482
  this.#usageCache = new AuthStorageUsageCache(this.#store);
1460
1483
  this.#usageFetch = options.usageFetch ?? fetch;
1461
1484
  this.#usageRequestTimeoutMs = options.usageRequestTimeoutMs ?? DEFAULT_USAGE_REQUEST_TIMEOUT_MS;
1485
+ const usageTimeoutForLease =
1486
+ typeof this.#usageRequestTimeoutMs === "number" &&
1487
+ Number.isFinite(this.#usageRequestTimeoutMs) &&
1488
+ this.#usageRequestTimeoutMs > 0
1489
+ ? this.#usageRequestTimeoutMs
1490
+ : DEFAULT_USAGE_REQUEST_TIMEOUT_MS;
1491
+ this.#usageFetchLeaseMs = usageTimeoutForLease + USAGE_FETCH_LEASE_GRACE_MS;
1462
1492
  this.#credentialRankingMode = options.credentialRankingMode ?? "balanced";
1463
1493
  this.#refreshOAuthCredentialOverride = options.refreshOAuthCredential;
1464
1494
  this.#fetchUsageReportsOverride = options.fetchUsageReports;
@@ -2009,6 +2039,7 @@ export class AuthStorage {
2009
2039
  this.#usageRequestInFlight.clear();
2010
2040
  this.#usageReportsInFlight.clear();
2011
2041
  this.#usageCache.deletePrefix?.(`report:${storageProvider}:`);
2042
+ this.#usageCache.deletePrefix?.("reports:");
2012
2043
  for (const [scopeId, selectors] of this.#sessionCredentialSelectors) {
2013
2044
  const selector = selectors.get(storageProvider);
2014
2045
  const selected = selector
@@ -2422,6 +2453,7 @@ export class AuthStorage {
2422
2453
  this.#data.set(provider, credentials);
2423
2454
  }
2424
2455
  if (identityOrderChanged) this.#resetProviderAssignments(storageProvider);
2456
+ if (!tokenRotationOnly) this.#invalidateUsageCacheForProvider(storageProvider);
2425
2457
  if (tokenRotationOnly) this.#bumpGeneration("oauth-token-rotation");
2426
2458
  else this.#bumpGeneration("credentials", provider);
2427
2459
  }
@@ -3111,6 +3143,7 @@ export class AuthStorage {
3111
3143
  this.#usageRequestInFlight.clear();
3112
3144
  this.#usageReportsInFlight.clear();
3113
3145
  this.#usageCache.deletePrefix?.(`report:${provider}:`);
3146
+ this.#usageCache.deletePrefix?.("reports:");
3114
3147
  }
3115
3148
 
3116
3149
  /**
@@ -4037,6 +4070,8 @@ export class AuthStorage {
4037
4070
  logDetails: boolean = true,
4038
4071
  ): Promise<UsageReport | null> {
4039
4072
  const cacheKey = this.#buildUsageReportCacheKey(request);
4073
+ const provider = resolveOAuthStorageProvider(request.provider);
4074
+ const credentialGeneration = this.#getProviderGeneration(provider);
4040
4075
  const now = Date.now();
4041
4076
  const cached = this.#usageCache.get<UsageReport | null>(cacheKey);
4042
4077
  // Fresh cache hit: return whatever's there (success or null fallback).
@@ -4049,6 +4084,7 @@ export class AuthStorage {
4049
4084
 
4050
4085
  const promise = (async () => {
4051
4086
  const report = await this.#fetchUsageUncached(request, timeoutMs, logDetails);
4087
+ if (this.#getProviderGeneration(provider) !== credentialGeneration) return null;
4052
4088
  const ttlJitter = USAGE_REPORT_TTL_MS * (Math.random() * 0.5 - 0.25);
4053
4089
  if (report !== null) {
4054
4090
  // Success: stagger per-credential cache expiry so all accounts don't
@@ -4080,6 +4116,51 @@ export class AuthStorage {
4080
4116
  return promise;
4081
4117
  }
4082
4118
 
4119
+ #captureUsageProviderGenerations(requests: ReadonlyArray<UsageRequestDescriptor>): Map<string, number> {
4120
+ const generations = new Map<string, number>();
4121
+ for (const request of requests) {
4122
+ const provider = resolveOAuthStorageProvider(request.provider);
4123
+ if (!generations.has(provider)) generations.set(provider, this.#getProviderGeneration(provider));
4124
+ }
4125
+ return generations;
4126
+ }
4127
+
4128
+ #usageProviderGenerationsMatch(generations: ReadonlyMap<string, number>): boolean {
4129
+ for (const [provider, generation] of generations) {
4130
+ if (this.#getProviderGeneration(provider) !== generation) return false;
4131
+ }
4132
+ return true;
4133
+ }
4134
+
4135
+ #readAggregateUsageCache(cacheKey: string): UsageReport[] | undefined {
4136
+ const cached = this.#usageCache.get<UsageReport[]>(cacheKey);
4137
+ if (!cached || cached.expiresAt <= Date.now() || !Array.isArray(cached.value)) return undefined;
4138
+ return cached.value;
4139
+ }
4140
+
4141
+ #tryAcquireUsageFetchLease(cacheKey: string): boolean | undefined {
4142
+ const claim = this.#store.tryAcquireUsageFetchLease;
4143
+ if (!claim) return undefined;
4144
+ return claim.call(this.#store, cacheKey, this.#oauthRefreshLeaseOwner, Date.now(), this.#usageFetchLeaseMs);
4145
+ }
4146
+
4147
+ async #waitForAggregateUsagePoll(
4148
+ cacheKey: string,
4149
+ ): Promise<{ cached: UsageReport[] | undefined; leaseAcquired: boolean }> {
4150
+ const deadline = Date.now() + this.#usageFetchLeaseMs;
4151
+ while (Date.now() < deadline) {
4152
+ const cached = this.#readAggregateUsageCache(cacheKey);
4153
+ if (cached !== undefined) return { cached, leaseAcquired: false };
4154
+ if (this.#tryAcquireUsageFetchLease(cacheKey) === true) return { cached: undefined, leaseAcquired: true };
4155
+ await Bun.sleep(USAGE_FETCH_WAIT_POLL_MS);
4156
+ }
4157
+ return { cached: this.#readAggregateUsageCache(cacheKey), leaseAcquired: false };
4158
+ }
4159
+
4160
+ #releaseUsageFetchLease(cacheKey: string): void {
4161
+ this.#store.releaseUsageFetchLease?.(cacheKey, this.#oauthRefreshLeaseOwner);
4162
+ }
4163
+
4083
4164
  #collectUsageRequests(options?: {
4084
4165
  provider?: Provider;
4085
4166
  baseUrlResolver?: (provider: Provider) => string | undefined;
@@ -4297,10 +4378,15 @@ export class AuthStorage {
4297
4378
  async fetchUsageReports(options?: {
4298
4379
  provider?: Provider;
4299
4380
  baseUrlResolver?: (provider: Provider) => string | undefined;
4300
- /** Caller's cancel signal; only rejects this caller, never the shared upstream fetch. */
4381
+ /** Caller’s cancel signal; only rejects this caller, never the shared upstream fetch. */
4301
4382
  signal?: AbortSignal;
4302
4383
  /** Disable provider/account/error logging for secret-safe control surfaces. */
4303
4384
  logDetails?: boolean;
4385
+ /**
4386
+ * Enable identity correlation diagnostics. Values are one-way hashed and
4387
+ * URLs are reduced to their origin; the default logs provider/type/count only.
4388
+ */
4389
+ logIdentity?: boolean;
4304
4390
  }): Promise<UsageReport[] | null> {
4305
4391
  // Caller override > store-level hook > local per-credential fan-out.
4306
4392
  // `RemoteAuthCredentialStore` implements the store hook so a gateway
@@ -4340,63 +4426,87 @@ export class AuthStorage {
4340
4426
  const requests = this.#collectUsageRequests(options);
4341
4427
  if (requests.length === 0) return [];
4342
4428
 
4343
- if (options?.logDetails !== false) {
4344
- this.#usageLogger?.debug("Usage fetch requested", {
4345
- providers: [...new Set(requests.map(request => request.provider))].sort(),
4346
- });
4347
- }
4348
-
4349
4429
  // Per-credential caching with jitter lives in #fetchUsageCached, so we
4350
- // don't store the aggregated result here — doing so locks the widget to
4351
- // a single decorrelation snapshot for 30s, defeating the jitter (some
4352
- // accounts can be missing from one fetch and present in the next; the
4353
- // aggregate cache freezes whichever set landed first).
4430
+ // also retain the completed aggregate in the durable store. The aggregate
4431
+ // cache is what lets another process reuse this poll instead of starting a
4432
+ // second provider fan-out while the first process is still collecting rows.
4354
4433
  const cacheKey = this.#buildUsageReportsCacheKey(requests);
4355
-
4434
+ const providerGenerations = this.#captureUsageProviderGenerations(requests);
4356
4435
  const inFlight = this.#usageReportsInFlight.get(cacheKey);
4357
4436
  if (inFlight) return raceUsageWithSignal(inFlight, options?.signal);
4358
4437
 
4359
4438
  const promise = (async () => {
4360
- if (options?.logDetails !== false) {
4361
- for (const request of requests) {
4362
- this.#usageLogger?.debug("Usage fetch queued", {
4363
- provider: request.provider,
4364
- credentialType: request.credential.type,
4365
- baseUrl: request.baseUrl,
4366
- accountId: request.credential.accountId,
4367
- email: request.credential.email,
4439
+ let leaseOwned = false;
4440
+ try {
4441
+ const initialLeaseClaim = this.#tryAcquireUsageFetchLease(cacheKey);
4442
+ const leaseSupported = initialLeaseClaim !== undefined;
4443
+ if (leaseSupported) {
4444
+ leaseOwned = initialLeaseClaim === true;
4445
+ if (!leaseOwned) {
4446
+ const shared = await this.#waitForAggregateUsagePoll(cacheKey);
4447
+ if (shared.cached !== undefined) return shared.cached;
4448
+ leaseOwned = shared.leaseAcquired || this.#tryAcquireUsageFetchLease(cacheKey) === true;
4449
+ }
4450
+ }
4451
+
4452
+ if (options?.logDetails !== false) {
4453
+ this.#usageLogger?.debug("Usage fetch requested", {
4454
+ providers: [...new Set(requests.map(request => request.provider))].sort(),
4455
+ credentialTypes: [...new Set(requests.map(request => request.credential.type))].sort(),
4456
+ credentials: requests.length,
4368
4457
  });
4458
+ if (options?.logIdentity === true) {
4459
+ for (const request of requests) {
4460
+ this.#usageLogger?.debug("Usage fetch queued", {
4461
+ provider: request.provider,
4462
+ credentialType: request.credential.type,
4463
+ baseUrl: redactUsageDiagnosticUrl(request.baseUrl),
4464
+ accountId: hashUsageDiagnosticValue(request.credential.accountId),
4465
+ email: hashUsageDiagnosticValue(request.credential.email),
4466
+ });
4467
+ }
4468
+ }
4369
4469
  }
4370
- }
4371
4470
 
4372
- const results = await Promise.all(
4373
- requests.map(request =>
4374
- this.#fetchUsageCached(request, this.#usageRequestTimeoutMs, options?.logDetails !== false),
4375
- ),
4376
- );
4377
- const reports = results.filter((report): report is UsageReport => report !== null);
4378
- const deduped = this.#dedupeUsageReports(reports);
4379
- // no outer cache write — see comment above.
4380
- const resolved = deduped;
4381
- if (options?.logDetails !== false) {
4382
- this.#usageLogger?.debug("Usage fetch resolved", {
4383
- reports: resolved.map(report => {
4384
- const accountLabel =
4385
- this.#getUsageReportMetadataValue(report, "email") ??
4386
- this.#getUsageReportMetadataValue(report, "accountId") ??
4387
- this.#getUsageReportMetadataValue(report, "account") ??
4388
- this.#getUsageReportMetadataValue(report, "user") ??
4389
- this.#getUsageReportMetadataValue(report, "username") ??
4390
- this.#getUsageReportScopeAccountId(report);
4391
- return {
4392
- provider: report.provider,
4393
- limits: report.limits.length,
4394
- account: accountLabel,
4395
- };
4396
- }),
4397
- });
4471
+ const results = await Promise.all(
4472
+ requests.map(request =>
4473
+ this.#fetchUsageCached(request, this.#usageRequestTimeoutMs, options?.logDetails !== false),
4474
+ ),
4475
+ );
4476
+ const reports = results.filter((report): report is UsageReport => report !== null);
4477
+ const resolved = this.#dedupeUsageReports(reports);
4478
+ if (this.#usageProviderGenerationsMatch(providerGenerations)) {
4479
+ // This is a lease-scoped handoff for concurrent processes, not a
4480
+ // long-lived aggregate cache: per-credential jitter remains active
4481
+ // on the next poll.
4482
+ this.#usageCache.set(cacheKey, { value: resolved, expiresAt: Date.now() + this.#usageFetchLeaseMs });
4483
+ }
4484
+ if (options?.logDetails !== false) {
4485
+ this.#usageLogger?.debug("Usage fetch resolved", {
4486
+ reports:
4487
+ options?.logIdentity === true
4488
+ ? resolved.map(report => ({
4489
+ provider: report.provider,
4490
+ limits: report.limits.length,
4491
+ account: hashUsageDiagnosticValue(
4492
+ this.#getUsageReportMetadataValue(report, "email") ??
4493
+ this.#getUsageReportMetadataValue(report, "accountId") ??
4494
+ this.#getUsageReportMetadataValue(report, "account") ??
4495
+ this.#getUsageReportMetadataValue(report, "user") ??
4496
+ this.#getUsageReportMetadataValue(report, "username") ??
4497
+ this.#getUsageReportScopeAccountId(report),
4498
+ ),
4499
+ }))
4500
+ : {
4501
+ count: resolved.length,
4502
+ providers: [...new Set(resolved.map(report => report.provider))].sort(),
4503
+ },
4504
+ });
4505
+ }
4506
+ return resolved;
4507
+ } finally {
4508
+ if (leaseOwned) this.#releaseUsageFetchLease(cacheKey);
4398
4509
  }
4399
- return resolved;
4400
4510
  })().finally(() => {
4401
4511
  if (this.#usageReportsInFlight.get(cacheKey) === promise) {
4402
4512
  this.#usageReportsInFlight.delete(cacheKey);
@@ -6669,6 +6779,8 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
6669
6779
  #getCacheStmt: Statement;
6670
6780
  #getCacheIncludingExpiredStmt: Statement;
6671
6781
  #upsertCacheStmt: Statement;
6782
+ #claimUsageFetchLeaseStmt: Statement;
6783
+ #releaseUsageFetchLeaseStmt: Statement;
6672
6784
  #deleteCachePrefixStmt: Statement;
6673
6785
  #deleteExpiredCacheStmt: Statement;
6674
6786
  #closed = false;
@@ -6718,6 +6830,10 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
6718
6830
  this.#upsertCacheStmt = this.#db.prepare(
6719
6831
  "INSERT INTO cache (key, value, expires_at) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, expires_at = excluded.expires_at",
6720
6832
  );
6833
+ this.#claimUsageFetchLeaseStmt = this.#db.prepare(
6834
+ "INSERT INTO cache (key, value, expires_at) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, expires_at = excluded.expires_at WHERE cache.expires_at <= ?",
6835
+ );
6836
+ this.#releaseUsageFetchLeaseStmt = this.#db.prepare("DELETE FROM cache WHERE key = ? AND value = ?");
6721
6837
  this.#deleteCachePrefixStmt = this.#db.prepare("DELETE FROM cache WHERE substr(key, 1, ?) = ?");
6722
6838
  this.#deleteExpiredCacheStmt = this.#db.prepare(`DELETE FROM cache WHERE expires_at <= ${SQLITE_NOW_EPOCH}`);
6723
6839
  }
@@ -7431,6 +7547,27 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
7431
7547
  }
7432
7548
  }
7433
7549
 
7550
+ tryAcquireUsageFetchLease(key: string, owner: string, nowMs: number, leaseMs: number): boolean | undefined {
7551
+ try {
7552
+ const nowSec = Math.floor(nowMs / 1000);
7553
+ const expiresAtSec = Math.ceil((nowMs + leaseMs) / 1000);
7554
+ const result = this.#claimUsageFetchLeaseStmt.run(`usage_fetch_lease:${key}`, owner, expiresAtSec, nowSec) as {
7555
+ changes: number;
7556
+ };
7557
+ return result.changes === 1;
7558
+ } catch {
7559
+ return undefined;
7560
+ }
7561
+ }
7562
+
7563
+ releaseUsageFetchLease(key: string, owner: string): void {
7564
+ try {
7565
+ this.#releaseUsageFetchLeaseStmt.run(`usage_fetch_lease:${key}`, owner);
7566
+ } catch {
7567
+ // Ignore cache lease cleanup failures; the bounded expiry recovers it.
7568
+ }
7569
+ }
7570
+
7434
7571
  allocateMonotonicSequence(key: string, expiresAtSec: number): number {
7435
7572
  const allocate = this.#db.transaction(() => {
7436
7573
  const row = this.#getCacheIncludingExpiredStmt.get(key) as { value?: string } | undefined;
@@ -25,9 +25,31 @@ const GPT_6_ASTRA_PRICING: TieredPricing = {
25
25
  },
26
26
  };
27
27
 
28
+ // GPT-6 Sol: $2/$10 standard, cache read $0.20, cache write $2.50; inputs past
29
+ // 272K apply 2x to input/cache and 1.5x to output.
30
+ const GPT_6_SOL_PRICING: TieredPricing = {
31
+ cost: { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 },
32
+ longContextPricing: {
33
+ threshold: LONG_CONTEXT_THRESHOLD,
34
+ cost: { input: 4, output: 15, cacheRead: 0.4, cacheWrite: 5 },
35
+ },
36
+ };
37
+
38
+ // GPT-6 Luna: $0.10/$0.50 standard, cache read $0.01, cache write $0.125;
39
+ // inputs past 272K apply 2x to input/cache and 1.5x to output.
40
+ const GPT_6_LUNA_PRICING: TieredPricing = {
41
+ cost: { input: 0.1, output: 0.5, cacheRead: 0.01, cacheWrite: 0.125 },
42
+ longContextPricing: {
43
+ threshold: LONG_CONTEXT_THRESHOLD,
44
+ cost: { input: 0.2, output: 0.75, cacheRead: 0.02, cacheWrite: 0.25 },
45
+ },
46
+ };
47
+
28
48
  // OpenAI Standard pricing: https://developers.openai.com/api/docs/pricing
29
49
  const OPENAI_GPT_5_6_PRICING: ReadonlyMap<string, TieredPricing> = new Map([
30
50
  ["gpt-6-astra", GPT_6_ASTRA_PRICING],
51
+ ["gpt-6-sol", GPT_6_SOL_PRICING],
52
+ ["gpt-6-luna", GPT_6_LUNA_PRICING],
31
53
  ["gpt-5.6", GPT_5_6_SOL_PRICING],
32
54
  ["gpt-5.6-sol", GPT_5_6_SOL_PRICING],
33
55
  [
@@ -66,6 +66,17 @@ const GPT_5_6_PLUS_EFFORTS: readonly Effort[] = [Effort.Low, Effort.Medium, Effo
66
66
  const GPT_5_5_DEFAULT_EFFORT = Effort.XHigh;
67
67
  const KIMI_K3_EFFORTS: readonly Effort[] = [Effort.Low, Effort.High, Effort.Max];
68
68
  const DEEPSEEK_V4_FLASH_0731_EFFORTS: readonly Effort[] = [Effort.Low, Effort.High, Effort.Max];
69
+ const ALIBABA_GLM_53_EFFORTS: readonly Effort[] = [Effort.Low, Effort.High, Effort.Max];
70
+ const ALIBABA_DEEPSEEK_V4_PINNED_IDS = new Set([
71
+ "deepseek-v4-flash-0731",
72
+ "deepseek-v4-pro-0813",
73
+ "deepseek-v4.1-flash",
74
+ ]);
75
+ const ALIBABA_DEEPSEEK_V4_PINNED_NAMES: Record<string, string> = {
76
+ "deepseek-v4-flash-0731": "DeepSeek V4 Flash 0731",
77
+ "deepseek-v4-pro-0813": "DeepSeek V4 Pro 0813",
78
+ "deepseek-v4.1-flash": "DeepSeek V4.1 Flash",
79
+ };
69
80
  const GROK_4_5_EFFORTS: readonly Effort[] = [Effort.Low, Effort.Medium, Effort.High];
70
81
  const GROK_4_6_EFFORTS: readonly Effort[] = [Effort.Low, Effort.Medium, Effort.High, Effort.XHigh];
71
82
  const GROK_4_20_EFFORTS: readonly Effort[] = [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High];
@@ -320,9 +331,13 @@ export function applyGeneratedModelPolicies(models: ApiModel<Api>[]): void {
320
331
  if (source.provider === "xai" && (source.id === "grok-4.5" || source.id === "grok-4.6")) {
321
332
  source.reasoning = true;
322
333
  }
323
- if (source.provider === "alibaba-token-plan" && source.id === "deepseek-v4-flash-0731") {
334
+ if (source.provider === "alibaba-token-plan" && ALIBABA_DEEPSEEK_V4_PINNED_IDS.has(source.id)) {
335
+ source.reasoning = true;
336
+ source.name = ALIBABA_DEEPSEEK_V4_PINNED_NAMES[source.id] ?? source.name;
337
+ }
338
+ if (source.provider === "alibaba-token-plan" && source.id === "glm-5.3") {
324
339
  source.reasoning = true;
325
- source.name = "DeepSeek V4 Flash 0731";
340
+ source.name = "GLM-5.3";
326
341
  }
327
342
  if (source.id.split("/").at(-1)?.toLowerCase() === "muse-spark-1.2") {
328
343
  source.reasoning = true;
@@ -669,7 +684,7 @@ function applyGeneratedModelPolicy(model: ApiModel<Api>): void {
669
684
  levels: [Effort.Low, Effort.High, Effort.Max],
670
685
  };
671
686
  }
672
- if (model.provider === "alibaba-token-plan" && model.id === "deepseek-v4-flash-0731") {
687
+ if (model.provider === "alibaba-token-plan" && ALIBABA_DEEPSEEK_V4_PINNED_IDS.has(model.id)) {
673
688
  model.contextWindow = 1_000_000;
674
689
  model.maxTokens = 384_000;
675
690
  model.compat = {
@@ -680,6 +695,24 @@ function applyGeneratedModelPolicy(model: ApiModel<Api>): void {
680
695
  requiresReasoningContentForToolCalls: true,
681
696
  };
682
697
  }
698
+ if (model.provider === "alibaba-token-plan" && model.id === "glm-5.3") {
699
+ model.contextWindow = 1_000_000;
700
+ model.maxTokens = 131_072;
701
+ model.compat = {
702
+ ...(model.compat ?? {}),
703
+ supportsDeveloperRole: false,
704
+ supportsReasoningEffort: true,
705
+ reasoningContentField: "reasoning_content",
706
+ requiresReasoningContentForToolCalls: true,
707
+ };
708
+ model.thinking = {
709
+ mode: "effort",
710
+ minLevel: Effort.Low,
711
+ maxLevel: Effort.Max,
712
+ defaultLevel: Effort.Max,
713
+ levels: [Effort.Low, Effort.High, Effort.Max],
714
+ };
715
+ }
683
716
  if (model.provider === "xai" && (model.id === "grok-4.5" || model.id === "grok-4.6")) {
684
717
  model.maxTokens = Math.min(model.maxTokens, 64_000);
685
718
  }
@@ -898,9 +931,12 @@ function inferSupportedEfforts<TApi extends Api>(parsedModel: ParsedModel, model
898
931
  if (model.provider === "kimi-code" && model.id === "k3") {
899
932
  return KIMI_K3_EFFORTS;
900
933
  }
901
- if (model.provider === "alibaba-token-plan" && model.id === "deepseek-v4-flash-0731") {
934
+ if (model.provider === "alibaba-token-plan" && ALIBABA_DEEPSEEK_V4_PINNED_IDS.has(model.id)) {
902
935
  return DEEPSEEK_V4_FLASH_0731_EFFORTS;
903
936
  }
937
+ if (model.provider === "alibaba-token-plan" && model.id === "glm-5.3") {
938
+ return ALIBABA_GLM_53_EFFORTS;
939
+ }
904
940
  switch (parsedModel.family) {
905
941
  case "openai":
906
942
  return inferOpenAISupportedEfforts(parsedModel);