@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 +8 -0
- package/dist/types/utils/idle-iterator.d.ts +2 -2
- package/package.json +2 -2
- package/src/auth-storage.ts +23 -15
- package/src/models.json +513 -13
- package/src/providers/anthropic.ts +36 -0
- package/src/usage/claude.ts +19 -2
- package/src/utils/idle-iterator.ts +15 -7
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
|
-
* `
|
|
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
|
-
*
|
|
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.
|
|
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.
|
|
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"
|
package/src/auth-storage.ts
CHANGED
|
@@ -2514,9 +2514,12 @@ export class AuthStorage {
|
|
|
2514
2514
|
if (storeHook) {
|
|
2515
2515
|
return storeHook(provider, credential, options?.signal);
|
|
2516
2516
|
}
|
|
2517
|
-
return
|
|
2518
|
-
this.#
|
|
2519
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
args.
|
|
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": [
|
|
11
|
-
|
|
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": {
|
|
15
|
-
|
|
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": [
|
|
25
|
-
|
|
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": {
|
|
29
|
-
|
|
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": [
|
|
39
|
-
|
|
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": {
|
|
43
|
-
|
|
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) {
|
package/src/usage/claude.ts
CHANGED
|
@@ -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
|
|
164
|
+
if (Number.isFinite(seconds)) return clampRetryDelay(baseline, seconds * 1000);
|
|
148
165
|
const dateDelay = Date.parse(retryAfter) - Date.now();
|
|
149
|
-
return Number.isFinite(dateDelay) ?
|
|
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
|
-
* `
|
|
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(
|
|
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
|
-
*
|
|
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
|
-
> = [
|
|
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") {
|