@eddyskywalker/dsh-chatgpt-subscription 0.14.0 → 0.16.0
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 +38 -0
- package/README.md +3 -0
- package/lib/client.js +69 -10
- package/lib/client.js.map +1 -1
- package/lib/index.js +567 -18
- package/lib/types/client/workbuddy/WorkBuddySection.d.ts +36 -0
- package/lib/types/client/workbuddy/WorkBuddySection.d.ts.map +1 -1
- package/lib/types/client/workbuddy/locales.d.ts +18 -0
- package/lib/types/client/workbuddy/locales.d.ts.map +1 -1
- package/lib/types/host/reasoning-collapse-guard/index.d.ts +166 -0
- package/lib/types/host/reasoning-collapse-guard/index.d.ts.map +1 -1
- package/lib/types/host/workbuddy/client.d.ts +9 -1
- package/lib/types/host/workbuddy/client.d.ts.map +1 -1
- package/lib/types/host/workbuddy/model-catalog.d.ts +107 -5
- package/lib/types/host/workbuddy/model-catalog.d.ts.map +1 -1
- package/lib/types/host/workbuddy/routes.d.ts +14 -1
- package/lib/types/host/workbuddy/routes.d.ts.map +1 -1
- package/lib/types/shared/workbuddy-contracts.d.ts +42 -0
- package/lib/types/shared/workbuddy-contracts.d.ts.map +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -30217,6 +30217,25 @@ function refreshSourceForDomain(domain) {
|
|
|
30217
30217
|
}
|
|
30218
30218
|
//#endregion
|
|
30219
30219
|
//#region src/shared/workbuddy-contracts.ts
|
|
30220
|
+
/**
|
|
30221
|
+
* Parse one gateway \`credits\` string into the bare multiplier it names.
|
|
30222
|
+
*
|
|
30223
|
+
* Measured shapes: \`x6.67\` (intl), \`x0.79 credits\` (cn), and \`''\` for a model
|
|
30224
|
+
* whose publisher declares no rate. \`x0.00\` is deliberately kept — it is a real
|
|
30225
|
+
* declaration ("this model does not consume allowance"), not a missing value,
|
|
30226
|
+
* so it must not be confused with the empty string that means nothing is known.
|
|
30227
|
+
*
|
|
30228
|
+
* The return is \`null\` rather than \`0\` for anything unparseable, so a caller can
|
|
30229
|
+
* tell "no rate published" from "a rate of zero".
|
|
30230
|
+
*/
|
|
30231
|
+
function parseWorkBuddyCreditMultiplier(raw) {
|
|
30232
|
+
if (typeof raw !== "string") return null;
|
|
30233
|
+
const stripped = raw.replace(/\s*credits\s*$/i, "").trim().replace(/^[x×]/i, "").trim();
|
|
30234
|
+
if (stripped === "") return null;
|
|
30235
|
+
const value = Number(stripped);
|
|
30236
|
+
if (!Number.isFinite(value) || value < 0) return null;
|
|
30237
|
+
return stripped;
|
|
30238
|
+
}
|
|
30220
30239
|
/** Every level this route can name, in escalating order (the card's sort key). */
|
|
30221
30240
|
const WORKBUDDY_REASONING_EFFORTS = [
|
|
30222
30241
|
"minimal",
|
|
@@ -30276,6 +30295,94 @@ function convergeWorkBuddyEffort(effort, ladder) {
|
|
|
30276
30295
|
//#endregion
|
|
30277
30296
|
//#region src/host/workbuddy/model-catalog.ts
|
|
30278
30297
|
/**
|
|
30298
|
+
* Models the gateway serves but does not publish in `/v3/config`.
|
|
30299
|
+
*
|
|
30300
|
+
* The catalog is a published list, not the served one: measured on the
|
|
30301
|
+
* international backend, `gpt-6-sol`, `gpt-6-luna` and `gemini-3.8-flash` all
|
|
30302
|
+
* answer a normal streaming completion with 200 while being absent from
|
|
30303
|
+
* `/v3/config` entirely, and a reachable gateway *replaces* the shipped table
|
|
30304
|
+
* with that list. Without these rows the picker would simply never show them, so
|
|
30305
|
+
* they are declared here and merged into a live catalog too
|
|
30306
|
+
* ({@link withUnpublishedModels}) rather than living in the fallback alone.
|
|
30307
|
+
*
|
|
30308
|
+
* What each row records, and how it was established, differs by field:
|
|
30309
|
+
*
|
|
30310
|
+
* - **Existence, the reasoning ladder, image support** were measured against the
|
|
30311
|
+
* live endpoint, not inferred from the name. A rejected level answers 400
|
|
30312
|
+
* `code 11133` with `extError` `400002`; `gpt-6-sol` takes exactly the ladder
|
|
30313
|
+
* `gpt-6-astra` publishes (`minimal` refused, `low`/`medium`/`high`/`xhigh`/
|
|
30314
|
+
* `max` accepted) and both accept a 1x1 PNG.
|
|
30315
|
+
* - **Context window, output cap and `canDisableThinking` are inherited from the
|
|
30316
|
+
* published sibling in the same family** — `gpt-6-astra` for `gpt-6-sol` and
|
|
30317
|
+
* `gpt-6-luna`, `gemini-3.5-flash` for `gemini-3.8-flash`. They are marked
|
|
30318
|
+
* here rather than measured because the gateway publishes nothing for these
|
|
30319
|
+
* ids and offers no cheap way to read the real figure: it accepts a
|
|
30320
|
+
* `max_tokens` larger than any published window (even for `gpt-6-astra`, whose
|
|
30321
|
+
* own catalog caps at 128000), so the number is neither enforced nor
|
|
30322
|
+
* discoverable from the request side.
|
|
30323
|
+
*
|
|
30324
|
+
* Every field stays overridable per model from the settings card, and an
|
|
30325
|
+
* inherited window errs low rather than high: over-declaring overflows the
|
|
30326
|
+
* gateway mid-session, while under-declaring only compresses earlier.
|
|
30327
|
+
*/
|
|
30328
|
+
const UNPUBLISHED_MODELS = [
|
|
30329
|
+
{
|
|
30330
|
+
id: "gpt-6-sol",
|
|
30331
|
+
name: "GPT-6-Sol",
|
|
30332
|
+
contextWindow: 4e5,
|
|
30333
|
+
maxContextWindow: 1e6,
|
|
30334
|
+
maxTokens: 128e3,
|
|
30335
|
+
regions: ["intl"],
|
|
30336
|
+
supportsImage: true,
|
|
30337
|
+
reasoningEfforts: [
|
|
30338
|
+
"low",
|
|
30339
|
+
"medium",
|
|
30340
|
+
"high",
|
|
30341
|
+
"xhigh",
|
|
30342
|
+
"max"
|
|
30343
|
+
],
|
|
30344
|
+
defaultReasoningEffort: "high",
|
|
30345
|
+
canDisableThinking: true,
|
|
30346
|
+
description: "OpenAI 旗舰模型,擅长复杂推理与长程任务"
|
|
30347
|
+
},
|
|
30348
|
+
{
|
|
30349
|
+
id: "gpt-6-luna",
|
|
30350
|
+
name: "GPT-6-Luna",
|
|
30351
|
+
contextWindow: 4e5,
|
|
30352
|
+
maxContextWindow: 1e6,
|
|
30353
|
+
maxTokens: 128e3,
|
|
30354
|
+
regions: ["intl"],
|
|
30355
|
+
supportsImage: true,
|
|
30356
|
+
reasoningEfforts: [
|
|
30357
|
+
"low",
|
|
30358
|
+
"medium",
|
|
30359
|
+
"high",
|
|
30360
|
+
"xhigh",
|
|
30361
|
+
"max"
|
|
30362
|
+
],
|
|
30363
|
+
defaultReasoningEffort: "high",
|
|
30364
|
+
canDisableThinking: true,
|
|
30365
|
+
description: "OpenAI 轻量模型,响应快速,适合日常任务"
|
|
30366
|
+
},
|
|
30367
|
+
{
|
|
30368
|
+
id: "gemini-3.8-flash",
|
|
30369
|
+
name: "Gemini-3.8-Flash",
|
|
30370
|
+
contextWindow: 1e6,
|
|
30371
|
+
maxContextWindow: 1e6,
|
|
30372
|
+
maxTokens: 65536,
|
|
30373
|
+
regions: ["intl"],
|
|
30374
|
+
supportsImage: true,
|
|
30375
|
+
reasoningEfforts: [
|
|
30376
|
+
"low",
|
|
30377
|
+
"high",
|
|
30378
|
+
"max"
|
|
30379
|
+
],
|
|
30380
|
+
defaultReasoningEffort: "medium",
|
|
30381
|
+
canDisableThinking: false,
|
|
30382
|
+
description: "能力均衡,适合日常使用"
|
|
30383
|
+
}
|
|
30384
|
+
];
|
|
30385
|
+
/**
|
|
30279
30386
|
* Offline fallback catalog, transcribed from a live `/v3/config` read.
|
|
30280
30387
|
*
|
|
30281
30388
|
* Used when the gateway cannot be reached (first run before sign-in, or a
|
|
@@ -30302,6 +30409,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30302
30409
|
maxContextWindow: 56e3,
|
|
30303
30410
|
maxTokens: 24e3,
|
|
30304
30411
|
regions: ["cn"],
|
|
30412
|
+
credits: { cn: "2.00" },
|
|
30305
30413
|
supportsImage: false,
|
|
30306
30414
|
reasoningEfforts: [],
|
|
30307
30415
|
defaultReasoningEffort: null,
|
|
@@ -30315,6 +30423,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30315
30423
|
maxContextWindow: 2e5,
|
|
30316
30424
|
maxTokens: 32e3,
|
|
30317
30425
|
regions: ["intl"],
|
|
30426
|
+
credits: { intl: "0.34" },
|
|
30318
30427
|
supportsImage: true,
|
|
30319
30428
|
reasoningEfforts: [
|
|
30320
30429
|
"low",
|
|
@@ -30332,6 +30441,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30332
30441
|
maxContextWindow: 256e3,
|
|
30333
30442
|
maxTokens: 32e3,
|
|
30334
30443
|
regions: ["intl"],
|
|
30444
|
+
credits: { intl: "0.59" },
|
|
30335
30445
|
supportsImage: true,
|
|
30336
30446
|
reasoningEfforts: [
|
|
30337
30447
|
"low",
|
|
@@ -30349,6 +30459,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30349
30459
|
maxContextWindow: 272e3,
|
|
30350
30460
|
maxTokens: 72e3,
|
|
30351
30461
|
regions: ["intl"],
|
|
30462
|
+
credits: { intl: "3.31" },
|
|
30352
30463
|
supportsImage: true,
|
|
30353
30464
|
reasoningEfforts: [
|
|
30354
30465
|
"low",
|
|
@@ -30366,12 +30477,14 @@ const FALLBACK_MODELS$1 = [
|
|
|
30366
30477
|
maxContextWindow: 2e5,
|
|
30367
30478
|
maxTokens: 24e3,
|
|
30368
30479
|
regions: ["intl"],
|
|
30480
|
+
credits: { intl: "3.33" },
|
|
30369
30481
|
supportsImage: true,
|
|
30370
30482
|
reasoningEfforts: [],
|
|
30371
30483
|
defaultReasoningEffort: null,
|
|
30372
30484
|
canDisableThinking: false,
|
|
30373
30485
|
description: "深度推理,适合深度分析与难题"
|
|
30374
30486
|
},
|
|
30487
|
+
...UNPUBLISHED_MODELS.filter((model) => model.id === "gpt-6-sol" || model.id === "gpt-6-luna"),
|
|
30375
30488
|
{
|
|
30376
30489
|
id: "gpt-6-astra",
|
|
30377
30490
|
name: "GPT-6-Astra",
|
|
@@ -30379,6 +30492,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30379
30492
|
maxContextWindow: 1e6,
|
|
30380
30493
|
maxTokens: 128e3,
|
|
30381
30494
|
regions: ["intl"],
|
|
30495
|
+
credits: { intl: "6.67" },
|
|
30382
30496
|
supportsImage: true,
|
|
30383
30497
|
reasoningEfforts: [
|
|
30384
30498
|
"low",
|
|
@@ -30398,6 +30512,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30398
30512
|
maxContextWindow: 1e6,
|
|
30399
30513
|
maxTokens: 128e3,
|
|
30400
30514
|
regions: ["intl"],
|
|
30515
|
+
credits: { intl: "3.47" },
|
|
30401
30516
|
supportsImage: true,
|
|
30402
30517
|
reasoningEfforts: [
|
|
30403
30518
|
"low",
|
|
@@ -30417,6 +30532,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30417
30532
|
maxContextWindow: 1e6,
|
|
30418
30533
|
maxTokens: 128e3,
|
|
30419
30534
|
regions: ["intl"],
|
|
30535
|
+
credits: { intl: "1.39" },
|
|
30420
30536
|
supportsImage: true,
|
|
30421
30537
|
reasoningEfforts: [
|
|
30422
30538
|
"low",
|
|
@@ -30436,6 +30552,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30436
30552
|
maxContextWindow: 1e6,
|
|
30437
30553
|
maxTokens: 128e3,
|
|
30438
30554
|
regions: ["intl"],
|
|
30555
|
+
credits: { intl: "0.14" },
|
|
30439
30556
|
supportsImage: true,
|
|
30440
30557
|
reasoningEfforts: [
|
|
30441
30558
|
"low",
|
|
@@ -30455,6 +30572,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30455
30572
|
maxContextWindow: 1e6,
|
|
30456
30573
|
maxTokens: 128e3,
|
|
30457
30574
|
regions: ["intl"],
|
|
30575
|
+
credits: { intl: "3.31" },
|
|
30458
30576
|
supportsImage: true,
|
|
30459
30577
|
reasoningEfforts: [
|
|
30460
30578
|
"low",
|
|
@@ -30473,6 +30591,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30473
30591
|
maxContextWindow: 272e3,
|
|
30474
30592
|
maxTokens: 72e3,
|
|
30475
30593
|
regions: ["intl"],
|
|
30594
|
+
credits: { intl: "1.65" },
|
|
30476
30595
|
supportsImage: true,
|
|
30477
30596
|
reasoningEfforts: [
|
|
30478
30597
|
"low",
|
|
@@ -30489,8 +30608,12 @@ const FALLBACK_MODELS$1 = [
|
|
|
30489
30608
|
name: "GLM-5.3",
|
|
30490
30609
|
contextWindow: 1e6,
|
|
30491
30610
|
maxContextWindow: 1e6,
|
|
30492
|
-
maxTokens:
|
|
30611
|
+
maxTokens: 48e3,
|
|
30493
30612
|
regions: ["intl", "cn"],
|
|
30613
|
+
credits: {
|
|
30614
|
+
cn: "0.79",
|
|
30615
|
+
intl: "0.79"
|
|
30616
|
+
},
|
|
30494
30617
|
supportsImage: true,
|
|
30495
30618
|
reasoningEfforts: [
|
|
30496
30619
|
"low",
|
|
@@ -30504,10 +30627,14 @@ const FALLBACK_MODELS$1 = [
|
|
|
30504
30627
|
{
|
|
30505
30628
|
id: "glm-5.3-flash",
|
|
30506
30629
|
name: "GLM-5.3-Flash",
|
|
30507
|
-
contextWindow:
|
|
30630
|
+
contextWindow: 3e5,
|
|
30508
30631
|
maxContextWindow: 1e6,
|
|
30509
30632
|
maxTokens: 32e3,
|
|
30510
|
-
regions: ["cn"],
|
|
30633
|
+
regions: ["intl", "cn"],
|
|
30634
|
+
credits: {
|
|
30635
|
+
cn: "0.06",
|
|
30636
|
+
intl: "0.06"
|
|
30637
|
+
},
|
|
30511
30638
|
supportsImage: true,
|
|
30512
30639
|
reasoningEfforts: [
|
|
30513
30640
|
"low",
|
|
@@ -30523,8 +30650,12 @@ const FALLBACK_MODELS$1 = [
|
|
|
30523
30650
|
name: "GLM-5.2",
|
|
30524
30651
|
contextWindow: 1e6,
|
|
30525
30652
|
maxContextWindow: 1e6,
|
|
30526
|
-
maxTokens:
|
|
30653
|
+
maxTokens: 48e3,
|
|
30527
30654
|
regions: ["intl", "cn"],
|
|
30655
|
+
credits: {
|
|
30656
|
+
cn: "0.79",
|
|
30657
|
+
intl: "0.79"
|
|
30658
|
+
},
|
|
30528
30659
|
supportsImage: true,
|
|
30529
30660
|
reasoningEfforts: ["high", "xhigh"],
|
|
30530
30661
|
defaultReasoningEffort: "high",
|
|
@@ -30538,6 +30669,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30538
30669
|
maxContextWindow: 2e5,
|
|
30539
30670
|
maxTokens: 48e3,
|
|
30540
30671
|
regions: ["cn"],
|
|
30672
|
+
credits: { cn: "0.79" },
|
|
30541
30673
|
supportsImage: true,
|
|
30542
30674
|
reasoningEfforts: [
|
|
30543
30675
|
"low",
|
|
@@ -30555,6 +30687,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30555
30687
|
maxContextWindow: 2e5,
|
|
30556
30688
|
maxTokens: 64e3,
|
|
30557
30689
|
regions: ["cn"],
|
|
30690
|
+
credits: { cn: "0.71" },
|
|
30558
30691
|
supportsImage: true,
|
|
30559
30692
|
reasoningEfforts: [
|
|
30560
30693
|
"low",
|
|
@@ -30572,6 +30705,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30572
30705
|
maxContextWindow: 2e5,
|
|
30573
30706
|
maxTokens: 48e3,
|
|
30574
30707
|
regions: ["cn"],
|
|
30708
|
+
credits: { cn: "0.80" },
|
|
30575
30709
|
supportsImage: false,
|
|
30576
30710
|
reasoningEfforts: [],
|
|
30577
30711
|
defaultReasoningEffort: null,
|
|
@@ -30585,6 +30719,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30585
30719
|
maxContextWindow: 2e5,
|
|
30586
30720
|
maxTokens: 48e3,
|
|
30587
30721
|
regions: ["cn"],
|
|
30722
|
+
credits: { cn: "0.95" },
|
|
30588
30723
|
supportsImage: false,
|
|
30589
30724
|
reasoningEfforts: [
|
|
30590
30725
|
"low",
|
|
@@ -30602,6 +30737,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30602
30737
|
maxContextWindow: 2e5,
|
|
30603
30738
|
maxTokens: 48e3,
|
|
30604
30739
|
regions: ["cn"],
|
|
30740
|
+
credits: { cn: "0.23" },
|
|
30605
30741
|
supportsImage: false,
|
|
30606
30742
|
reasoningEfforts: [],
|
|
30607
30743
|
defaultReasoningEffort: null,
|
|
@@ -30615,6 +30751,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30615
30751
|
maxContextWindow: 128e3,
|
|
30616
30752
|
maxTokens: 32e3,
|
|
30617
30753
|
regions: ["cn"],
|
|
30754
|
+
credits: { cn: "0.11" },
|
|
30618
30755
|
supportsImage: true,
|
|
30619
30756
|
reasoningEfforts: [
|
|
30620
30757
|
"low",
|
|
@@ -30632,6 +30769,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30632
30769
|
maxContextWindow: 168e3,
|
|
30633
30770
|
maxTokens: 32e3,
|
|
30634
30771
|
regions: ["cn"],
|
|
30772
|
+
credits: { cn: "0.23" },
|
|
30635
30773
|
supportsImage: false,
|
|
30636
30774
|
reasoningEfforts: [],
|
|
30637
30775
|
defaultReasoningEffort: null,
|
|
@@ -30645,6 +30783,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30645
30783
|
maxContextWindow: 1e6,
|
|
30646
30784
|
maxTokens: 32e3,
|
|
30647
30785
|
regions: ["intl"],
|
|
30786
|
+
credits: { intl: "1.62" },
|
|
30648
30787
|
supportsImage: true,
|
|
30649
30788
|
reasoningEfforts: [
|
|
30650
30789
|
"low",
|
|
@@ -30662,6 +30801,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30662
30801
|
maxContextWindow: 1e6,
|
|
30663
30802
|
maxTokens: 32e3,
|
|
30664
30803
|
regions: ["cn"],
|
|
30804
|
+
credits: { cn: "1.62" },
|
|
30665
30805
|
supportsImage: true,
|
|
30666
30806
|
reasoningEfforts: [
|
|
30667
30807
|
"low",
|
|
@@ -30677,8 +30817,12 @@ const FALLBACK_MODELS$1 = [
|
|
|
30677
30817
|
name: "Kimi-K2.8-Preview",
|
|
30678
30818
|
contextWindow: 3e5,
|
|
30679
30819
|
maxContextWindow: 1e6,
|
|
30680
|
-
maxTokens:
|
|
30820
|
+
maxTokens: 32e3,
|
|
30681
30821
|
regions: ["intl", "cn"],
|
|
30822
|
+
credits: {
|
|
30823
|
+
cn: "0.77",
|
|
30824
|
+
intl: "0.77"
|
|
30825
|
+
},
|
|
30682
30826
|
supportsImage: true,
|
|
30683
30827
|
reasoningEfforts: [
|
|
30684
30828
|
"low",
|
|
@@ -30696,6 +30840,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30696
30840
|
maxContextWindow: 256e3,
|
|
30697
30841
|
maxTokens: 32e3,
|
|
30698
30842
|
regions: ["cn"],
|
|
30843
|
+
credits: { cn: "0.57" },
|
|
30699
30844
|
supportsImage: true,
|
|
30700
30845
|
reasoningEfforts: [
|
|
30701
30846
|
"low",
|
|
@@ -30713,6 +30858,10 @@ const FALLBACK_MODELS$1 = [
|
|
|
30713
30858
|
maxContextWindow: 256e3,
|
|
30714
30859
|
maxTokens: 32e3,
|
|
30715
30860
|
regions: ["intl", "cn"],
|
|
30861
|
+
credits: {
|
|
30862
|
+
cn: "0.52",
|
|
30863
|
+
intl: "0.52"
|
|
30864
|
+
},
|
|
30716
30865
|
supportsImage: true,
|
|
30717
30866
|
reasoningEfforts: [
|
|
30718
30867
|
"low",
|
|
@@ -30730,6 +30879,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30730
30879
|
maxContextWindow: 164e3,
|
|
30731
30880
|
maxTokens: 32e3,
|
|
30732
30881
|
regions: ["cn"],
|
|
30882
|
+
credits: { cn: "0.45" },
|
|
30733
30883
|
supportsImage: true,
|
|
30734
30884
|
reasoningEfforts: [
|
|
30735
30885
|
"low",
|
|
@@ -30747,6 +30897,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30747
30897
|
maxContextWindow: 164e3,
|
|
30748
30898
|
maxTokens: 32e3,
|
|
30749
30899
|
regions: ["cn"],
|
|
30900
|
+
credits: { cn: "0.54" },
|
|
30750
30901
|
supportsImage: false,
|
|
30751
30902
|
reasoningEfforts: [
|
|
30752
30903
|
"low",
|
|
@@ -30764,6 +30915,10 @@ const FALLBACK_MODELS$1 = [
|
|
|
30764
30915
|
maxContextWindow: 1e6,
|
|
30765
30916
|
maxTokens: 128e3,
|
|
30766
30917
|
regions: ["intl", "cn"],
|
|
30918
|
+
credits: {
|
|
30919
|
+
cn: "0.11",
|
|
30920
|
+
intl: "0.00"
|
|
30921
|
+
},
|
|
30767
30922
|
supportsImage: true,
|
|
30768
30923
|
reasoningEfforts: [
|
|
30769
30924
|
"low",
|
|
@@ -30781,6 +30936,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30781
30936
|
maxContextWindow: 1e6,
|
|
30782
30937
|
maxTokens: 128e3,
|
|
30783
30938
|
regions: ["intl"],
|
|
30939
|
+
credits: { intl: "0.03" },
|
|
30784
30940
|
supportsImage: true,
|
|
30785
30941
|
reasoningEfforts: [
|
|
30786
30942
|
"low",
|
|
@@ -30798,6 +30954,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30798
30954
|
maxContextWindow: 1e6,
|
|
30799
30955
|
maxTokens: 128e3,
|
|
30800
30956
|
regions: ["cn"],
|
|
30957
|
+
credits: { cn: "0.51" },
|
|
30801
30958
|
supportsImage: true,
|
|
30802
30959
|
reasoningEfforts: [
|
|
30803
30960
|
"low",
|
|
@@ -30815,6 +30972,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30815
30972
|
maxContextWindow: 1e6,
|
|
30816
30973
|
maxTokens: 5e4,
|
|
30817
30974
|
regions: ["cn"],
|
|
30975
|
+
credits: { cn: "0.17" },
|
|
30818
30976
|
supportsImage: true,
|
|
30819
30977
|
reasoningEfforts: [
|
|
30820
30978
|
"low",
|
|
@@ -30832,6 +30990,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30832
30990
|
maxContextWindow: 96e3,
|
|
30833
30991
|
maxTokens: 32e3,
|
|
30834
30992
|
regions: ["cn"],
|
|
30993
|
+
credits: { cn: "0.29" },
|
|
30835
30994
|
supportsImage: false,
|
|
30836
30995
|
reasoningEfforts: [
|
|
30837
30996
|
"low",
|
|
@@ -30849,6 +31008,10 @@ const FALLBACK_MODELS$1 = [
|
|
|
30849
31008
|
maxContextWindow: 1e6,
|
|
30850
31009
|
maxTokens: 64e3,
|
|
30851
31010
|
regions: ["intl", "cn"],
|
|
31011
|
+
credits: {
|
|
31012
|
+
cn: "0.29",
|
|
31013
|
+
intl: "0.29"
|
|
31014
|
+
},
|
|
30852
31015
|
supportsImage: true,
|
|
30853
31016
|
reasoningEfforts: ["high"],
|
|
30854
31017
|
defaultReasoningEffort: "high",
|
|
@@ -30856,12 +31019,13 @@ const FALLBACK_MODELS$1 = [
|
|
|
30856
31019
|
description: "混元思考模型,具有增强的推理能力"
|
|
30857
31020
|
},
|
|
30858
31021
|
{
|
|
30859
|
-
id: "hy4-preview-
|
|
31022
|
+
id: "hy4-preview-f",
|
|
30860
31023
|
name: "Hy4 preview",
|
|
30861
31024
|
contextWindow: 1e6,
|
|
30862
31025
|
maxContextWindow: 1e6,
|
|
30863
31026
|
maxTokens: 64e3,
|
|
30864
31027
|
regions: ["cn"],
|
|
31028
|
+
credits: { cn: "0.00" },
|
|
30865
31029
|
supportsImage: true,
|
|
30866
31030
|
reasoningEfforts: ["high"],
|
|
30867
31031
|
defaultReasoningEffort: "high",
|
|
@@ -30875,6 +31039,10 @@ const FALLBACK_MODELS$1 = [
|
|
|
30875
31039
|
maxContextWindow: 192e3,
|
|
30876
31040
|
maxTokens: 64e3,
|
|
30877
31041
|
regions: ["intl", "cn"],
|
|
31042
|
+
credits: {
|
|
31043
|
+
cn: "0.00",
|
|
31044
|
+
intl: "0.00"
|
|
31045
|
+
},
|
|
30878
31046
|
supportsImage: true,
|
|
30879
31047
|
reasoningEfforts: ["low", "high"],
|
|
30880
31048
|
defaultReasoningEffort: "high",
|
|
@@ -30888,6 +31056,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30888
31056
|
maxContextWindow: 192e3,
|
|
30889
31057
|
maxTokens: 64e3,
|
|
30890
31058
|
regions: ["cn"],
|
|
31059
|
+
credits: { cn: "0.05" },
|
|
30891
31060
|
supportsImage: true,
|
|
30892
31061
|
reasoningEfforts: [
|
|
30893
31062
|
"low",
|
|
@@ -30905,12 +31074,33 @@ const FALLBACK_MODELS$1 = [
|
|
|
30905
31074
|
maxContextWindow: 2e5,
|
|
30906
31075
|
maxTokens: 8192,
|
|
30907
31076
|
regions: ["cn"],
|
|
31077
|
+
credits: { cn: "0.10" },
|
|
30908
31078
|
supportsImage: false,
|
|
30909
31079
|
reasoningEfforts: [],
|
|
30910
31080
|
defaultReasoningEffort: null,
|
|
30911
31081
|
canDisableThinking: false,
|
|
30912
31082
|
description: ""
|
|
30913
31083
|
},
|
|
31084
|
+
{
|
|
31085
|
+
id: "space-bunny",
|
|
31086
|
+
name: "Space-Bunny",
|
|
31087
|
+
contextWindow: 1e6,
|
|
31088
|
+
maxContextWindow: 1e6,
|
|
31089
|
+
maxTokens: 128e3,
|
|
31090
|
+
regions: ["cn"],
|
|
31091
|
+
credits: { cn: "0.08" },
|
|
31092
|
+
supportsImage: true,
|
|
31093
|
+
reasoningEfforts: [
|
|
31094
|
+
"low",
|
|
31095
|
+
"medium",
|
|
31096
|
+
"high",
|
|
31097
|
+
"xhigh",
|
|
31098
|
+
"max"
|
|
31099
|
+
],
|
|
31100
|
+
defaultReasoningEffort: "max",
|
|
31101
|
+
canDisableThinking: false,
|
|
31102
|
+
description: "推理速度极快,编码能力强劲,并支持原生多模态输入的匿名大模型"
|
|
31103
|
+
},
|
|
30914
31104
|
{
|
|
30915
31105
|
id: "minimax-m3",
|
|
30916
31106
|
name: "MiniMax-M3",
|
|
@@ -30918,6 +31108,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30918
31108
|
maxContextWindow: 512e3,
|
|
30919
31109
|
maxTokens: 64e3,
|
|
30920
31110
|
regions: ["cn"],
|
|
31111
|
+
credits: { cn: "0.25" },
|
|
30921
31112
|
supportsImage: true,
|
|
30922
31113
|
reasoningEfforts: [
|
|
30923
31114
|
"low",
|
|
@@ -30935,6 +31126,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30935
31126
|
maxContextWindow: 2e5,
|
|
30936
31127
|
maxTokens: 48e3,
|
|
30937
31128
|
regions: ["cn"],
|
|
31129
|
+
credits: { cn: "0.19" },
|
|
30938
31130
|
supportsImage: true,
|
|
30939
31131
|
reasoningEfforts: [
|
|
30940
31132
|
"low",
|
|
@@ -30952,6 +31144,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30952
31144
|
maxContextWindow: 2e5,
|
|
30953
31145
|
maxTokens: 48e3,
|
|
30954
31146
|
regions: ["cn"],
|
|
31147
|
+
credits: { cn: "0.18" },
|
|
30955
31148
|
supportsImage: false,
|
|
30956
31149
|
reasoningEfforts: [
|
|
30957
31150
|
"low",
|
|
@@ -30962,6 +31155,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30962
31155
|
canDisableThinking: false,
|
|
30963
31156
|
description: ""
|
|
30964
31157
|
},
|
|
31158
|
+
...UNPUBLISHED_MODELS.filter((model) => model.id === "gemini-3.8-flash"),
|
|
30965
31159
|
{
|
|
30966
31160
|
id: "gemini-3.5-flash",
|
|
30967
31161
|
name: "Gemini-3.5-Flash",
|
|
@@ -30969,6 +31163,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
30969
31163
|
maxContextWindow: 1e6,
|
|
30970
31164
|
maxTokens: 65536,
|
|
30971
31165
|
regions: ["intl"],
|
|
31166
|
+
credits: { intl: "0.99" },
|
|
30972
31167
|
supportsImage: true,
|
|
30973
31168
|
reasoningEfforts: [
|
|
30974
31169
|
"low",
|
|
@@ -30997,6 +31192,21 @@ const DEFAULT_VISIBLE_MODEL_IDS$1 = [
|
|
|
30997
31192
|
/** Every id the fallback knows, in declaration order. */
|
|
30998
31193
|
const WORKBUDDY_MODEL_IDS = FALLBACK_MODELS$1.map((model) => model.id);
|
|
30999
31194
|
/**
|
|
31195
|
+
* Add the served-but-unpublished models to a live `/v3/config` catalog.
|
|
31196
|
+
*
|
|
31197
|
+
* A reachable gateway replaces the shipped table with the list it publishes, so
|
|
31198
|
+
* a model it serves without listing ({@link UNPUBLISHED_MODELS}) would drop out
|
|
31199
|
+
* of the picker the moment the catalog loads. Merging here keeps it offered.
|
|
31200
|
+
*
|
|
31201
|
+
* A published entry always wins: the gateway is the authority on anything it
|
|
31202
|
+
* does describe, so these rows only fill ids the payload is silent about.
|
|
31203
|
+
*/
|
|
31204
|
+
function withUnpublishedModels(catalog, region) {
|
|
31205
|
+
const known = new Set(catalog.map((model) => model.id));
|
|
31206
|
+
const extra = UNPUBLISHED_MODELS.filter((model) => model.regions.includes(region) && !known.has(model.id));
|
|
31207
|
+
return extra.length > 0 ? [...catalog, ...extra] : [...catalog];
|
|
31208
|
+
}
|
|
31209
|
+
/**
|
|
31000
31210
|
* Resolve one entry from a catalog.
|
|
31001
31211
|
*
|
|
31002
31212
|
* An unknown id resolves to a synthetic text-only entry rather than the first
|
|
@@ -31021,6 +31231,7 @@ function resolveWorkBuddyModel(id, catalog = FALLBACK_MODELS$1) {
|
|
|
31021
31231
|
maxContextWindow: DEFAULT_CONTEXT_WINDOW$1,
|
|
31022
31232
|
maxTokens: FALLBACK_MAX_TOKENS,
|
|
31023
31233
|
regions: ["cn", "intl"],
|
|
31234
|
+
credits: {},
|
|
31024
31235
|
supportsImage: false,
|
|
31025
31236
|
reasoningEfforts: [],
|
|
31026
31237
|
defaultReasoningEffort: null,
|
|
@@ -31028,9 +31239,131 @@ function resolveWorkBuddyModel(id, catalog = FALLBACK_MODELS$1) {
|
|
|
31028
31239
|
description: ""
|
|
31029
31240
|
};
|
|
31030
31241
|
}
|
|
31031
|
-
/**
|
|
31242
|
+
/**
|
|
31243
|
+
* Vendor families, resolved from a model id.
|
|
31244
|
+
*
|
|
31245
|
+
* The gateway orders `/v3/config` the way it feels like, which interleaves
|
|
31246
|
+
* vendors: a DeepSeek row, then a GPT row, then two Hunyuan rows, then a Kimi
|
|
31247
|
+
* row, then three more GPT rows. In a picker that reads as noise — the eye
|
|
31248
|
+
* cannot tell that the GPT entries belong together — so the list is grouped by
|
|
31249
|
+
* family before it is shown.
|
|
31250
|
+
*
|
|
31251
|
+
* The rules are ordered, first match wins. Only families this subscription
|
|
31252
|
+
* actually serves are listed: a rule for a model that cannot appear would be a
|
|
31253
|
+
* guess about a future catalog.
|
|
31254
|
+
*
|
|
31255
|
+
* An id no rule claims is its own family rather than a shared `other` bucket, so
|
|
31256
|
+
* an unrecognised model keeps its place in the order instead of being swept into
|
|
31257
|
+
* a pile of models it has nothing to do with.
|
|
31258
|
+
*/
|
|
31259
|
+
const WORKBUDDY_VENDOR_RULES = [
|
|
31260
|
+
{
|
|
31261
|
+
re: /^(default|fast|balanced|primary|deep)(-model)?$/,
|
|
31262
|
+
family: "alias"
|
|
31263
|
+
},
|
|
31264
|
+
{
|
|
31265
|
+
re: /^gpt-/,
|
|
31266
|
+
family: "openai"
|
|
31267
|
+
},
|
|
31268
|
+
{
|
|
31269
|
+
re: /^gemini-/,
|
|
31270
|
+
family: "google"
|
|
31271
|
+
},
|
|
31272
|
+
{
|
|
31273
|
+
re: /^glm-/,
|
|
31274
|
+
family: "zhipu"
|
|
31275
|
+
},
|
|
31276
|
+
{
|
|
31277
|
+
re: /^kimi-/,
|
|
31278
|
+
family: "moonshot"
|
|
31279
|
+
},
|
|
31280
|
+
{
|
|
31281
|
+
re: /^deepseek-/,
|
|
31282
|
+
family: "deepseek"
|
|
31283
|
+
},
|
|
31284
|
+
{
|
|
31285
|
+
re: /^hy[0-9]/,
|
|
31286
|
+
family: "tencent"
|
|
31287
|
+
},
|
|
31288
|
+
{
|
|
31289
|
+
re: /^hunyuan/,
|
|
31290
|
+
family: "tencent"
|
|
31291
|
+
},
|
|
31292
|
+
{
|
|
31293
|
+
re: /^minimax-/,
|
|
31294
|
+
family: "minimax"
|
|
31295
|
+
},
|
|
31296
|
+
{
|
|
31297
|
+
re: /^space-bunny$/,
|
|
31298
|
+
family: "space-bunny"
|
|
31299
|
+
}
|
|
31300
|
+
];
|
|
31301
|
+
/** The vendor family one model belongs to. */
|
|
31302
|
+
function workBuddyModelVendor(id) {
|
|
31303
|
+
const value = id.toLowerCase();
|
|
31304
|
+
for (const rule of WORKBUDDY_VENDOR_RULES) if (rule.re.test(value)) return rule.family;
|
|
31305
|
+
return id;
|
|
31306
|
+
}
|
|
31307
|
+
/**
|
|
31308
|
+
* Numeric segments of a model id, e.g. `gpt-6-astra` → [6].
|
|
31309
|
+
*
|
|
31310
|
+
* Version numbers are compared as numbers, so GPT-6.0 sorts below GPT-6.6 rather
|
|
31311
|
+
* than above it as it would if the strings were compared.
|
|
31312
|
+
*/
|
|
31313
|
+
function versionSegments$1(id) {
|
|
31314
|
+
return [...id.toLowerCase().matchAll(/\d+/g)].map((match) => Number(match[0]));
|
|
31315
|
+
}
|
|
31316
|
+
/**
|
|
31317
|
+
* Order two models of one family, newest generation first.
|
|
31318
|
+
*
|
|
31319
|
+
* Catalog order alone is not good enough. The models served but not published
|
|
31320
|
+
* are appended after the published list, which left `gpt-6-sol` below
|
|
31321
|
+
* `gpt-5.4` — the newest models at the bottom of their own group. Comparing the
|
|
31322
|
+
* numeric segments puts every GPT-6 row together and ahead of every GPT-5 one,
|
|
31323
|
+
* and leaves an id whose version cannot be read (an alias, a nickname) in the
|
|
31324
|
+
* catalog's own order rather than guessing where it belongs.
|
|
31325
|
+
*
|
|
31326
|
+
* Ties keep catalog order, so `deepseek-v4.1-flash` still leads its
|
|
31327
|
+
* `-flash-sg` sibling.
|
|
31328
|
+
*/
|
|
31329
|
+
function compareWithinFamily(a, b) {
|
|
31330
|
+
const left = versionSegments$1(a.id);
|
|
31331
|
+
const right = versionSegments$1(b.id);
|
|
31332
|
+
for (let index = 0; index < Math.max(left.length, right.length); index += 1) {
|
|
31333
|
+
const x = left[index];
|
|
31334
|
+
const y = right[index];
|
|
31335
|
+
if (x === void 0) return 1;
|
|
31336
|
+
if (y === void 0) return -1;
|
|
31337
|
+
if (x !== y) return y - x;
|
|
31338
|
+
}
|
|
31339
|
+
return 0;
|
|
31340
|
+
}
|
|
31341
|
+
/**
|
|
31342
|
+
* Group a list so every vendor's models sit together.
|
|
31343
|
+
*
|
|
31344
|
+
* Families appear in the order their first model does — the catalog decides
|
|
31345
|
+
* which vendor leads, not this function — and each family is ordered newest
|
|
31346
|
+
* generation first.
|
|
31347
|
+
*/
|
|
31348
|
+
function groupWorkBuddyModelsByVendor(models) {
|
|
31349
|
+
const families = /* @__PURE__ */ new Map();
|
|
31350
|
+
for (const model of models) {
|
|
31351
|
+
const family = workBuddyModelVendor(model.id);
|
|
31352
|
+
const bucket = families.get(family);
|
|
31353
|
+
if (bucket === void 0) families.set(family, [model]);
|
|
31354
|
+
else bucket.push(model);
|
|
31355
|
+
}
|
|
31356
|
+
return [...families.values()].map((bucket) => [...bucket].sort(compareWithinFamily)).flat();
|
|
31357
|
+
}
|
|
31358
|
+
/**
|
|
31359
|
+
* Models an account in `region` can actually call, grouped by vendor.
|
|
31360
|
+
*
|
|
31361
|
+
* Grouped here rather than in the settings card because this is also what the
|
|
31362
|
+
* request path resolves against: one ordering rule, so the picker and the
|
|
31363
|
+
* adapter can never disagree about which models a region offers.
|
|
31364
|
+
*/
|
|
31032
31365
|
function modelsForRegion(region, catalog = FALLBACK_MODELS$1) {
|
|
31033
|
-
return catalog.filter((model) => model.regions.includes(region));
|
|
31366
|
+
return groupWorkBuddyModelsByVendor(catalog.filter((model) => model.regions.includes(region)));
|
|
31034
31367
|
}
|
|
31035
31368
|
/** Whether one model accepts image input. */
|
|
31036
31369
|
function workBuddyModelSupportsImage(id, catalog = FALLBACK_MODELS$1) {
|
|
@@ -31905,6 +32238,14 @@ async function fetchAccountIdentity$1(credentials, options = {}) {
|
|
|
31905
32238
|
* inferred from the model's name. Image-generation tools (`tags` naming
|
|
31906
32239
|
* `text-to-image`/`image-to-image`) are skipped: they are not chat models and
|
|
31907
32240
|
* carry no context window.
|
|
32241
|
+
*
|
|
32242
|
+
* `credits` is read here rather than left to the shipped table because a
|
|
32243
|
+
* reachable gateway **replaces** that table entirely: a multiplier transcribed
|
|
32244
|
+
* offline would be the only one a signed-in user ever saw, and it would stay
|
|
32245
|
+
* wrong the moment the subscription repriced a model. A string this route cannot
|
|
32246
|
+
* turn into a number (`''`, which `default-model` publishes, or a value a
|
|
32247
|
+
* future gateway invents) leaves the field off the entry altogether instead of
|
|
32248
|
+
* writing a plausible-looking `0` — see `parseWorkBuddyCreditMultiplier`.
|
|
31908
32249
|
*/
|
|
31909
32250
|
function parseConfigModels(payload, region) {
|
|
31910
32251
|
const root = asRecord$4(payload) ?? {};
|
|
@@ -31926,6 +32267,7 @@ function parseConfigModels(payload, region) {
|
|
|
31926
32267
|
const declaredDefault = asString$5(reasoning.defaultEffort) ?? single ?? null;
|
|
31927
32268
|
const modelDefault = declaredDefault === null || efforts.includes(declaredDefault) ? declaredDefault : convergeWorkBuddyEffort(declaredDefault, efforts);
|
|
31928
32269
|
const servedDefault = asNumber$1(asRecord$4(record.contextWindow)?.defaultLength);
|
|
32270
|
+
const multiplier = parseWorkBuddyCreditMultiplier(record.credits);
|
|
31929
32271
|
models.push({
|
|
31930
32272
|
id,
|
|
31931
32273
|
name: asString$5(record.name) ?? id,
|
|
@@ -31933,6 +32275,7 @@ function parseConfigModels(payload, region) {
|
|
|
31933
32275
|
maxContextWindow,
|
|
31934
32276
|
maxTokens: asNumber$1(record.maxOutputTokens) ?? 32768,
|
|
31935
32277
|
regions: [region],
|
|
32278
|
+
...multiplier === null ? {} : { credits: { [region]: multiplier } },
|
|
31936
32279
|
supportsImage: record.supportsImages === true,
|
|
31937
32280
|
reasoningEfforts: efforts,
|
|
31938
32281
|
defaultReasoningEffort: modelDefault,
|
|
@@ -31980,7 +32323,7 @@ async function fetchConfigCatalog(credentials, options = {}) {
|
|
|
31980
32323
|
signal: timeoutSignal$1(options.signal, DISCOVERY_TIMEOUT_MS$1)
|
|
31981
32324
|
});
|
|
31982
32325
|
if (!response.ok) throw new Error(`${PROVIDER_NAME$1} model catalog failed: ${response.status}`);
|
|
31983
|
-
return parseConfigModels(await response.json(), credentials.region);
|
|
32326
|
+
return withUnpublishedModels(parseConfigModels(await response.json(), credentials.region), credentials.region);
|
|
31984
32327
|
}
|
|
31985
32328
|
/** Cached catalog with a TTL; a failed refresh keeps the previous snapshot.
|
|
31986
32329
|
*
|
|
@@ -32014,7 +32357,7 @@ async function loadConfigCatalog(credentials, options = {}) {
|
|
|
32014
32357
|
}, (fetchedAt, models) => {
|
|
32015
32358
|
cachedCatalog = {
|
|
32016
32359
|
region: credentials.region,
|
|
32017
|
-
models,
|
|
32360
|
+
models: withUnpublishedModels(models, credentials.region),
|
|
32018
32361
|
fetchedAt
|
|
32019
32362
|
};
|
|
32020
32363
|
}, 0) > 0) {
|
|
@@ -32540,7 +32883,20 @@ function resolveEnabledModelIds$2(stored, available, enabled = true) {
|
|
|
32540
32883
|
const known = new Set(available);
|
|
32541
32884
|
return stored.filter((id) => known.has(id));
|
|
32542
32885
|
}
|
|
32543
|
-
/**
|
|
32886
|
+
/**
|
|
32887
|
+
* Build the model options the settings card renders.
|
|
32888
|
+
*
|
|
32889
|
+
* Order comes from the caller, which passes a vendor-grouped list so a family
|
|
32890
|
+
* is not interleaved with the next one; this preserves whatever order it is
|
|
32891
|
+
* given rather than imposing a second one.
|
|
32892
|
+
*
|
|
32893
|
+
* `credits` is passed through whole, every region the entry carries. Trimming
|
|
32894
|
+
* it to one account's region is tempting and wrong: this DTO is also read with
|
|
32895
|
+
* no account in hand (the harness resolves a model list before a credential is
|
|
32896
|
+
* chosen), and such a caller would be told nothing rather than everything. The
|
|
32897
|
+
* region key the reader actually has is already on each entry as `regions`, so
|
|
32898
|
+
* picking a value is the renderer's job, not this one's.
|
|
32899
|
+
*/
|
|
32544
32900
|
function buildModelOptions(catalog, available, enabledModelIds, overrides) {
|
|
32545
32901
|
const enabled = new Set(enabledModelIds);
|
|
32546
32902
|
return available.map((id) => {
|
|
@@ -32557,6 +32913,7 @@ function buildModelOptions(catalog, available, enabledModelIds, overrides) {
|
|
|
32557
32913
|
reasoningEfforts: [...workBuddyReasoningEfforts(id, catalog)],
|
|
32558
32914
|
supportsImage: workBuddyModelSupportsImage(id, catalog),
|
|
32559
32915
|
regions: [...entry.regions],
|
|
32916
|
+
...entry.credits === void 0 || Object.keys(entry.credits).length === 0 ? {} : { credits: { ...entry.credits } },
|
|
32560
32917
|
description: entry.description
|
|
32561
32918
|
};
|
|
32562
32919
|
});
|
|
@@ -45550,6 +45907,22 @@ function installRelayProbe(ctx, options) {
|
|
|
45550
45907
|
* degenerate stream scores near one. On a hit it stops yielding chunks, so the
|
|
45551
45908
|
* in-flight request ends, and lets the turn resume on a fresh step.
|
|
45552
45909
|
*
|
|
45910
|
+
* Uniqueness alone only sees verbatim repetition. A stream can restate one
|
|
45911
|
+
* conclusion over and over in fresh words, score 0.48 where the threshold is
|
|
45912
|
+
* 0.85, and run to the output ceiling untouched. So a second, independent
|
|
45913
|
+
* signal scores the same window for recurrence: how strongly each sentence
|
|
45914
|
+
* repeats something the model already said at least `semanticLag` sentences
|
|
45915
|
+
* earlier. Enumerative reasoning repeats itself too, but adjacently — each item
|
|
45916
|
+
* borrows the previous item's frame — and recurrence ignores adjacency, which
|
|
45917
|
+
* is what separates the two.
|
|
45918
|
+
*
|
|
45919
|
+
* Neither signal stops anything on its own. A break needs both over their
|
|
45920
|
+
* thresholds in the same window, and then `semanticConfirmations` consecutive
|
|
45921
|
+
* such windows: one signal alone, or a single anomalous window, is logged and
|
|
45922
|
+
* released. That gradient is the whole design. A guard that cuts a healthy
|
|
45923
|
+
* answer is worse than one that lets a runaway finish, because the user sees a
|
|
45924
|
+
* turn stop mid-sentence with no explanation, and the fix costs a whole turn.
|
|
45925
|
+
*
|
|
45553
45926
|
* Seam choice: the guard wraps `llm/stream`, not `agent/assistant-stream`.
|
|
45554
45927
|
* `agent/assistant-stream` is emit-mode — it can observe but cannot stop a
|
|
45555
45928
|
* stream — and it does not exist before harness 0.1.5, which would make the
|
|
@@ -45586,6 +45959,15 @@ const DEFAULT_GUARD_OPTIONS = {
|
|
|
45586
45959
|
maxBreaksPerTurn: 3,
|
|
45587
45960
|
cooldownMs: 3e4,
|
|
45588
45961
|
includeModels: [],
|
|
45962
|
+
semanticThreshold: .35,
|
|
45963
|
+
semanticLag: 6,
|
|
45964
|
+
semanticMinChars: 360,
|
|
45965
|
+
semanticMinSentences: 8,
|
|
45966
|
+
semanticConfirmations: 3,
|
|
45967
|
+
semanticLiteralFloor: .6,
|
|
45968
|
+
semanticStrictThreshold: .45,
|
|
45969
|
+
semanticDistinctCeiling: .47,
|
|
45970
|
+
semanticStrictConfirmations: 5,
|
|
45589
45971
|
now: () => Date.now()
|
|
45590
45972
|
};
|
|
45591
45973
|
function pruneUndefined(options) {
|
|
@@ -45611,8 +45993,21 @@ function resolveGuardOptions(options = {}) {
|
|
|
45611
45993
|
if (!Number.isFinite(resolved.threshold) || resolved.threshold <= 0 || resolved.threshold >= 1) throw new Error(`reasoning-collapse-guard: invalid threshold ${String(resolved.threshold)} — must be strictly between 0 and 1`);
|
|
45612
45994
|
if (!Number.isInteger(resolved.maxBreaksPerTurn) || resolved.maxBreaksPerTurn < 1) throw new Error(`reasoning-collapse-guard: invalid maxBreaksPerTurn ${String(resolved.maxBreaksPerTurn)} — must be an integer >= 1`);
|
|
45613
45995
|
if (!Number.isInteger(resolved.cooldownMs) || resolved.cooldownMs < 0) throw new Error(`reasoning-collapse-guard: invalid cooldownMs ${String(resolved.cooldownMs)} — must be an integer >= 0`);
|
|
45996
|
+
if (!Number.isFinite(resolved.semanticThreshold) || resolved.semanticThreshold <= 0 || resolved.semanticThreshold >= 1) throw new Error(`reasoning-collapse-guard: invalid semanticThreshold ${String(resolved.semanticThreshold)} — must be strictly between 0 and 1`);
|
|
45997
|
+
if (!Number.isInteger(resolved.semanticLag) || resolved.semanticLag < 1) throw new Error(`reasoning-collapse-guard: invalid semanticLag ${String(resolved.semanticLag)} — must be an integer >= 1`);
|
|
45998
|
+
if (!Number.isInteger(resolved.semanticMinChars) || resolved.semanticMinChars < 1) throw new Error(`reasoning-collapse-guard: invalid semanticMinChars ${String(resolved.semanticMinChars)} — must be an integer >= 1`);
|
|
45999
|
+
if (resolved.semanticMinChars > resolved.windowChars) throw new Error(`reasoning-collapse-guard: invalid semanticMinChars ${resolved.semanticMinChars} — must not exceed windowChars ${resolved.windowChars}`);
|
|
46000
|
+
if (!Number.isInteger(resolved.semanticMinSentences) || resolved.semanticMinSentences < 2) throw new Error(`reasoning-collapse-guard: invalid semanticMinSentences ${String(resolved.semanticMinSentences)} — must be an integer >= 2`);
|
|
46001
|
+
if (!Number.isInteger(resolved.semanticConfirmations) || resolved.semanticConfirmations < 1) throw new Error(`reasoning-collapse-guard: invalid semanticConfirmations ${String(resolved.semanticConfirmations)} — must be an integer >= 1`);
|
|
46002
|
+
if (!Number.isFinite(resolved.semanticLiteralFloor) || resolved.semanticLiteralFloor <= 0 || resolved.semanticLiteralFloor >= 1) throw new Error(`reasoning-collapse-guard: invalid semanticLiteralFloor ${String(resolved.semanticLiteralFloor)} — must be strictly between 0 and 1`);
|
|
46003
|
+
if (!Number.isFinite(resolved.semanticStrictThreshold) || resolved.semanticStrictThreshold <= 0 || resolved.semanticStrictThreshold >= 1) throw new Error(`reasoning-collapse-guard: invalid semanticStrictThreshold ${String(resolved.semanticStrictThreshold)} — must be strictly between 0 and 1`);
|
|
46004
|
+
if (!Number.isFinite(resolved.semanticDistinctCeiling) || resolved.semanticDistinctCeiling <= 0 || resolved.semanticDistinctCeiling >= 1) throw new Error(`reasoning-collapse-guard: invalid semanticDistinctCeiling ${String(resolved.semanticDistinctCeiling)} — must be strictly between 0 and 1`);
|
|
46005
|
+
if (!Number.isInteger(resolved.semanticStrictConfirmations) || resolved.semanticStrictConfirmations < 1) throw new Error(`reasoning-collapse-guard: invalid semanticStrictConfirmations ${String(resolved.semanticStrictConfirmations)} — must be an integer >= 1`);
|
|
45614
46006
|
if (!Array.isArray(resolved.includeModels) || resolved.includeModels.some((entry) => typeof entry !== "string")) throw new Error("reasoning-collapse-guard: includeModels must be an array of strings");
|
|
45615
46007
|
if (typeof resolved.now !== "function") throw new Error("reasoning-collapse-guard: now must be a function");
|
|
46008
|
+
resolved.semanticStrictThreshold = Math.max(resolved.semanticStrictThreshold, resolved.semanticThreshold);
|
|
46009
|
+
resolved.semanticStrictConfirmations = Math.max(resolved.semanticStrictConfirmations, resolved.semanticConfirmations + 1);
|
|
46010
|
+
resolved.semanticLiteralFloor = Math.min(resolved.semanticLiteralFloor, resolved.threshold);
|
|
45616
46011
|
return resolved;
|
|
45617
46012
|
}
|
|
45618
46013
|
/**
|
|
@@ -45635,6 +46030,92 @@ function collapseScore(window, n) {
|
|
|
45635
46030
|
if (total === 0) return 0;
|
|
45636
46031
|
return 1 - grams.size / total;
|
|
45637
46032
|
}
|
|
46033
|
+
/**
|
|
46034
|
+
* Split reasoning text into sentences, for mixed CJK and Latin streams.
|
|
46035
|
+
*
|
|
46036
|
+
* A Chinese full stop ends a sentence whatever follows it. An ASCII `.!?` only
|
|
46037
|
+
* ends one when whitespace or the end of the text follows, because the same
|
|
46038
|
+
* characters carry out decimals, file names and version numbers
|
|
46039
|
+
* (`selectors.test.ts:745`, `3.14`) that must not become sentence
|
|
46040
|
+
* edges. Whitespace runs collapse and case folds, so a difference in spacing
|
|
46041
|
+
* or capitalisation cannot read as a difference in wording.
|
|
46042
|
+
*
|
|
46043
|
+
* @param text - reasoning text, typically one trailing window.
|
|
46044
|
+
* @returns non-empty normalized sentences, in order.
|
|
46045
|
+
*/
|
|
46046
|
+
function splitSentences(text) {
|
|
46047
|
+
const parts = [];
|
|
46048
|
+
let start = 0;
|
|
46049
|
+
for (let i = 0; i < text.length; i++) {
|
|
46050
|
+
const char = text[i];
|
|
46051
|
+
const next = text[i + 1];
|
|
46052
|
+
const cjk = CJK_TERMINATORS.has(char);
|
|
46053
|
+
const latin = LATIN_TERMINATORS.has(char) && (next === void 0 || TRAILING_SPACE.test(next));
|
|
46054
|
+
if (!cjk && !latin) continue;
|
|
46055
|
+
parts.push(text.slice(start, i + 1));
|
|
46056
|
+
start = i + 1;
|
|
46057
|
+
}
|
|
46058
|
+
parts.push(text.slice(start));
|
|
46059
|
+
const sentences = [];
|
|
46060
|
+
for (const part of parts) {
|
|
46061
|
+
const normalized = part.replace(WHITESPACE_RUN, " ").trim().toLowerCase();
|
|
46062
|
+
if (normalized.length > 0) sentences.push(normalized);
|
|
46063
|
+
}
|
|
46064
|
+
return sentences;
|
|
46065
|
+
}
|
|
46066
|
+
/** The three Chinese sentence terminators, which need no trailing space. */
|
|
46067
|
+
const CJK_TERMINATORS = /* @__PURE__ */ new Set([
|
|
46068
|
+
"。",
|
|
46069
|
+
"!",
|
|
46070
|
+
"?"
|
|
46071
|
+
]);
|
|
46072
|
+
/** The three ASCII terminators, which only close a sentence before whitespace. */
|
|
46073
|
+
const LATIN_TERMINATORS = /* @__PURE__ */ new Set([
|
|
46074
|
+
".",
|
|
46075
|
+
"!",
|
|
46076
|
+
"?"
|
|
46077
|
+
]);
|
|
46078
|
+
const TRAILING_SPACE = /\s/;
|
|
46079
|
+
const WHITESPACE_RUN = /\s+/g;
|
|
46080
|
+
/** Character n-grams of one normalized sentence, as a set. */
|
|
46081
|
+
function sentenceGrams(sentence) {
|
|
46082
|
+
const grams = /* @__PURE__ */ new Set();
|
|
46083
|
+
for (let i = 0; i + SEMANTIC_NGRAM_SIZE <= sentence.length; i++) grams.add(sentence.slice(i, i + SEMANTIC_NGRAM_SIZE));
|
|
46084
|
+
return grams;
|
|
46085
|
+
}
|
|
46086
|
+
function gramJaccard(a, b) {
|
|
46087
|
+
if (a.size === 0 || b.size === 0) return 0;
|
|
46088
|
+
let shared = 0;
|
|
46089
|
+
for (const gram of a) if (b.has(gram)) shared++;
|
|
46090
|
+
return shared / (a.size + b.size - shared);
|
|
46091
|
+
}
|
|
46092
|
+
/** {@link semanticRecurrence} over sentences the caller has already split. */
|
|
46093
|
+
function sentenceRecurrence(sentences, lag) {
|
|
46094
|
+
const grams = sentences.map(sentenceGrams);
|
|
46095
|
+
if (grams.length <= lag) return 0;
|
|
46096
|
+
let total = 0;
|
|
46097
|
+
let counted = 0;
|
|
46098
|
+
for (let i = lag; i < grams.length; i++) {
|
|
46099
|
+
const current = grams[i];
|
|
46100
|
+
let best = 0;
|
|
46101
|
+
for (let j = 0; j <= i - lag; j++) {
|
|
46102
|
+
const earlier = grams[j];
|
|
46103
|
+
if (Math.min(current.size, earlier.size) / Math.max(current.size, earlier.size) <= best) continue;
|
|
46104
|
+
const similarity = gramJaccard(current, earlier);
|
|
46105
|
+
if (similarity > best) best = similarity;
|
|
46106
|
+
}
|
|
46107
|
+
total += best;
|
|
46108
|
+
counted++;
|
|
46109
|
+
}
|
|
46110
|
+
return counted === 0 ? 0 : total / counted;
|
|
46111
|
+
}
|
|
46112
|
+
/** Character n-gram size both halves of the semantic signal are built on. */
|
|
46113
|
+
const SEMANTIC_NGRAM_SIZE = 3;
|
|
46114
|
+
/** {@link distinctSentenceRatio} over sentences the caller already split. */
|
|
46115
|
+
function distinctRatioOf(sentences) {
|
|
46116
|
+
if (sentences.length < 2) return 1;
|
|
46117
|
+
return new Set(sentences).size / sentences.length;
|
|
46118
|
+
}
|
|
45638
46119
|
const RESUME_HINT = "Your previous reasoning became repetitive and was stopped before it finished. Resume from what you already established instead of restarting the analysis, and keep the rest of this response short.";
|
|
45639
46120
|
/** The second resume message, used once a resume has also collapsed. */
|
|
45640
46121
|
const RESUME_HINT_STRICT = "Your reasoning has now repeated across two attempts. Do not re-derive the analysis a third time. Either state the conclusion you already reached, or make the single next tool call that moves the task forward. Keep this response to a few sentences.";
|
|
@@ -45643,7 +46124,11 @@ function createBreakerState() {
|
|
|
45643
46124
|
breaksThisTurn: 0,
|
|
45644
46125
|
lastBreakAt: -Infinity,
|
|
45645
46126
|
exhausted: false,
|
|
45646
|
-
resuming: false
|
|
46127
|
+
resuming: false,
|
|
46128
|
+
dualStreak: 0,
|
|
46129
|
+
lowStreak: 0,
|
|
46130
|
+
notedLiteralOnly: false,
|
|
46131
|
+
notedSemanticOnly: false
|
|
45647
46132
|
};
|
|
45648
46133
|
}
|
|
45649
46134
|
/**
|
|
@@ -45798,10 +46283,12 @@ function installReasoningCollapseGuard(ctx, options = {}) {
|
|
|
45798
46283
|
* point — and the log says so, because a break that cannot resume is
|
|
45799
46284
|
* indistinguishable from a broken turn to the user.
|
|
45800
46285
|
*/
|
|
45801
|
-
function breakOff(state,
|
|
46286
|
+
function breakOff(state, literal, semantic, path, agent, sessionId) {
|
|
45802
46287
|
state.breaksThisTurn++;
|
|
45803
46288
|
state.lastBreakAt = resolved.now();
|
|
45804
|
-
|
|
46289
|
+
state.dualStreak = 0;
|
|
46290
|
+
state.lowStreak = 0;
|
|
46291
|
+
ctx.logger?.warn(`reasoning-collapse-guard: stopped degenerate reasoning on the ${path} path after ${path === "high-literal" ? resolved.semanticConfirmations : resolved.semanticStrictConfirmations} confirmed windows (literal ${literal.toFixed(3)}, semantic ${semantic.toFixed(3)}, break ${state.breaksThisTurn}/${resolved.maxBreaksPerTurn}, session ${sessionId ?? "unidentified"}${agent === void 0 ? ", no agent to resume" : ""})`);
|
|
45805
46292
|
if (agent === void 0) return;
|
|
45806
46293
|
agent.cancel?.({
|
|
45807
46294
|
kind: "hook",
|
|
@@ -45819,6 +46306,48 @@ function installReasoningCollapseGuard(ctx, options = {}) {
|
|
|
45819
46306
|
}
|
|
45820
46307
|
});
|
|
45821
46308
|
}
|
|
46309
|
+
/**
|
|
46310
|
+
* Report one signal holding without the other.
|
|
46311
|
+
*
|
|
46312
|
+
* Reported once per kind per turn, not once per window: a runaway can hold
|
|
46313
|
+
* one signal for hundreds of windows, and a line each would bury the report
|
|
46314
|
+
* of the one that mattered. This is the diagnostic half of the gradient —
|
|
46315
|
+
* the reason a near miss is still diagnosable after the fact.
|
|
46316
|
+
*/
|
|
46317
|
+
function noteLoneSignal(state, kind, literal, semantic, sessionId) {
|
|
46318
|
+
if (kind === "literal" ? state.notedLiteralOnly : state.notedSemanticOnly) return;
|
|
46319
|
+
if (kind === "literal") state.notedLiteralOnly = true;
|
|
46320
|
+
else state.notedSemanticOnly = true;
|
|
46321
|
+
ctx.logger?.warn(`reasoning-collapse-guard: observed the ${kind} signal alone (literal ${literal === void 0 ? "not scored" : literal.toFixed(3)}, semantic ${semantic === void 0 ? "not scored" : semantic.toFixed(3)}, session ${sessionId ?? "unidentified"}) — the other signal has not agreed, not acting`);
|
|
46322
|
+
}
|
|
46323
|
+
/**
|
|
46324
|
+
* Report a run of agreeing windows that ended before it reached the count.
|
|
46325
|
+
*
|
|
46326
|
+
* Without this, two short runs of agreeing windows look in the log exactly
|
|
46327
|
+
* like one long one that simply stopped short, and the confirmation count
|
|
46328
|
+
* becomes impossible to reason about after the fact.
|
|
46329
|
+
*/
|
|
46330
|
+
function noteStreakCleared(state, reached, sessionId) {
|
|
46331
|
+
ctx.logger?.warn(`reasoning-collapse-guard: the signals stopped agreeing after ${reached} of ${resolved.semanticConfirmations} windows (session ${sessionId ?? "unidentified"}) — streak cleared, not acting`);
|
|
46332
|
+
}
|
|
46333
|
+
/** Report the low-literal run that ended before it reached its longer count. */
|
|
46334
|
+
function noteLowStreakCleared(reached, sessionId) {
|
|
46335
|
+
ctx.logger?.warn(`reasoning-collapse-guard: the low-literal bars stopped holding after ${reached} of ${resolved.semanticStrictConfirmations} windows (session ${sessionId ?? "unidentified"}) — streak cleared, not acting`);
|
|
46336
|
+
}
|
|
46337
|
+
/** Report a low-literal window that qualifies but has not been confirmed enough. */
|
|
46338
|
+
function notePendingLowConfirmation(state, literal, semantic, distinct, sessionId) {
|
|
46339
|
+
ctx.logger?.warn(`reasoning-collapse-guard: low-literal bars hold in window ${state.lowStreak}/${resolved.semanticStrictConfirmations} (literal ${literal.toFixed(3)} >= ${resolved.semanticLiteralFloor}, semantic ${semantic.toFixed(3)} >= ${resolved.semanticStrictThreshold}, distinct ${distinct.toFixed(3)} <= ${resolved.semanticDistinctCeiling}, session ${sessionId ?? "unidentified"}) — not acting yet`);
|
|
46340
|
+
}
|
|
46341
|
+
/**
|
|
46342
|
+
* Report a window where both signals held but the streak is still short.
|
|
46343
|
+
*
|
|
46344
|
+
* `semanticConfirmations` is what makes a break a decision rather than a
|
|
46345
|
+
* reflex, so each pending confirmation is named, and the log then says how
|
|
46346
|
+
* close a stream came to being cut by a guard that did not cut it.
|
|
46347
|
+
*/
|
|
46348
|
+
function notePendingConfirmation(state, literal, semantic, sessionId) {
|
|
46349
|
+
ctx.logger?.warn(`reasoning-collapse-guard: both signals hold in window ${state.dualStreak}/${resolved.semanticConfirmations} (literal ${literal.toFixed(3)}, semantic ${semantic.toFixed(3)}, session ${sessionId ?? "unidentified"}) — not acting yet`);
|
|
46350
|
+
}
|
|
45822
46351
|
function guardStream(request, next) {
|
|
45823
46352
|
const state = breakerFor(request.signal ?? STATIC_KEY);
|
|
45824
46353
|
if (state.exhausted || !watchedModel(request.model) || !conversationCall(request)) return next();
|
|
@@ -45829,13 +46358,33 @@ function installReasoningCollapseGuard(ctx, options = {}) {
|
|
|
45829
46358
|
if (chunk.type === "block-start") window = "";
|
|
45830
46359
|
if (chunk.type === "reasoning-delta" && typeof chunk.text === "string") {
|
|
45831
46360
|
window = appendWindow(window, chunk.text, resolved.windowChars);
|
|
45832
|
-
if (
|
|
45833
|
-
const
|
|
45834
|
-
|
|
46361
|
+
if (resolved.now() - state.lastBreakAt >= resolved.cooldownMs) {
|
|
46362
|
+
const literal = window.length >= resolved.minWindowChars ? collapseScore(window, resolved.ngramSize) : void 0;
|
|
46363
|
+
const sentences = window.length >= resolved.semanticMinChars ? splitSentences(window) : [];
|
|
46364
|
+
const semantic = sentences.length >= resolved.semanticMinSentences ? sentenceRecurrence(sentences, resolved.semanticLag) : void 0;
|
|
46365
|
+
const distinct = sentences.length >= resolved.semanticMinSentences ? distinctRatioOf(sentences) : void 0;
|
|
46366
|
+
const high = literal !== void 0 && literal >= resolved.threshold && semantic !== void 0 && semantic >= resolved.semanticThreshold;
|
|
46367
|
+
const low = literal !== void 0 && literal >= resolved.semanticLiteralFloor && literal < resolved.threshold && semantic !== void 0 && semantic >= resolved.semanticStrictThreshold && distinct !== void 0 && distinct <= resolved.semanticDistinctCeiling;
|
|
46368
|
+
const reachedHigh = state.dualStreak;
|
|
46369
|
+
const reachedLow = state.lowStreak;
|
|
46370
|
+
state.dualStreak = high ? reachedHigh + 1 : 0;
|
|
46371
|
+
state.lowStreak = low ? reachedLow + 1 : 0;
|
|
46372
|
+
if (reachedHigh !== 0 && state.dualStreak === 0) noteStreakCleared(state, reachedHigh, request.sessionId);
|
|
46373
|
+
if (reachedLow !== 0 && state.lowStreak === 0) noteLowStreakCleared(reachedLow, request.sessionId);
|
|
46374
|
+
if (high && state.dualStreak >= resolved.semanticConfirmations) if (state.breaksThisTurn >= resolved.maxBreaksPerTurn) state.exhausted = true;
|
|
46375
|
+
else {
|
|
46376
|
+
breakOff(state, literal, semantic, "high-literal", agent, request.sessionId);
|
|
46377
|
+
return;
|
|
46378
|
+
}
|
|
46379
|
+
else if (low && state.lowStreak >= resolved.semanticStrictConfirmations) if (state.breaksThisTurn >= resolved.maxBreaksPerTurn) state.exhausted = true;
|
|
45835
46380
|
else {
|
|
45836
|
-
breakOff(state,
|
|
46381
|
+
breakOff(state, literal, semantic, "low-literal", agent, request.sessionId);
|
|
45837
46382
|
return;
|
|
45838
46383
|
}
|
|
46384
|
+
else if (high) notePendingConfirmation(state, literal, semantic, request.sessionId);
|
|
46385
|
+
else if (low) notePendingLowConfirmation(state, literal, semantic, distinct, request.sessionId);
|
|
46386
|
+
else if (literal !== void 0 && literal >= resolved.threshold) noteLoneSignal(state, "literal", literal, semantic, request.sessionId);
|
|
46387
|
+
else if (semantic !== void 0 && semantic >= resolved.semanticThreshold) noteLoneSignal(state, "semantic", literal, semantic, request.sessionId);
|
|
45839
46388
|
}
|
|
45840
46389
|
}
|
|
45841
46390
|
yield chunk;
|