@gajae-code/ai 0.11.9 → 0.11.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,9 +2,17 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.11.11] - 2026-07-26
6
+
7
+ ### Fixed
8
+
9
+ - Anthropic `ping` keepalives no longer reset stream progress, so responses that stop producing content now reach the idle timeout instead of hanging indefinitely.
10
+ - The documented `GJC_OPENAI_STREAM_IDLE_TIMEOUT_MS` environment variable now takes effect: the stream-watchdog idle-timeout helpers resolve it GJC-first before the legacy `PI_OPENAI_STREAM_IDLE_TIMEOUT_MS` / `PI_STREAM_IDLE_TIMEOUT_MS` aliases (previously only the `PI_`-prefixed names were read, so setting the documented GJC name was a silent no-op).
11
+
5
12
  ## [0.11.9] - 2026-07-24
6
13
  ### Fixed
7
14
 
15
+ - Credential selection and aggregate usage callers now stop awaiting immediately when their own signal aborts without cancelling shared usage fetches, and ranking deadlines no longer re-await the same stalled usage request during credential resolution.
8
16
  - Kimi Code now allows one continuous 300-second first-event wait before aborting, while preserving explicit caller and environment timeout overrides and the existing inter-event idle timeout.
9
17
 
10
18
  ### Added
@@ -2,7 +2,7 @@ export declare function getProviderFirstEventTimeoutFallbackMs(provider: string)
2
2
  /**
3
3
  * Returns the idle timeout used for provider streaming transports.
4
4
  *
5
- * `PI_OPENAI_STREAM_IDLE_TIMEOUT_MS` is accepted as a backward-compatible alias.
5
+ * `GJC_OPENAI_STREAM_IDLE_TIMEOUT_MS` is honored first; `PI_OPENAI_STREAM_IDLE_TIMEOUT_MS` is a backward-compatible alias.
6
6
  * Set `PI_STREAM_IDLE_TIMEOUT_MS=0` to disable the watchdog.
7
7
  *
8
8
  * Providers that legitimately stream much slower than the global default can pass
@@ -13,7 +13,7 @@ export declare function getStreamIdleTimeoutMs(fallbackMs?: number): number | un
13
13
  /**
14
14
  * Returns the idle timeout used for OpenAI-family streaming transports.
15
15
  *
16
- * Set `PI_OPENAI_STREAM_IDLE_TIMEOUT_MS=0` to disable the watchdog.
16
+ * Honors `GJC_OPENAI_STREAM_IDLE_TIMEOUT_MS` first (`PI_OPENAI_STREAM_IDLE_TIMEOUT_MS` is the legacy alias). Set `=0` to disable.
17
17
  */
18
18
  export declare function getOpenAIStreamIdleTimeoutMs(): number | undefined;
19
19
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/ai",
4
- "version": "0.11.9",
4
+ "version": "0.11.11",
5
5
  "description": "Unified LLM API with automatic model discovery and provider configuration",
6
6
  "homepage": "https://gajae-code.com",
7
7
  "author": "Yeachan-Heo and Gajae Code Contributors",
@@ -40,7 +40,7 @@
40
40
  "dependencies": {
41
41
  "@anthropic-ai/sdk": "^0.94.0",
42
42
  "@bufbuild/protobuf": "^2.12.0",
43
- "@gajae-code/utils": "0.11.9",
43
+ "@gajae-code/utils": "0.11.11",
44
44
  "openai": "^6.36.0",
45
45
  "partial-json": "^0.1.7",
46
46
  "zod": "4.4.3"
@@ -2514,9 +2514,12 @@ export class AuthStorage {
2514
2514
  if (storeHook) {
2515
2515
  return storeHook(provider, credential, options?.signal);
2516
2516
  }
2517
- return this.#fetchUsageCached(
2518
- this.#buildUsageRequestForOauth(provider, credential, options?.baseUrl),
2519
- options?.timeoutMs ?? this.#usageRequestTimeoutMs,
2517
+ return raceUsageWithSignal(
2518
+ this.#fetchUsageCached(
2519
+ this.#buildUsageRequestForOauth(provider, credential, options?.baseUrl),
2520
+ options?.timeoutMs ?? this.#usageRequestTimeoutMs,
2521
+ ),
2522
+ options?.signal,
2520
2523
  );
2521
2524
  }
2522
2525
 
@@ -2568,7 +2571,7 @@ export class AuthStorage {
2568
2571
  const cacheKey = this.#buildUsageReportsCacheKey(requests);
2569
2572
 
2570
2573
  const inFlight = this.#usageReportsInFlight.get(cacheKey);
2571
- if (inFlight) return inFlight;
2574
+ if (inFlight) return raceUsageWithSignal(inFlight, options?.signal);
2572
2575
 
2573
2576
  const promise = (async () => {
2574
2577
  if (options?.logDetails !== false) {
@@ -2616,7 +2619,7 @@ export class AuthStorage {
2616
2619
  });
2617
2620
 
2618
2621
  this.#usageReportsInFlight.set(cacheKey, promise);
2619
- return promise;
2622
+ return raceUsageWithSignal(promise, options?.signal);
2620
2623
  }
2621
2624
 
2622
2625
  /**
@@ -2867,7 +2870,7 @@ export class AuthStorage {
2867
2870
  const blockedUntil = this.#getCredentialBlockedUntil(args.providerKey, selection.index);
2868
2871
  if (blockedUntil !== undefined) return { selection, usage: null, usageChecked: false, blockedUntil };
2869
2872
  const usage = await this.#getUsageReport(args.provider, selection.credential, {
2870
- ...args.options,
2873
+ baseUrl: args.options?.baseUrl,
2871
2874
  timeoutMs: this.#usageRequestTimeoutMs,
2872
2875
  });
2873
2876
  return { selection, usage, usageChecked: true, blockedUntil: undefined as number | undefined };
@@ -2880,16 +2883,21 @@ export class AuthStorage {
2880
2883
  // path so memory drops immediately.
2881
2884
  const timer = setTimeout(() => timeoutSignal.resolve(null), usageTimeout);
2882
2885
  timer.unref?.();
2883
- const usageResults = await Promise.race([usagePromise, timeoutSignal.promise]).then(result => {
2884
- clearTimeout(timer);
2885
- return (
2886
- result ??
2887
- args.order.map(idx => {
2888
- const selection = args.credentials[idx];
2889
- return selection ? { selection, usage: null, usageChecked: false, blockedUntil: undefined } : null;
2890
- })
2886
+ let resolvedUsageResults: Awaited<typeof usagePromise> | null;
2887
+ try {
2888
+ resolvedUsageResults = await raceUsageWithSignal(
2889
+ Promise.race([usagePromise, timeoutSignal.promise]),
2890
+ args.options?.signal,
2891
2891
  );
2892
- });
2892
+ } finally {
2893
+ clearTimeout(timer);
2894
+ }
2895
+ const usageResults =
2896
+ resolvedUsageResults ??
2897
+ args.order.map(idx => {
2898
+ const selection = args.credentials[idx];
2899
+ return selection ? { selection, usage: null, usageChecked: true, blockedUntil: undefined } : null;
2900
+ });
2893
2901
 
2894
2902
  for (let orderPos = 0; orderPos < usageResults.length; orderPos += 1) {
2895
2903
  const result = usageResults[orderPos];
package/src/models.json CHANGED
@@ -7,12 +7,25 @@
7
7
  "provider": "alibaba-token-plan",
8
8
  "baseUrl": "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
9
9
  "reasoning": true,
10
- "input": ["text"],
11
- "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
10
+ "input": [
11
+ "text"
12
+ ],
13
+ "cost": {
14
+ "input": 0,
15
+ "output": 0,
16
+ "cacheRead": 0,
17
+ "cacheWrite": 0
18
+ },
12
19
  "contextWindow": 1000000,
13
20
  "maxTokens": 384000,
14
- "compat": { "supportsDeveloperRole": false },
15
- "thinking": { "mode": "effort", "minLevel": "minimal", "maxLevel": "xhigh" }
21
+ "compat": {
22
+ "supportsDeveloperRole": false
23
+ },
24
+ "thinking": {
25
+ "mode": "effort",
26
+ "minLevel": "minimal",
27
+ "maxLevel": "xhigh"
28
+ }
16
29
  },
17
30
  "glm-5.2": {
18
31
  "id": "glm-5.2",
@@ -21,12 +34,25 @@
21
34
  "provider": "alibaba-token-plan",
22
35
  "baseUrl": "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
23
36
  "reasoning": true,
24
- "input": ["text"],
25
- "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
37
+ "input": [
38
+ "text"
39
+ ],
40
+ "cost": {
41
+ "input": 0,
42
+ "output": 0,
43
+ "cacheRead": 0,
44
+ "cacheWrite": 0
45
+ },
26
46
  "contextWindow": 1000000,
27
47
  "maxTokens": 131072,
28
- "compat": { "supportsDeveloperRole": false },
29
- "thinking": { "mode": "effort", "minLevel": "minimal", "maxLevel": "xhigh" }
48
+ "compat": {
49
+ "supportsDeveloperRole": false
50
+ },
51
+ "thinking": {
52
+ "mode": "effort",
53
+ "minLevel": "minimal",
54
+ "maxLevel": "xhigh"
55
+ }
30
56
  },
31
57
  "qwen3.8-max-preview": {
32
58
  "id": "qwen3.8-max-preview",
@@ -35,12 +61,25 @@
35
61
  "provider": "alibaba-token-plan",
36
62
  "baseUrl": "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
37
63
  "reasoning": true,
38
- "input": ["text"],
39
- "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
64
+ "input": [
65
+ "text"
66
+ ],
67
+ "cost": {
68
+ "input": 0,
69
+ "output": 0,
70
+ "cacheRead": 0,
71
+ "cacheWrite": 0
72
+ },
40
73
  "contextWindow": 1000000,
41
74
  "maxTokens": 65536,
42
- "compat": { "supportsDeveloperRole": false },
43
- "thinking": { "mode": "effort", "minLevel": "minimal", "maxLevel": "xhigh" }
75
+ "compat": {
76
+ "supportsDeveloperRole": false
77
+ },
78
+ "thinking": {
79
+ "mode": "effort",
80
+ "minLevel": "minimal",
81
+ "maxLevel": "xhigh"
82
+ }
44
83
  }
45
84
  },
46
85
  "amazon-bedrock": {
@@ -3142,6 +3181,198 @@
3142
3181
  "minLevel": "minimal",
3143
3182
  "maxLevel": "high"
3144
3183
  }
3184
+ },
3185
+ "anthropic.claude-opus-5": {
3186
+ "id": "anthropic.claude-opus-5",
3187
+ "name": "Anthropic Opus 5",
3188
+ "api": "bedrock-converse-stream",
3189
+ "provider": "amazon-bedrock",
3190
+ "baseUrl": "https://bedrock-runtime.us-east-1.amazonaws.com",
3191
+ "reasoning": true,
3192
+ "input": [
3193
+ "text",
3194
+ "image"
3195
+ ],
3196
+ "cost": {
3197
+ "input": 5,
3198
+ "output": 25,
3199
+ "cacheRead": 0.5,
3200
+ "cacheWrite": 6.25
3201
+ },
3202
+ "contextWindow": 1000000,
3203
+ "maxTokens": 128000,
3204
+ "thinking": {
3205
+ "mode": "anthropic-adaptive",
3206
+ "minLevel": "minimal",
3207
+ "maxLevel": "max",
3208
+ "levels": [
3209
+ "minimal",
3210
+ "low",
3211
+ "medium",
3212
+ "high",
3213
+ "max"
3214
+ ]
3215
+ }
3216
+ },
3217
+ "au.anthropic.claude-opus-5": {
3218
+ "id": "au.anthropic.claude-opus-5",
3219
+ "name": "Anthropic Opus 5 (AU)",
3220
+ "api": "bedrock-converse-stream",
3221
+ "provider": "amazon-bedrock",
3222
+ "baseUrl": "https://bedrock-runtime.us-east-1.amazonaws.com",
3223
+ "reasoning": true,
3224
+ "input": [
3225
+ "text",
3226
+ "image"
3227
+ ],
3228
+ "cost": {
3229
+ "input": 5,
3230
+ "output": 25,
3231
+ "cacheRead": 0.5,
3232
+ "cacheWrite": 6.25
3233
+ },
3234
+ "contextWindow": 1000000,
3235
+ "maxTokens": 128000,
3236
+ "thinking": {
3237
+ "mode": "anthropic-adaptive",
3238
+ "minLevel": "minimal",
3239
+ "maxLevel": "max",
3240
+ "levels": [
3241
+ "minimal",
3242
+ "low",
3243
+ "medium",
3244
+ "high",
3245
+ "max"
3246
+ ]
3247
+ }
3248
+ },
3249
+ "eu.anthropic.claude-opus-5": {
3250
+ "id": "eu.anthropic.claude-opus-5",
3251
+ "name": "Anthropic Opus 5 (EU)",
3252
+ "api": "bedrock-converse-stream",
3253
+ "provider": "amazon-bedrock",
3254
+ "baseUrl": "https://bedrock-runtime.us-east-1.amazonaws.com",
3255
+ "reasoning": true,
3256
+ "input": [
3257
+ "text",
3258
+ "image"
3259
+ ],
3260
+ "cost": {
3261
+ "input": 5.5,
3262
+ "output": 27.5,
3263
+ "cacheRead": 0.55,
3264
+ "cacheWrite": 6.875
3265
+ },
3266
+ "contextWindow": 1000000,
3267
+ "maxTokens": 128000,
3268
+ "thinking": {
3269
+ "mode": "anthropic-adaptive",
3270
+ "minLevel": "minimal",
3271
+ "maxLevel": "max",
3272
+ "levels": [
3273
+ "minimal",
3274
+ "low",
3275
+ "medium",
3276
+ "high",
3277
+ "max"
3278
+ ]
3279
+ }
3280
+ },
3281
+ "global.anthropic.claude-opus-5": {
3282
+ "id": "global.anthropic.claude-opus-5",
3283
+ "name": "Anthropic Opus 5 (Global)",
3284
+ "api": "bedrock-converse-stream",
3285
+ "provider": "amazon-bedrock",
3286
+ "baseUrl": "https://bedrock-runtime.us-east-1.amazonaws.com",
3287
+ "reasoning": true,
3288
+ "input": [
3289
+ "text",
3290
+ "image"
3291
+ ],
3292
+ "cost": {
3293
+ "input": 5,
3294
+ "output": 25,
3295
+ "cacheRead": 0.5,
3296
+ "cacheWrite": 6.25
3297
+ },
3298
+ "contextWindow": 1000000,
3299
+ "maxTokens": 128000,
3300
+ "thinking": {
3301
+ "mode": "anthropic-adaptive",
3302
+ "minLevel": "minimal",
3303
+ "maxLevel": "max",
3304
+ "levels": [
3305
+ "minimal",
3306
+ "low",
3307
+ "medium",
3308
+ "high",
3309
+ "max"
3310
+ ]
3311
+ }
3312
+ },
3313
+ "jp.anthropic.claude-opus-5": {
3314
+ "id": "jp.anthropic.claude-opus-5",
3315
+ "name": "Anthropic Opus 5 (JP)",
3316
+ "api": "bedrock-converse-stream",
3317
+ "provider": "amazon-bedrock",
3318
+ "baseUrl": "https://bedrock-runtime.us-east-1.amazonaws.com",
3319
+ "reasoning": true,
3320
+ "input": [
3321
+ "text",
3322
+ "image"
3323
+ ],
3324
+ "cost": {
3325
+ "input": 5,
3326
+ "output": 25,
3327
+ "cacheRead": 0.5,
3328
+ "cacheWrite": 6.25
3329
+ },
3330
+ "contextWindow": 1000000,
3331
+ "maxTokens": 128000,
3332
+ "thinking": {
3333
+ "mode": "anthropic-adaptive",
3334
+ "minLevel": "minimal",
3335
+ "maxLevel": "max",
3336
+ "levels": [
3337
+ "minimal",
3338
+ "low",
3339
+ "medium",
3340
+ "high",
3341
+ "max"
3342
+ ]
3343
+ }
3344
+ },
3345
+ "us.anthropic.claude-opus-5": {
3346
+ "id": "us.anthropic.claude-opus-5",
3347
+ "name": "Anthropic Opus 5 (US)",
3348
+ "api": "bedrock-converse-stream",
3349
+ "provider": "amazon-bedrock",
3350
+ "baseUrl": "https://bedrock-runtime.us-east-1.amazonaws.com",
3351
+ "reasoning": true,
3352
+ "input": [
3353
+ "text",
3354
+ "image"
3355
+ ],
3356
+ "cost": {
3357
+ "input": 5,
3358
+ "output": 25,
3359
+ "cacheRead": 0.5,
3360
+ "cacheWrite": 6.25
3361
+ },
3362
+ "contextWindow": 1000000,
3363
+ "maxTokens": 128000,
3364
+ "thinking": {
3365
+ "mode": "anthropic-adaptive",
3366
+ "minLevel": "minimal",
3367
+ "maxLevel": "max",
3368
+ "levels": [
3369
+ "minimal",
3370
+ "low",
3371
+ "medium",
3372
+ "high",
3373
+ "max"
3374
+ ]
3375
+ }
3145
3376
  }
3146
3377
  },
3147
3378
  "anthropic": {
@@ -3661,6 +3892,31 @@
3661
3892
  "minLevel": "minimal",
3662
3893
  "maxLevel": "high"
3663
3894
  }
3895
+ },
3896
+ "claude-opus-5": {
3897
+ "id": "claude-opus-5",
3898
+ "name": "Anthropic Opus 5",
3899
+ "api": "anthropic-messages",
3900
+ "provider": "anthropic",
3901
+ "baseUrl": "https://api.anthropic.com",
3902
+ "reasoning": true,
3903
+ "input": [
3904
+ "text",
3905
+ "image"
3906
+ ],
3907
+ "cost": {
3908
+ "input": 5,
3909
+ "output": 25,
3910
+ "cacheRead": 0.5,
3911
+ "cacheWrite": 6.25
3912
+ },
3913
+ "contextWindow": 1000000,
3914
+ "maxTokens": 128000,
3915
+ "thinking": {
3916
+ "mode": "anthropic-adaptive",
3917
+ "minLevel": "minimal",
3918
+ "maxLevel": "max"
3919
+ }
3664
3920
  }
3665
3921
  },
3666
3922
  "azure-openai": {
@@ -10255,6 +10511,34 @@
10255
10511
  "minLevel": "minimal",
10256
10512
  "maxLevel": "high"
10257
10513
  }
10514
+ },
10515
+ "claude-opus-5": {
10516
+ "id": "claude-opus-5",
10517
+ "name": "Anthropic Opus 5",
10518
+ "api": "anthropic-messages",
10519
+ "provider": "github-copilot",
10520
+ "baseUrl": "https://api.githubcopilot.com",
10521
+ "reasoning": true,
10522
+ "input": [
10523
+ "text",
10524
+ "image"
10525
+ ],
10526
+ "cost": {
10527
+ "input": 5,
10528
+ "output": 25,
10529
+ "cacheRead": 0.5,
10530
+ "cacheWrite": 6.25
10531
+ },
10532
+ "contextWindow": 1000000,
10533
+ "maxTokens": 64000,
10534
+ "headers": {
10535
+ "User-Agent": "opencode/1.3.15"
10536
+ },
10537
+ "thinking": {
10538
+ "mode": "anthropic-adaptive",
10539
+ "minLevel": "minimal",
10540
+ "maxLevel": "max"
10541
+ }
10258
10542
  }
10259
10543
  },
10260
10544
  "gitlab-duo": {
@@ -22786,6 +23070,46 @@
22786
23070
  "minLevel": "minimal",
22787
23071
  "maxLevel": "xhigh"
22788
23072
  }
23073
+ },
23074
+ "anthropic/claude-opus-5": {
23075
+ "id": "anthropic/claude-opus-5",
23076
+ "name": "Anthropic Opus 5",
23077
+ "api": "openai-completions",
23078
+ "provider": "kilo",
23079
+ "baseUrl": "https://api.kilo.ai/api/gateway",
23080
+ "reasoning": false,
23081
+ "input": [
23082
+ "text",
23083
+ "image"
23084
+ ],
23085
+ "cost": {
23086
+ "input": 0,
23087
+ "output": 0,
23088
+ "cacheRead": 0,
23089
+ "cacheWrite": 0
23090
+ },
23091
+ "contextWindow": 222222,
23092
+ "maxTokens": 8888
23093
+ },
23094
+ "anthropic/claude-opus-5-fast": {
23095
+ "id": "anthropic/claude-opus-5-fast",
23096
+ "name": "Anthropic Opus 5 (Fast) ($$$$)",
23097
+ "api": "openai-completions",
23098
+ "provider": "kilo",
23099
+ "baseUrl": "https://api.kilo.ai/api/gateway",
23100
+ "reasoning": false,
23101
+ "input": [
23102
+ "text",
23103
+ "image"
23104
+ ],
23105
+ "cost": {
23106
+ "input": 0,
23107
+ "output": 0,
23108
+ "cacheRead": 0,
23109
+ "cacheWrite": 0
23110
+ },
23111
+ "contextWindow": 222222,
23112
+ "maxTokens": 8888
22789
23113
  }
22790
23114
  },
22791
23115
  "kimi-code": {
@@ -61331,6 +61655,31 @@
61331
61655
  },
61332
61656
  "contextWindow": 131072,
61333
61657
  "maxTokens": 131072
61658
+ },
61659
+ "claude-opus-5": {
61660
+ "id": "claude-opus-5",
61661
+ "name": "Anthropic Opus 5",
61662
+ "api": "anthropic-messages",
61663
+ "provider": "opencode-zen",
61664
+ "baseUrl": "https://opencode.ai/zen",
61665
+ "reasoning": true,
61666
+ "input": [
61667
+ "text",
61668
+ "image"
61669
+ ],
61670
+ "cost": {
61671
+ "input": 5,
61672
+ "output": 25,
61673
+ "cacheRead": 0.5,
61674
+ "cacheWrite": 6.25
61675
+ },
61676
+ "contextWindow": 1000000,
61677
+ "maxTokens": 128000,
61678
+ "thinking": {
61679
+ "mode": "anthropic-adaptive",
61680
+ "minLevel": "minimal",
61681
+ "maxLevel": "max"
61682
+ }
61334
61683
  }
61335
61684
  },
61336
61685
  "opengateway": {
@@ -69992,6 +70341,56 @@
69992
70341
  "minLevel": "minimal",
69993
70342
  "maxLevel": "high"
69994
70343
  }
70344
+ },
70345
+ "anthropic/claude-opus-5": {
70346
+ "id": "anthropic/claude-opus-5",
70347
+ "name": "Anthropic Opus 5",
70348
+ "api": "openai-completions",
70349
+ "provider": "openrouter",
70350
+ "baseUrl": "https://openrouter.ai/api/v1",
70351
+ "reasoning": true,
70352
+ "input": [
70353
+ "text",
70354
+ "image"
70355
+ ],
70356
+ "cost": {
70357
+ "input": 5,
70358
+ "output": 25,
70359
+ "cacheRead": 0.5,
70360
+ "cacheWrite": 6.25
70361
+ },
70362
+ "contextWindow": 1000000,
70363
+ "maxTokens": 128000,
70364
+ "thinking": {
70365
+ "mode": "effort",
70366
+ "minLevel": "minimal",
70367
+ "maxLevel": "high"
70368
+ }
70369
+ },
70370
+ "anthropic/claude-opus-5-fast": {
70371
+ "id": "anthropic/claude-opus-5-fast",
70372
+ "name": "Anthropic Opus 5 (Fast)",
70373
+ "api": "openai-completions",
70374
+ "provider": "openrouter",
70375
+ "baseUrl": "https://openrouter.ai/api/v1",
70376
+ "reasoning": true,
70377
+ "input": [
70378
+ "text",
70379
+ "image"
70380
+ ],
70381
+ "cost": {
70382
+ "input": 10,
70383
+ "output": 50,
70384
+ "cacheRead": 1,
70385
+ "cacheWrite": 12.5
70386
+ },
70387
+ "contextWindow": 1000000,
70388
+ "maxTokens": 128000,
70389
+ "thinking": {
70390
+ "mode": "effort",
70391
+ "minLevel": "minimal",
70392
+ "maxLevel": "high"
70393
+ }
69995
70394
  }
69996
70395
  },
69997
70396
  "qianfan": {
@@ -73609,6 +74008,57 @@
73609
74008
  "compat": {
73610
74009
  "supportsUsageInStreaming": false
73611
74010
  }
74011
+ },
74012
+ "claude-opus-5": {
74013
+ "id": "claude-opus-5",
74014
+ "name": "Anthropic Opus 5",
74015
+ "api": "openai-completions",
74016
+ "provider": "venice",
74017
+ "baseUrl": "https://api.venice.ai/api/v1",
74018
+ "reasoning": true,
74019
+ "input": [
74020
+ "text",
74021
+ "image"
74022
+ ],
74023
+ "cost": {
74024
+ "input": 0,
74025
+ "output": 0,
74026
+ "cacheRead": 0,
74027
+ "cacheWrite": 0
74028
+ },
74029
+ "contextWindow": 1000000,
74030
+ "maxTokens": 128000,
74031
+ "compat": {
74032
+ "supportsUsageInStreaming": false
74033
+ },
74034
+ "thinking": {
74035
+ "mode": "effort",
74036
+ "minLevel": "minimal",
74037
+ "maxLevel": "xhigh"
74038
+ }
74039
+ },
74040
+ "claude-opus-5-fast": {
74041
+ "id": "claude-opus-5-fast",
74042
+ "name": "anthropic-opus-5-fast",
74043
+ "api": "openai-completions",
74044
+ "provider": "venice",
74045
+ "baseUrl": "https://api.venice.ai/api/v1",
74046
+ "reasoning": false,
74047
+ "input": [
74048
+ "text",
74049
+ "image"
74050
+ ],
74051
+ "cost": {
74052
+ "input": 0,
74053
+ "output": 0,
74054
+ "cacheRead": 0,
74055
+ "cacheWrite": 0
74056
+ },
74057
+ "contextWindow": 1000000,
74058
+ "maxTokens": 8888,
74059
+ "compat": {
74060
+ "supportsUsageInStreaming": false
74061
+ }
73612
74062
  }
73613
74063
  },
73614
74064
  "vercel-ai-gateway": {
@@ -78853,6 +79303,56 @@
78853
79303
  "minLevel": "minimal",
78854
79304
  "maxLevel": "xhigh"
78855
79305
  }
79306
+ },
79307
+ "anthropic/claude-opus-5": {
79308
+ "id": "anthropic/claude-opus-5",
79309
+ "name": "Anthropic Opus 5",
79310
+ "api": "anthropic-messages",
79311
+ "provider": "vercel-ai-gateway",
79312
+ "baseUrl": "https://ai-gateway.vercel.sh",
79313
+ "reasoning": true,
79314
+ "input": [
79315
+ "text",
79316
+ "image"
79317
+ ],
79318
+ "cost": {
79319
+ "input": 5,
79320
+ "output": 25,
79321
+ "cacheRead": 0.5,
79322
+ "cacheWrite": 6.25
79323
+ },
79324
+ "contextWindow": 1000000,
79325
+ "maxTokens": 128000,
79326
+ "thinking": {
79327
+ "mode": "anthropic-adaptive",
79328
+ "minLevel": "minimal",
79329
+ "maxLevel": "max"
79330
+ }
79331
+ },
79332
+ "anthropic/claude-opus-5-fast": {
79333
+ "id": "anthropic/claude-opus-5-fast",
79334
+ "name": "Anthropic Opus 5 (Fast)",
79335
+ "api": "anthropic-messages",
79336
+ "provider": "vercel-ai-gateway",
79337
+ "baseUrl": "https://ai-gateway.vercel.sh",
79338
+ "reasoning": true,
79339
+ "input": [
79340
+ "text",
79341
+ "image"
79342
+ ],
79343
+ "cost": {
79344
+ "input": 10,
79345
+ "output": 50,
79346
+ "cacheRead": 1,
79347
+ "cacheWrite": 12.5
79348
+ },
79349
+ "contextWindow": 1000000,
79350
+ "maxTokens": 128000,
79351
+ "thinking": {
79352
+ "mode": "anthropic-adaptive",
79353
+ "minLevel": "minimal",
79354
+ "maxLevel": "max"
79355
+ }
78856
79356
  }
78857
79357
  },
78858
79358
  "xai": {
@@ -84851,4 +85351,4 @@
84851
85351
  }
84852
85352
  }
84853
85353
  }
84854
- }
85354
+ }
@@ -1182,6 +1182,40 @@ function shouldIgnoreAnthropicPreambleEvent(eventType: unknown): boolean {
1182
1182
  return !ANTHROPIC_PRE_MESSAGE_START_EVENT_TYPES.has(eventType);
1183
1183
  }
1184
1184
 
1185
+ function createAnthropicStreamProgressPredicate(): (event: unknown) => boolean {
1186
+ let outputTokens = -1;
1187
+
1188
+ return event => {
1189
+ if (!isRecord(event) || typeof event.type !== "string") return false;
1190
+ if (
1191
+ event.type === "message_start" ||
1192
+ event.type === "content_block_start" ||
1193
+ event.type === "content_block_stop" ||
1194
+ event.type === "message_stop"
1195
+ ) {
1196
+ return true;
1197
+ }
1198
+ if (event.type === "content_block_delta") {
1199
+ if (!isRecord(event.delta)) return false;
1200
+ const delta = event.delta;
1201
+ return (
1202
+ (typeof delta.text === "string" && delta.text.length > 0) ||
1203
+ (typeof delta.thinking === "string" && delta.thinking.length > 0) ||
1204
+ (typeof delta.partial_json === "string" && delta.partial_json.length > 0) ||
1205
+ (typeof delta.signature === "string" && delta.signature.length > 0)
1206
+ );
1207
+ }
1208
+ if (event.type === "message_delta") {
1209
+ if (isRecord(event.delta) && event.delta.stop_reason != null) return true;
1210
+ if (!isRecord(event.usage) || typeof event.usage.output_tokens !== "number") return false;
1211
+ if (event.usage.output_tokens <= outputTokens) return false;
1212
+ outputTokens = event.usage.output_tokens;
1213
+ return true;
1214
+ }
1215
+ return false;
1216
+ };
1217
+ }
1218
+
1185
1219
  function isTransientStreamEnvelopeError(error: unknown): boolean {
1186
1220
  if (!(error instanceof Error)) return false;
1187
1221
  return (
@@ -1457,6 +1491,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
1457
1491
  let sawEvent = false;
1458
1492
  let sawMessageStart = false;
1459
1493
  let sawTerminalEnvelope = false;
1494
+ const isProgressEvent = createAnthropicStreamProgressPredicate();
1460
1495
 
1461
1496
  for await (const event of iterateWithIdleTimeout(anthropicStream, {
1462
1497
  idleTimeoutMs,
@@ -1466,6 +1501,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
1466
1501
  onIdle: () => activeAbortTracker.abortLocally(idleTimeoutAbortError),
1467
1502
  onFirstItemTimeout: () => activeAbortTracker.abortLocally(firstEventTimeoutAbortError),
1468
1503
  abortSignal: options?.signal,
1504
+ isProgressItem: isProgressEvent,
1469
1505
  })) {
1470
1506
  sawEvent = true;
1471
1507
  if (sawProviderSafetyStop) {
@@ -17,6 +17,13 @@ const FIVE_HOURS_MS = 5 * 60 * 60 * 1000;
17
17
  const SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
18
18
  const MAX_ATTEMPTS = 3;
19
19
  const BASE_RETRY_DELAY_MS = 500;
20
+ /**
21
+ * Ceiling for a server-supplied `Retry-After`. Matches `OPENAI_RETRY_DELAY_CAP_MS`
22
+ * and `fetchWithRetry`'s `DEFAULT_MAX_DELAY_MS`. Without it a hostile or
23
+ * misconfigured endpoint stalls the usage fetch for as long as it likes
24
+ * (`Retry-After: 86400` previously produced a 24h sleep).
25
+ */
26
+ const MAX_RETRY_DELAY_MS = 60_000;
20
27
 
21
28
  const CLAUDE_HEADERS = {
22
29
  accept: "application/json, text/plain, */*",
@@ -140,13 +147,23 @@ function isAbortError(error: unknown, signal?: AbortSignal): boolean {
140
147
  return error.name === "AbortError" || error.name === "TimeoutError";
141
148
  }
142
149
 
150
+ /**
151
+ * Honour the server hint but never exceed `MAX_RETRY_DELAY_MS`, and never
152
+ * return a negative/non-finite delay. Keeps the sleep bounded so an abort has
153
+ * an upper bound to fire within.
154
+ */
155
+ function clampRetryDelay(baseline: number, hintMs: number): number {
156
+ const hint = Number.isFinite(hintMs) ? Math.max(0, hintMs) : 0;
157
+ return Math.min(Math.max(baseline, hint), MAX_RETRY_DELAY_MS);
158
+ }
159
+
143
160
  function retryDelayMs(attempt: number, retryAfter: string | null): number {
144
161
  const baseline = BASE_RETRY_DELAY_MS * 2 ** attempt;
145
162
  if (!retryAfter?.trim()) return baseline;
146
163
  const seconds = Number.parseFloat(retryAfter);
147
- if (Number.isFinite(seconds)) return Math.max(baseline, Math.max(0, seconds * 1000));
164
+ if (Number.isFinite(seconds)) return clampRetryDelay(baseline, seconds * 1000);
148
165
  const dateDelay = Date.parse(retryAfter) - Date.now();
149
- return Number.isFinite(dateDelay) ? Math.max(baseline, Math.max(0, dateDelay)) : baseline;
166
+ return Number.isFinite(dateDelay) ? clampRetryDelay(baseline, dateDelay) : baseline;
150
167
  }
151
168
 
152
169
  async function waitBeforeRetry(
@@ -19,7 +19,7 @@ function normalizeIdleTimeoutMs(value: string | undefined, fallback: number): nu
19
19
  /**
20
20
  * Returns the idle timeout used for provider streaming transports.
21
21
  *
22
- * `PI_OPENAI_STREAM_IDLE_TIMEOUT_MS` is accepted as a backward-compatible alias.
22
+ * `GJC_OPENAI_STREAM_IDLE_TIMEOUT_MS` is honored first; `PI_OPENAI_STREAM_IDLE_TIMEOUT_MS` is a backward-compatible alias.
23
23
  * Set `PI_STREAM_IDLE_TIMEOUT_MS=0` to disable the watchdog.
24
24
  *
25
25
  * Providers that legitimately stream much slower than the global default can pass
@@ -27,17 +27,20 @@ function normalizeIdleTimeoutMs(value: string | undefined, fallback: number): nu
27
27
  * Caller options still take precedence; env overrides still trump the fallback.
28
28
  */
29
29
  export function getStreamIdleTimeoutMs(fallbackMs: number = DEFAULT_STREAM_IDLE_TIMEOUT_MS): number | undefined {
30
- return normalizeIdleTimeoutMs($env.PI_STREAM_IDLE_TIMEOUT_MS ?? $env.PI_OPENAI_STREAM_IDLE_TIMEOUT_MS, fallbackMs);
30
+ return normalizeIdleTimeoutMs(
31
+ $env.GJC_OPENAI_STREAM_IDLE_TIMEOUT_MS ?? $env.PI_STREAM_IDLE_TIMEOUT_MS ?? $env.PI_OPENAI_STREAM_IDLE_TIMEOUT_MS,
32
+ fallbackMs,
33
+ );
31
34
  }
32
35
 
33
36
  /**
34
37
  * Returns the idle timeout used for OpenAI-family streaming transports.
35
38
  *
36
- * Set `PI_OPENAI_STREAM_IDLE_TIMEOUT_MS=0` to disable the watchdog.
39
+ * Honors `GJC_OPENAI_STREAM_IDLE_TIMEOUT_MS` first (`PI_OPENAI_STREAM_IDLE_TIMEOUT_MS` is the legacy alias). Set `=0` to disable.
37
40
  */
38
41
  export function getOpenAIStreamIdleTimeoutMs(): number | undefined {
39
42
  return normalizeIdleTimeoutMs(
40
- $env.PI_OPENAI_STREAM_IDLE_TIMEOUT_MS ?? $env.PI_STREAM_IDLE_TIMEOUT_MS,
43
+ $env.GJC_OPENAI_STREAM_IDLE_TIMEOUT_MS ?? $env.PI_OPENAI_STREAM_IDLE_TIMEOUT_MS ?? $env.PI_STREAM_IDLE_TIMEOUT_MS,
41
44
  DEFAULT_STREAM_IDLE_TIMEOUT_MS,
42
45
  );
43
46
  }
@@ -173,8 +176,6 @@ export async function* iterateWithIdleTimeout<T>(
173
176
  }
174
177
  }
175
178
 
176
- const nextResultPromise = withRacy(iterator.next());
177
-
178
179
  const racers: Array<
179
180
  Promise<
180
181
  | { kind: "next"; result: IteratorResult<T> }
@@ -182,7 +183,7 @@ export async function* iterateWithIdleTimeout<T>(
182
183
  | { kind: "timeout" }
183
184
  | { kind: "abort" }
184
185
  >
185
- > = [nextResultPromise];
186
+ > = [];
186
187
 
187
188
  let timer: NodeJS.Timeout | undefined;
188
189
  let resolveTimeout: ((value: { kind: "timeout" }) => void) | undefined;
@@ -207,6 +208,13 @@ export async function* iterateWithIdleTimeout<T>(
207
208
  racers.push(promise);
208
209
  }
209
210
 
211
+ // Arm timeout/abort races before asking the source for its next item. A
212
+ // periodic keepalive iterator commonly registers its own timer inside
213
+ // `next()`; registering that first lets equal-deadline keepalives win every
214
+ // race and extend the idle window forever. Already-buffered items still
215
+ // settle as microtasks before a 0ms watchdog.
216
+ racers.unshift(withRacy(iterator.next()));
217
+
210
218
  try {
211
219
  const outcome = await Promise.race(racers);
212
220
  if (outcome.kind === "abort") {