dsh-xdpool 2.3.0 → 2.4.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 +25 -0
- package/lib/bin.js +88 -11
- package/lib/client.js +3 -3
- package/lib/index.d.ts +2 -2
- package/lib/index.js +166 -42
- package/package.json +1 -1
- package/vendor/channel-pack/pack.js +41 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
本项目的所有重要变更都会记录在此文件。
|
|
3
3
|
版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
4
|
+
## 2.4.0 — 会话身份头、清单不再冻结、中文路由可存
|
|
5
|
+
> 这一版收的是「用着用着就报错」的那一类问题:调用被网关判成非法渠道(11128)、国际版模型清单永远停在启动那一刻、中文名字的路由点了保存却没存下、上下文选择一选就弹回去。
|
|
6
|
+
### 会话身份头:修掉 400 / 11128「Illegal API invocation from an unapproved channel」
|
|
7
|
+
网关对「这一轮对话属于谁」有一组身份头,缺一个就整轮拒绝,报 `11128 Illegal API invocation from an unapproved channel`,看起来像密钥失效,其实是新会话没自报身份。补齐官方客户端的契约:
|
|
8
|
+
- **会话稳定三头**(同一会话内不变):`X-Conversation-ID`、`X-Conversation-Request-ID`、`acp-connection-id`。
|
|
9
|
+
- **每请求两头**(逐次随机):`X-Conversation-Message-ID`、`X-Request-ID`。
|
|
10
|
+
- 另附 `X-Agent-Intent`、`X-Agent-Purpose`、`x-codebuddy-request`、`X-Private-Data`、`X-Product` 与 W3C/B3 追踪族。
|
|
11
|
+
|
|
12
|
+
实测口径:三个稳定头缺任一个即 400 11128,齐全即 200;值本身不校验(非 UUID 字符串也过)。池车道(WorkBuddy 国内/国际)与扫码渠道的 `send` 两条路径都已接上。
|
|
13
|
+
顺带修掉同源的一处:`role: "developer"` 的消息在国内网关同样触发 11128,现统一上送为 `system`。
|
|
14
|
+
### 国际版模型清单:不再冻结在启动那一刻
|
|
15
|
+
`createProvider` 的 `models` 是在创建 provider 时快照下来的数组,而原来的 `invalidate()` 只重排了 profile、复用了同一个 provider 对象,宿主按对象身份记忆化 `current()`,于是**新拿到的模型清单永远送不到选择器**——表现是「重新获取模型」点了没反应、国际版清单停在开机那一刻。现在每次 `invalidate()` 重建 provider 与 profile 两个对象,并通过 adapter handle 的 `replace()` 重新登记路由,由宿主自己发出 `llm/adapters-updated`。
|
|
16
|
+
(附带核查:国际版本来就有独立的 `modelSelectionGlobal` 键,与国内白名单分开读写,这一处无需改动。)
|
|
17
|
+
### 中文路由名现在存得下
|
|
18
|
+
链名 slug 原来是 `[^a-z0-9]+ → '-'`,中文名整串被压成空 id,规范化时被当作无效条目静默丢弃——于是「保存成功」但什么都没存。改为 Unicode 感知的 slug(`\p{L}`/`\p{N}`),拉丁名形态完全不变,已存的路由 id 继续可解析。
|
|
19
|
+
### 上下文大小:选中态不再一选就弹回
|
|
20
|
+
渠道卡把「从未保存过预算」当成了「已收窄到 200K」显示,用户点「完整」清空预算后该行又算回 200K,选中态立刻跳回第一项。现在未保存过预算就显示完整窗口;清空预算时同时保留 `nativeContextWindow`,radio 组不会在指针底下消失。
|
|
21
|
+
### 思考档位:不再只认一档
|
|
22
|
+
pack 兜底表把十几个模型的 `reasoningEfforts` 写成了单档(`["medium"]` / `["high"]`),于是国际版 `Deepseek-V4.1-Flash` 只能选 High。逐模型实测(两网关、五档全跑)后按真实能力补齐:凡是能调通的模型五档都接受、且各档 `reasoning_content` 长度不同。`gpt-5.3-codex` 五档皆 200 但 reasoning 恒空——档位合法而不可观测,已经在表里注明;`gemini-3.5-flash` 与 `primary-model` 的 400 是账号套餐权限(`model [X] is not allowed for user type [personal]`),与档位无关,未动。
|
|
23
|
+
### 重复账号:同一个人不再显示成两个
|
|
24
|
+
桌面端凭据同时带数字 `uin` 与 UUID `uid`,扫码渠道只带 `uid`,而账号 id 是按 `uin` 优先哈希的——同一个人走两条登录路径就整出两个池条目,各自带一部分模型。现在按 `uid` 建次级索引合并到同一个池条目,既不改哈希(否则全部冷却、失效标记、忽略名单、持久化快照一次性作废),也与凭据到达顺序无关。
|
|
25
|
+
### 「重新检测账号」不再静默失败
|
|
26
|
+
`listAllAccounts` 漏了 `await`,拿到的是一个 Promise,随后的 `.filter` 抛 `is not a function` 并被外层 try/catch 吞掉,表现是扫码账号全部并进池、重新检测永远没结果。
|
|
27
|
+
### 其他
|
|
28
|
+
- 测试:新增会话身份头与中文路由名的回归用例,全套 68 文件 910 用例。
|
|
4
29
|
## 2.3.0 — 扫码渠道接入账号池,免密车道修复,数据看板有了真数
|
|
5
30
|
> 这一版收掉三件大事:扫码登录的账号真正归入账号池并按渠道展示;免费模型车道跟上上游的新校验、不再动辄 403;数据看板不再是一排零。
|
|
6
31
|
### 账号池:扫码渠道按渠道入池、逐模型可控
|
package/lib/bin.js
CHANGED
|
@@ -277,6 +277,80 @@ const CHAT_IDENTITY_HEADERS = Object.freeze({
|
|
|
277
277
|
"X-IDE-Type": "WorkBuddy",
|
|
278
278
|
"X-IDE-Version": "5.5.6"
|
|
279
279
|
});
|
|
280
|
+
/**
|
|
281
|
+
* The conversation-identity headers the gateway's channel check demands.
|
|
282
|
+
*
|
|
283
|
+
* WHY THIS EXISTS
|
|
284
|
+
*
|
|
285
|
+
* `www.workbuddy.ai` turned "channel verification" from a soft check into a
|
|
286
|
+
* hard one. A `POST /v2/chat/completions` missing ANY of `X-Conversation-ID`,
|
|
287
|
+
* `X-Conversation-Request-ID` or `X-Conversation-Message-ID` is rejected with
|
|
288
|
+
* business code 11128 (`msg: "request illegal"` on the international gateway,
|
|
289
|
+
* `msg: "conversation id missing"` in the earlier wording). Verified by
|
|
290
|
+
* elimination against real credentials: delete any one of the three and the
|
|
291
|
+
* call 400s; send all three and it 200s with normal SSE. Every other header the
|
|
292
|
+
* desktop sends (`acp-connection-id`, `X-Request-ID`, `X-Agent-Intent`,
|
|
293
|
+
* `X-Private-Data`, the W3C/B3 tracing family) can be dropped without effect,
|
|
294
|
+
* so only these three are load-bearing.
|
|
295
|
+
*
|
|
296
|
+
* The values are NOT validated — plain non-UUID strings pass — so the gateway
|
|
297
|
+
* is checking for presence and consistency, not for a signature. That lets us
|
|
298
|
+
* derive them from the conversation instead of registering state with upstream.
|
|
299
|
+
*
|
|
300
|
+
* 11128 IS MULTIPLEXED. The domestic gateway returns the same code with
|
|
301
|
+
* `msg: "Illegal API invocation from an unapproved channel"` for a different
|
|
302
|
+
* cause entirely: a `developer`-role message. `prepareChatBody()` already
|
|
303
|
+
* rewrites that role; see its comment. Never branch on the business code alone.
|
|
304
|
+
*/
|
|
305
|
+
const CONVERSATION_STABLE_SALT = "conversation";
|
|
306
|
+
/**
|
|
307
|
+
* The three stable conversation ids for one logical conversation.
|
|
308
|
+
*
|
|
309
|
+
* Stable across every call and every account in a conversation, because the
|
|
310
|
+
* upstream treats the triple as one session: regenerating them per request
|
|
311
|
+
* would read as a new conversation on each turn. Derived rather than stored so
|
|
312
|
+
* the identity survives a restart and needs no registry — the same reasoning
|
|
313
|
+
* `deriveDeviceId` uses for the device id.
|
|
314
|
+
*
|
|
315
|
+
* Falls back to a random seed when the caller has no key (a one-shot compaction
|
|
316
|
+
* outside any conversation), which is safe because presence is all that is
|
|
317
|
+
* checked.
|
|
318
|
+
*/
|
|
319
|
+
function conversationIdentityOf(conversationKey, seed = randomUUID) {
|
|
320
|
+
const base = conversationKey === void 0 || conversationKey === "" ? seed() : conversationKey;
|
|
321
|
+
const hash = (suffix) => createHash("sha256").update(`${CONVERSATION_STABLE_SALT}:${suffix}:${base}`).digest("hex");
|
|
322
|
+
const asUuid = (hex) => `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
323
|
+
return {
|
|
324
|
+
conversationId: asUuid(hash("id")),
|
|
325
|
+
conversationRequestId: asUuid(hash("request")),
|
|
326
|
+
acpConnectionId: asUuid(hash("acp"))
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Full header set for one chat completion.
|
|
331
|
+
*
|
|
332
|
+
* `chatHeaders()` stays untouched on purpose: it is shared with the
|
|
333
|
+
* points-automation endpoints, which have never been shown to carry
|
|
334
|
+
* conversation identity, and adding it there would change how that traffic is
|
|
335
|
+
* attributed. The per-request ids are fresh UUIDs every call; only the triple
|
|
336
|
+
* from `conversationIdentityOf` is stable.
|
|
337
|
+
*/
|
|
338
|
+
function chatCompletionHeaders(credential, conversationKey) {
|
|
339
|
+
const identity = conversationIdentityOf(conversationKey);
|
|
340
|
+
return {
|
|
341
|
+
...chatHeaders(credential),
|
|
342
|
+
...CHAT_IDENTITY_HEADERS,
|
|
343
|
+
"X-Conversation-ID": identity.conversationId,
|
|
344
|
+
"X-Conversation-Request-ID": identity.conversationRequestId,
|
|
345
|
+
"acp-connection-id": identity.acpConnectionId,
|
|
346
|
+
"X-Conversation-Message-ID": randomUUID(),
|
|
347
|
+
"X-Request-ID": randomUUID(),
|
|
348
|
+
"X-Agent-Intent": "craft",
|
|
349
|
+
"X-Agent-Purpose": "conversation",
|
|
350
|
+
"X-Private-Data": "true",
|
|
351
|
+
"x-codebuddy-request": "1"
|
|
352
|
+
};
|
|
353
|
+
}
|
|
280
354
|
/** Refresh-endpoint headers; X-Refresh-Token appears here and nowhere else. */
|
|
281
355
|
function refreshHeaders(credential) {
|
|
282
356
|
const headers = {
|
|
@@ -879,13 +953,10 @@ var WorkBuddyUpstreamClient = class {
|
|
|
879
953
|
* single string. Throws on any failure: the compactor then falls back to
|
|
880
954
|
* plain truncation rather than failing the user's turn.
|
|
881
955
|
*/
|
|
882
|
-
async completeChat(credential, prepared, signal) {
|
|
956
|
+
async completeChat(credential, prepared, signal, conversationKey) {
|
|
883
957
|
const response = await this.fetchImpl(`${chatBase(credential)}/v2/chat/completions`, {
|
|
884
958
|
method: "POST",
|
|
885
|
-
headers:
|
|
886
|
-
...chatHeaders(credential),
|
|
887
|
-
...CHAT_IDENTITY_HEADERS
|
|
888
|
-
},
|
|
959
|
+
headers: chatCompletionHeaders(credential, conversationKey),
|
|
889
960
|
body: prepared,
|
|
890
961
|
...signal === void 0 ? {} : { signal }
|
|
891
962
|
});
|
|
@@ -897,15 +968,12 @@ var WorkBuddyUpstreamClient = class {
|
|
|
897
968
|
return await readCompletionText(response.body);
|
|
898
969
|
}
|
|
899
970
|
/** Forward one chat completion. Never throws for upstream failures. */
|
|
900
|
-
async chatStream(credential, prepared, signal) {
|
|
971
|
+
async chatStream(credential, prepared, signal, conversationKey) {
|
|
901
972
|
let response;
|
|
902
973
|
try {
|
|
903
974
|
response = await this.fetchImpl(`${chatBase(credential)}/v2/chat/completions`, {
|
|
904
975
|
method: "POST",
|
|
905
|
-
headers:
|
|
906
|
-
...chatHeaders(credential),
|
|
907
|
-
...CHAT_IDENTITY_HEADERS
|
|
908
|
-
},
|
|
976
|
+
headers: chatCompletionHeaders(credential, conversationKey),
|
|
909
977
|
body: prepared,
|
|
910
978
|
...signal === void 0 ? {} : { signal }
|
|
911
979
|
});
|
|
@@ -3325,8 +3393,17 @@ var WorkBuddyAccountPool = class {
|
|
|
3325
3393
|
});
|
|
3326
3394
|
}
|
|
3327
3395
|
}
|
|
3396
|
+
const byUid = /* @__PURE__ */ new Map();
|
|
3397
|
+
for (const account of byId.values()) {
|
|
3398
|
+
const uid = account.credential.uid;
|
|
3399
|
+
if (typeof uid === "string" && uid !== "" && !byUid.has(uid)) byUid.set(uid, account.id);
|
|
3400
|
+
}
|
|
3328
3401
|
for (const credential of found) {
|
|
3329
|
-
const
|
|
3402
|
+
const byIdentity = workbuddyAccountId(credential);
|
|
3403
|
+
const uid = credential.uid;
|
|
3404
|
+
const knownId = typeof uid === "string" && uid !== "" ? byUid.get(uid) : void 0;
|
|
3405
|
+
const id = knownId ?? byIdentity;
|
|
3406
|
+
if (knownId === void 0 && typeof uid === "string" && uid !== "") byUid.set(uid, id);
|
|
3330
3407
|
const existing = byId.get(id);
|
|
3331
3408
|
if (existing === void 0) {
|
|
3332
3409
|
byId.set(id, {
|
package/lib/client.js
CHANGED
|
@@ -11674,7 +11674,7 @@ window.__ModuleLoader__.load({
|
|
|
11674
11674
|
...model,
|
|
11675
11675
|
...budget === void 0 ? {
|
|
11676
11676
|
contextBudget: void 0,
|
|
11677
|
-
|
|
11677
|
+
contextWindow: model.nativeContextWindow ?? model.contextWindow
|
|
11678
11678
|
} : {
|
|
11679
11679
|
contextBudget: budget,
|
|
11680
11680
|
contextWindow: budget,
|
|
@@ -12079,7 +12079,7 @@ window.__ModuleLoader__.load({
|
|
|
12079
12079
|
const recuse = recommendUse(merged);
|
|
12080
12080
|
const native = model.nativeContextWindow ?? model.contextWindow;
|
|
12081
12081
|
const capped = native !== void 0 && native > CONTEXT_BUDGET_DEFAULT;
|
|
12082
|
-
const budget = model.contextBudget ??
|
|
12082
|
+
const budget = model.contextBudget ?? native;
|
|
12083
12083
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
12084
12084
|
className: "dsm-xdpool-cpool-modelrow",
|
|
12085
12085
|
"data-off": !on,
|
|
@@ -12188,7 +12188,7 @@ window.__ModuleLoader__.load({
|
|
|
12188
12188
|
*
|
|
12189
12189
|
* @module dsh-xdpool/version
|
|
12190
12190
|
*/
|
|
12191
|
-
const POOL_VERSION = "2.
|
|
12191
|
+
const POOL_VERSION = "2.4.0";
|
|
12192
12192
|
//#endregion
|
|
12193
12193
|
//#region src/client/PoolCard.tsx
|
|
12194
12194
|
/**
|
package/lib/index.d.ts
CHANGED
|
@@ -489,9 +489,9 @@ export declare class WorkBuddyUpstreamClient {
|
|
|
489
489
|
* single string. Throws on any failure: the compactor then falls back to
|
|
490
490
|
* plain truncation rather than failing the user's turn.
|
|
491
491
|
*/
|
|
492
|
-
completeChat(credential: WorkBuddyCredential, prepared: string, signal?: AbortSignal): Promise<string>;
|
|
492
|
+
completeChat(credential: WorkBuddyCredential, prepared: string, signal?: AbortSignal, conversationKey?: string): Promise<string>;
|
|
493
493
|
/** Forward one chat completion. Never throws for upstream failures. */
|
|
494
|
-
chatStream(credential: WorkBuddyCredential, prepared: string, signal?: AbortSignal): Promise<ChatStreamResult>;
|
|
494
|
+
chatStream(credential: WorkBuddyCredential, prepared: string, signal?: AbortSignal, conversationKey?: string): Promise<ChatStreamResult>;
|
|
495
495
|
/** POST the token-refresh endpoint; the caller merges the outcome. */
|
|
496
496
|
refreshToken(credential: WorkBuddyCredential): Promise<WorkBuddyRefreshOutcome>;
|
|
497
497
|
/**
|
package/lib/index.js
CHANGED
|
@@ -328,6 +328,80 @@ const CHAT_IDENTITY_HEADERS = Object.freeze({
|
|
|
328
328
|
"X-IDE-Type": "WorkBuddy",
|
|
329
329
|
"X-IDE-Version": "5.5.6"
|
|
330
330
|
});
|
|
331
|
+
/**
|
|
332
|
+
* The conversation-identity headers the gateway's channel check demands.
|
|
333
|
+
*
|
|
334
|
+
* WHY THIS EXISTS
|
|
335
|
+
*
|
|
336
|
+
* `www.workbuddy.ai` turned "channel verification" from a soft check into a
|
|
337
|
+
* hard one. A `POST /v2/chat/completions` missing ANY of `X-Conversation-ID`,
|
|
338
|
+
* `X-Conversation-Request-ID` or `X-Conversation-Message-ID` is rejected with
|
|
339
|
+
* business code 11128 (`msg: "request illegal"` on the international gateway,
|
|
340
|
+
* `msg: "conversation id missing"` in the earlier wording). Verified by
|
|
341
|
+
* elimination against real credentials: delete any one of the three and the
|
|
342
|
+
* call 400s; send all three and it 200s with normal SSE. Every other header the
|
|
343
|
+
* desktop sends (`acp-connection-id`, `X-Request-ID`, `X-Agent-Intent`,
|
|
344
|
+
* `X-Private-Data`, the W3C/B3 tracing family) can be dropped without effect,
|
|
345
|
+
* so only these three are load-bearing.
|
|
346
|
+
*
|
|
347
|
+
* The values are NOT validated — plain non-UUID strings pass — so the gateway
|
|
348
|
+
* is checking for presence and consistency, not for a signature. That lets us
|
|
349
|
+
* derive them from the conversation instead of registering state with upstream.
|
|
350
|
+
*
|
|
351
|
+
* 11128 IS MULTIPLEXED. The domestic gateway returns the same code with
|
|
352
|
+
* `msg: "Illegal API invocation from an unapproved channel"` for a different
|
|
353
|
+
* cause entirely: a `developer`-role message. `prepareChatBody()` already
|
|
354
|
+
* rewrites that role; see its comment. Never branch on the business code alone.
|
|
355
|
+
*/
|
|
356
|
+
const CONVERSATION_STABLE_SALT = "conversation";
|
|
357
|
+
/**
|
|
358
|
+
* The three stable conversation ids for one logical conversation.
|
|
359
|
+
*
|
|
360
|
+
* Stable across every call and every account in a conversation, because the
|
|
361
|
+
* upstream treats the triple as one session: regenerating them per request
|
|
362
|
+
* would read as a new conversation on each turn. Derived rather than stored so
|
|
363
|
+
* the identity survives a restart and needs no registry — the same reasoning
|
|
364
|
+
* `deriveDeviceId` uses for the device id.
|
|
365
|
+
*
|
|
366
|
+
* Falls back to a random seed when the caller has no key (a one-shot compaction
|
|
367
|
+
* outside any conversation), which is safe because presence is all that is
|
|
368
|
+
* checked.
|
|
369
|
+
*/
|
|
370
|
+
function conversationIdentityOf(conversationKey, seed = randomUUID) {
|
|
371
|
+
const base = conversationKey === void 0 || conversationKey === "" ? seed() : conversationKey;
|
|
372
|
+
const hash = (suffix) => createHash("sha256").update(`${CONVERSATION_STABLE_SALT}:${suffix}:${base}`).digest("hex");
|
|
373
|
+
const asUuid = (hex) => `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
374
|
+
return {
|
|
375
|
+
conversationId: asUuid(hash("id")),
|
|
376
|
+
conversationRequestId: asUuid(hash("request")),
|
|
377
|
+
acpConnectionId: asUuid(hash("acp"))
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Full header set for one chat completion.
|
|
382
|
+
*
|
|
383
|
+
* `chatHeaders()` stays untouched on purpose: it is shared with the
|
|
384
|
+
* points-automation endpoints, which have never been shown to carry
|
|
385
|
+
* conversation identity, and adding it there would change how that traffic is
|
|
386
|
+
* attributed. The per-request ids are fresh UUIDs every call; only the triple
|
|
387
|
+
* from `conversationIdentityOf` is stable.
|
|
388
|
+
*/
|
|
389
|
+
function chatCompletionHeaders(credential, conversationKey) {
|
|
390
|
+
const identity = conversationIdentityOf(conversationKey);
|
|
391
|
+
return {
|
|
392
|
+
...chatHeaders(credential),
|
|
393
|
+
...CHAT_IDENTITY_HEADERS,
|
|
394
|
+
"X-Conversation-ID": identity.conversationId,
|
|
395
|
+
"X-Conversation-Request-ID": identity.conversationRequestId,
|
|
396
|
+
"acp-connection-id": identity.acpConnectionId,
|
|
397
|
+
"X-Conversation-Message-ID": randomUUID(),
|
|
398
|
+
"X-Request-ID": randomUUID(),
|
|
399
|
+
"X-Agent-Intent": "craft",
|
|
400
|
+
"X-Agent-Purpose": "conversation",
|
|
401
|
+
"X-Private-Data": "true",
|
|
402
|
+
"x-codebuddy-request": "1"
|
|
403
|
+
};
|
|
404
|
+
}
|
|
331
405
|
/** Refresh-endpoint headers; X-Refresh-Token appears here and nowhere else. */
|
|
332
406
|
function refreshHeaders(credential) {
|
|
333
407
|
const headers = {
|
|
@@ -1039,13 +1113,10 @@ var WorkBuddyUpstreamClient = class {
|
|
|
1039
1113
|
* single string. Throws on any failure: the compactor then falls back to
|
|
1040
1114
|
* plain truncation rather than failing the user's turn.
|
|
1041
1115
|
*/
|
|
1042
|
-
async completeChat(credential, prepared, signal) {
|
|
1116
|
+
async completeChat(credential, prepared, signal, conversationKey) {
|
|
1043
1117
|
const response = await this.fetchImpl(`${chatBase(credential)}/v2/chat/completions`, {
|
|
1044
1118
|
method: "POST",
|
|
1045
|
-
headers:
|
|
1046
|
-
...chatHeaders(credential),
|
|
1047
|
-
...CHAT_IDENTITY_HEADERS
|
|
1048
|
-
},
|
|
1119
|
+
headers: chatCompletionHeaders(credential, conversationKey),
|
|
1049
1120
|
body: prepared,
|
|
1050
1121
|
...signal === void 0 ? {} : { signal }
|
|
1051
1122
|
});
|
|
@@ -1057,15 +1128,12 @@ var WorkBuddyUpstreamClient = class {
|
|
|
1057
1128
|
return await readCompletionText(response.body);
|
|
1058
1129
|
}
|
|
1059
1130
|
/** Forward one chat completion. Never throws for upstream failures. */
|
|
1060
|
-
async chatStream(credential, prepared, signal) {
|
|
1131
|
+
async chatStream(credential, prepared, signal, conversationKey) {
|
|
1061
1132
|
let response;
|
|
1062
1133
|
try {
|
|
1063
1134
|
response = await this.fetchImpl(`${chatBase(credential)}/v2/chat/completions`, {
|
|
1064
1135
|
method: "POST",
|
|
1065
|
-
headers:
|
|
1066
|
-
...chatHeaders(credential),
|
|
1067
|
-
...CHAT_IDENTITY_HEADERS
|
|
1068
|
-
},
|
|
1136
|
+
headers: chatCompletionHeaders(credential, conversationKey),
|
|
1069
1137
|
body: prepared,
|
|
1070
1138
|
...signal === void 0 ? {} : { signal }
|
|
1071
1139
|
});
|
|
@@ -3501,8 +3569,17 @@ var WorkBuddyAccountPool = class {
|
|
|
3501
3569
|
});
|
|
3502
3570
|
}
|
|
3503
3571
|
}
|
|
3572
|
+
const byUid = /* @__PURE__ */ new Map();
|
|
3573
|
+
for (const account of byId.values()) {
|
|
3574
|
+
const uid = account.credential.uid;
|
|
3575
|
+
if (typeof uid === "string" && uid !== "" && !byUid.has(uid)) byUid.set(uid, account.id);
|
|
3576
|
+
}
|
|
3504
3577
|
for (const credential of found) {
|
|
3505
|
-
const
|
|
3578
|
+
const byIdentity = workbuddyAccountId(credential);
|
|
3579
|
+
const uid = credential.uid;
|
|
3580
|
+
const knownId = typeof uid === "string" && uid !== "" ? byUid.get(uid) : void 0;
|
|
3581
|
+
const id = knownId ?? byIdentity;
|
|
3582
|
+
if (knownId === void 0 && typeof uid === "string" && uid !== "") byUid.set(uid, id);
|
|
3506
3583
|
const existing = byId.get(id);
|
|
3507
3584
|
if (existing === void 0) {
|
|
3508
3585
|
byId.set(id, {
|
|
@@ -10043,9 +10120,22 @@ function stringList$2(value) {
|
|
|
10043
10120
|
}
|
|
10044
10121
|
return out;
|
|
10045
10122
|
}
|
|
10046
|
-
/**
|
|
10123
|
+
/**
|
|
10124
|
+
* The combo id a name slugifies to, or '' when the name cannot form one.
|
|
10125
|
+
*
|
|
10126
|
+
* Unicode-aware on purpose. This used to be `/[^a-z0-9]+/g`, which meant any
|
|
10127
|
+
* name that was not ASCII — every Chinese chain name — slugged to the empty
|
|
10128
|
+
* string, and `normalizeCombos` then dropped the entry with a bare `continue`.
|
|
10129
|
+
* The card reported "saved", the store held nothing, and the chain vanished on
|
|
10130
|
+
* the next read: the "new route cannot be saved" report. A name is user data and
|
|
10131
|
+
* may be written in any script, so the id has to survive one.
|
|
10132
|
+
*
|
|
10133
|
+
* Latin names keep the old shape (`writing-fallback`), because ids already
|
|
10134
|
+
* stored that way must keep resolving; only characters that used to be stripped
|
|
10135
|
+
* to nothing are now kept.
|
|
10136
|
+
*/
|
|
10047
10137
|
function comboId(name) {
|
|
10048
|
-
const slug = text$6(name).toLowerCase().replace(/[
|
|
10138
|
+
const slug = text$6(name).toLowerCase().replace(/[^\p{L}\p{N}]+/gu, "-").replace(/^-+|-+$/gu, "").slice(0, 48).replace(/-+$/u, "");
|
|
10049
10139
|
return slug === "" ? "" : `${COMBO_PREFIX}${slug}`;
|
|
10050
10140
|
}
|
|
10051
10141
|
/** True when the id names a combo row. */
|
|
@@ -10068,7 +10158,9 @@ function isComboRow(row) {
|
|
|
10068
10158
|
*
|
|
10069
10159
|
* A chain with no members is DROPPED rather than kept as an empty row: a model
|
|
10070
10160
|
* that cannot answer anything is not a model, and showing one in the picker
|
|
10071
|
-
* only produces a request that fails with nothing to fix.
|
|
10161
|
+
* only produces a request that fails with nothing to fix. The card warns about
|
|
10162
|
+
* that case before the save (`route.noMembers`), which is why dropping here is
|
|
10163
|
+
* not a silent loss.
|
|
10072
10164
|
*/
|
|
10073
10165
|
function normalizeCombos(raw) {
|
|
10074
10166
|
if (!Array.isArray(raw)) return [];
|
|
@@ -11596,7 +11688,6 @@ const NO_COST = {
|
|
|
11596
11688
|
cacheRead: 0,
|
|
11597
11689
|
cacheWrite: 0
|
|
11598
11690
|
};
|
|
11599
|
-
/** pi-ai input modalities: images only when the catalog advertises them. */
|
|
11600
11691
|
function modelInput(info) {
|
|
11601
11692
|
return info.supportsImages ? ["text", "image"] : ["text"];
|
|
11602
11693
|
}
|
|
@@ -11773,26 +11864,44 @@ function createWorkBuddyAdapter(options) {
|
|
|
11773
11864
|
if (aggregateCatalog) return [...catalog.providerRows().map((row) => toAggregateModel(row, baseUrl, providerId)), ...catalog.comboRows().map((row) => toComboModel(row, baseUrl, providerId))];
|
|
11774
11865
|
return catalog.visible().map((info) => toPiModel(info, baseUrl, providerId));
|
|
11775
11866
|
};
|
|
11776
|
-
|
|
11777
|
-
|
|
11778
|
-
|
|
11779
|
-
|
|
11780
|
-
|
|
11781
|
-
|
|
11782
|
-
|
|
11783
|
-
|
|
11784
|
-
|
|
11785
|
-
|
|
11786
|
-
|
|
11787
|
-
|
|
11788
|
-
|
|
11789
|
-
|
|
11790
|
-
|
|
11791
|
-
|
|
11792
|
-
|
|
11793
|
-
|
|
11867
|
+
/**
|
|
11868
|
+
* Build a fresh provider object whose `models` array is the catalog as it
|
|
11869
|
+
* stands right now.
|
|
11870
|
+
*
|
|
11871
|
+
* `createProvider` snapshots the array it is handed, and the host keeps its
|
|
11872
|
+
* own snapshot keyed by the IDENTITY of the `profiles` map
|
|
11873
|
+
* (`dsh-llm-pi-ai/lib/index.js` `PiAiAdapter.current()`: it rebuilds the
|
|
11874
|
+
* model collection only when `this.snapshot.profiles !== profiles`, then
|
|
11875
|
+
* calls `models.setProvider(profile.piProvider)`). Reusing one provider object
|
|
11876
|
+
* across `invalidate()` therefore could not work: the host would recognise the
|
|
11877
|
+
* unchanged profiles map, hand back its cached snapshot, and re-collect the
|
|
11878
|
+
* very same provider — so a model list that changed upstream (a new model
|
|
11879
|
+
* released, an account's entitlements changing) stayed frozen until restart.
|
|
11880
|
+
* That is what users saw on the international gateway. Returning a brand-new
|
|
11881
|
+
* provider from a brand-new profile is the only lever the adapter is given.
|
|
11882
|
+
*/
|
|
11883
|
+
const makeProvider = () => {
|
|
11884
|
+
return {
|
|
11885
|
+
...createProvider({
|
|
11886
|
+
id: providerId,
|
|
11887
|
+
name: displayName,
|
|
11888
|
+
auth: { apiKey: {
|
|
11889
|
+
name: "XD Pool loopback secret",
|
|
11890
|
+
async resolve({ credential }) {
|
|
11891
|
+
const apiKey = credential?.key;
|
|
11892
|
+
return apiKey === void 0 || apiKey.length === 0 ? void 0 : {
|
|
11893
|
+
auth: { apiKey },
|
|
11894
|
+
source: "XD Pool"
|
|
11895
|
+
};
|
|
11896
|
+
}
|
|
11897
|
+
} },
|
|
11898
|
+
models: buildModels(),
|
|
11899
|
+
api: openAICompletionsApi()
|
|
11900
|
+
}),
|
|
11901
|
+
getModels: () => buildModels()
|
|
11902
|
+
};
|
|
11794
11903
|
};
|
|
11795
|
-
const
|
|
11904
|
+
const makeProfile = (piProvider) => ({
|
|
11796
11905
|
provider: providerId,
|
|
11797
11906
|
displayName,
|
|
11798
11907
|
streamIdleTimeoutMs: WORKBUDDY_STREAM_IDLE_TIMEOUT_MS,
|
|
@@ -11800,8 +11909,9 @@ function createWorkBuddyAdapter(options) {
|
|
|
11800
11909
|
configuredMaxTokens: /* @__PURE__ */ new Map(),
|
|
11801
11910
|
modelErrors: /* @__PURE__ */ new Map(),
|
|
11802
11911
|
...REQUEST_IMAGE_BUDGETS,
|
|
11803
|
-
piProvider
|
|
11804
|
-
};
|
|
11912
|
+
piProvider
|
|
11913
|
+
});
|
|
11914
|
+
const profile = makeProfile(makeProvider());
|
|
11805
11915
|
let profiles = /* @__PURE__ */ new Map([[providerId, profile]]);
|
|
11806
11916
|
return {
|
|
11807
11917
|
providerId,
|
|
@@ -11815,7 +11925,7 @@ function createWorkBuddyAdapter(options) {
|
|
|
11815
11925
|
}),
|
|
11816
11926
|
buildModels,
|
|
11817
11927
|
invalidate: () => {
|
|
11818
|
-
profiles = /* @__PURE__ */ new Map([[providerId,
|
|
11928
|
+
profiles = /* @__PURE__ */ new Map([[providerId, makeProfile(makeProvider())]]);
|
|
11819
11929
|
}
|
|
11820
11930
|
};
|
|
11821
11931
|
}
|
|
@@ -11968,7 +12078,7 @@ function readPackAccountPool(ctx) {
|
|
|
11968
12078
|
*/
|
|
11969
12079
|
async function readPackCredentials(pool) {
|
|
11970
12080
|
if (pool === void 0) return [];
|
|
11971
|
-
const entries = pool.listAllAccounts().filter((entry) => {
|
|
12081
|
+
const entries = (await pool.listAllAccounts()).filter((entry) => {
|
|
11972
12082
|
const provider = text$3(entry.provider);
|
|
11973
12083
|
return provider !== void 0 && PACK_REGION_CHANNELS.includes(provider);
|
|
11974
12084
|
});
|
|
@@ -18204,6 +18314,7 @@ function createWorkBuddyShim(options) {
|
|
|
18204
18314
|
}
|
|
18205
18315
|
const attempts = [];
|
|
18206
18316
|
let last;
|
|
18317
|
+
const conversationKey = conversationKeyOf(raw);
|
|
18207
18318
|
for (const member of members) {
|
|
18208
18319
|
if (signal.aborted) return;
|
|
18209
18320
|
attempts.push(member.modelId);
|
|
@@ -18219,7 +18330,7 @@ function createWorkBuddyShim(options) {
|
|
|
18219
18330
|
continue;
|
|
18220
18331
|
}
|
|
18221
18332
|
const prepared = client.prepareChatBody(raw);
|
|
18222
|
-
const result = await client.chatStream(account.credential, prepared, signal);
|
|
18333
|
+
const result = await client.chatStream(account.credential, prepared, signal, conversationKey);
|
|
18223
18334
|
if (result.ok) {
|
|
18224
18335
|
const turnId = randomBytes(8).toString("hex");
|
|
18225
18336
|
await serveSuccessfulStream(res, account, result, logger, refreshBalance, pool, usageRecorder === void 0 ? void 0 : {
|
|
@@ -18381,7 +18492,7 @@ function createWorkBuddyShim(options) {
|
|
|
18381
18492
|
return;
|
|
18382
18493
|
}
|
|
18383
18494
|
tried.push(account.label);
|
|
18384
|
-
const result = await client.chatStream(account.credential, prepared, controller.signal);
|
|
18495
|
+
const result = await client.chatStream(account.credential, prepared, controller.signal, conversationKey);
|
|
18385
18496
|
if (result.ok) {
|
|
18386
18497
|
await serveSuccessfulStream(res, account, result, logger, refreshBalance, pool, usageContext);
|
|
18387
18498
|
return;
|
|
@@ -18640,7 +18751,7 @@ async function recoverFromContextOverrun(options) {
|
|
|
18640
18751
|
stream: true,
|
|
18641
18752
|
max_tokens: Math.max(256, Math.floor(budget / 2))
|
|
18642
18753
|
}, request);
|
|
18643
|
-
return await client.completeChat(summariser.credential, body, signal ?? controller.signal);
|
|
18754
|
+
return await client.completeChat(summariser.credential, body, signal ?? controller.signal, conversationKeyOf(raw));
|
|
18644
18755
|
} }, controller.signal);
|
|
18645
18756
|
compacted = outcome.messages;
|
|
18646
18757
|
summary = outcome.summary;
|
|
@@ -18667,7 +18778,7 @@ async function recoverFromContextOverrun(options) {
|
|
|
18667
18778
|
detail: "no account available after compaction"
|
|
18668
18779
|
};
|
|
18669
18780
|
tried.push(account.label);
|
|
18670
|
-
const result = await client.chatStream(account.credential, retryBody, controller.signal);
|
|
18781
|
+
const result = await client.chatStream(account.credential, retryBody, controller.signal, conversationKeyOf(raw));
|
|
18671
18782
|
if (!result.ok) usage?.recorder.record({
|
|
18672
18783
|
region: usage.region,
|
|
18673
18784
|
model: usage.model,
|
|
@@ -20415,6 +20526,19 @@ function apply(ctx, config = {}) {
|
|
|
20415
20526
|
adaptersByRegion.global.invalidate();
|
|
20416
20527
|
adaptersByRegion.aggregate.invalidate();
|
|
20417
20528
|
adaptersByRegion.free.invalidate();
|
|
20529
|
+
for (const [handle, routes] of [
|
|
20530
|
+
[releaseAdapterCn, [POOL_PROVIDER_BY_REGION.cn]],
|
|
20531
|
+
[releaseAdapterGlobal, [POOL_PROVIDER_BY_REGION.global]],
|
|
20532
|
+
[releaseAdapterAggregate, [XDPOOL_AGGREGATE_PROVIDER]],
|
|
20533
|
+
[releaseAdapterFree, [XDPOOL_FREE_PROVIDER]]
|
|
20534
|
+
]) {
|
|
20535
|
+
if (handle === void 0) continue;
|
|
20536
|
+
try {
|
|
20537
|
+
handle.replace([...routes]);
|
|
20538
|
+
} catch (error) {
|
|
20539
|
+
ctx.logger.warn("dsh-xdpool: could not re-announce adapter routes", error);
|
|
20540
|
+
}
|
|
20541
|
+
}
|
|
20418
20542
|
};
|
|
20419
20543
|
aggregateCatalog.onChange(() => {
|
|
20420
20544
|
invalidateCatalog();
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-xdpool",
|
|
3
3
|
"displayName": "DSH XD Pool",
|
|
4
4
|
"description": "One DSH model pool for every free quota you have: merge all locally signed-in WorkBuddy accounts with auto-failover, sign in to 13 vendors by QR/web, aggregate 76 vendors' free tiers, plus a usage dashboard, model routing, a local OpenAI-compatible gateway and a points-automation suite.",
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.4.0",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "XDTrees",
|
|
8
8
|
"homepage": "https://github.com/XDTrees/dsh-xdpool#readme",
|
|
@@ -34239,7 +34239,11 @@ var CODEBUDDY_FALLBACK_MODELS = [
|
|
|
34239
34239
|
name: "GLM-5.1",
|
|
34240
34240
|
contextWindow: 2e5,
|
|
34241
34241
|
supportsImages: true,
|
|
34242
|
-
|
|
34242
|
+
// Measured 2026-10-21 against the live gateway: all five levels answered
|
|
34243
|
+
// 200 with a distinct reasoning_content length per level. The single level
|
|
34244
|
+
// this used to declare was a guess, and it hid four working levels from
|
|
34245
|
+
// the picker.
|
|
34246
|
+
reasoningEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
34243
34247
|
maxOutputTokens: 48e3
|
|
34244
34248
|
},
|
|
34245
34249
|
{
|
|
@@ -34247,7 +34251,11 @@ var CODEBUDDY_FALLBACK_MODELS = [
|
|
|
34247
34251
|
name: "GLM-5V-Turbo",
|
|
34248
34252
|
contextWindow: 2e5,
|
|
34249
34253
|
supportsImages: true,
|
|
34250
|
-
|
|
34254
|
+
// Measured 2026-10-21 against the live gateway: all five levels answered
|
|
34255
|
+
// 200 with a distinct reasoning_content length per level. The single level
|
|
34256
|
+
// this used to declare was a guess, and it hid four working levels from
|
|
34257
|
+
// the picker.
|
|
34258
|
+
reasoningEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
34251
34259
|
maxOutputTokens: 64e3
|
|
34252
34260
|
},
|
|
34253
34261
|
{
|
|
@@ -34255,7 +34263,11 @@ var CODEBUDDY_FALLBACK_MODELS = [
|
|
|
34255
34263
|
name: "Kimi-K3-1",
|
|
34256
34264
|
contextWindow: 1e6,
|
|
34257
34265
|
supportsImages: true,
|
|
34258
|
-
|
|
34266
|
+
// Measured 2026-10-21 against the live gateway: all five levels answered
|
|
34267
|
+
// 200 with a distinct reasoning_content length per level. The single level
|
|
34268
|
+
// this used to declare was a guess, and it hid four working levels from
|
|
34269
|
+
// the picker.
|
|
34270
|
+
reasoningEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
34259
34271
|
maxOutputTokens: 32e3
|
|
34260
34272
|
},
|
|
34261
34273
|
{
|
|
@@ -34274,7 +34286,11 @@ var CODEBUDDY_FALLBACK_MODELS = [
|
|
|
34274
34286
|
name: "Kimi-K2.7",
|
|
34275
34287
|
contextWindow: 256e3,
|
|
34276
34288
|
supportsImages: true,
|
|
34277
|
-
|
|
34289
|
+
// Measured 2026-10-21 against the live gateway: all five levels answered
|
|
34290
|
+
// 200 with a distinct reasoning_content length per level. The single level
|
|
34291
|
+
// this used to declare was a guess, and it hid four working levels from
|
|
34292
|
+
// the picker.
|
|
34293
|
+
reasoningEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
34278
34294
|
maxOutputTokens: 32e3
|
|
34279
34295
|
},
|
|
34280
34296
|
{
|
|
@@ -34282,7 +34298,11 @@ var CODEBUDDY_FALLBACK_MODELS = [
|
|
|
34282
34298
|
name: "Kimi-K2.6",
|
|
34283
34299
|
contextWindow: 256e3,
|
|
34284
34300
|
supportsImages: true,
|
|
34285
|
-
|
|
34301
|
+
// Measured 2026-10-21 against the live gateway: all five levels answered
|
|
34302
|
+
// 200 with a distinct reasoning_content length per level. The single level
|
|
34303
|
+
// this used to declare was a guess, and it hid four working levels from
|
|
34304
|
+
// the picker.
|
|
34305
|
+
reasoningEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
34286
34306
|
maxOutputTokens: 32e3
|
|
34287
34307
|
},
|
|
34288
34308
|
{
|
|
@@ -34290,7 +34310,11 @@ var CODEBUDDY_FALLBACK_MODELS = [
|
|
|
34290
34310
|
name: "MiniMax-M3",
|
|
34291
34311
|
contextWindow: 512e3,
|
|
34292
34312
|
supportsImages: true,
|
|
34293
|
-
|
|
34313
|
+
// Measured 2026-10-21 against the live gateway: all five levels answered
|
|
34314
|
+
// 200 with a distinct reasoning_content length per level. The single level
|
|
34315
|
+
// this used to declare was a guess, and it hid four working levels from
|
|
34316
|
+
// the picker.
|
|
34317
|
+
reasoningEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
34294
34318
|
maxOutputTokens: 64e3
|
|
34295
34319
|
}
|
|
34296
34320
|
];
|
|
@@ -34316,8 +34340,8 @@ var WORKBUDDY_FALLBACK_MODELS = [
|
|
|
34316
34340
|
// (`node scripts/dump-max-output.mjs`)。该值就是用户在 IDE 里实际拿到的
|
|
34317
34341
|
// 单次输出额度,远端不可用时由本表顶替。远端未下发的模型保持 undefined。
|
|
34318
34342
|
{ id: "default-model", name: "Auto", contextWindow: 176e3, supportsImages: true, maxOutputTokens: 24e3 },
|
|
34319
|
-
{ id: "fast-model", name: "Fast", contextWindow: 2e5, supportsImages: true, reasoningEfforts: ["medium"], maxOutputTokens: 32e3 },
|
|
34320
|
-
{ id: "balanced-model", name: "Balanced", contextWindow: 256e3, supportsImages: true, reasoningEfforts: ["medium"], maxOutputTokens: 32e3 },
|
|
34343
|
+
{ id: "fast-model", name: "Fast", contextWindow: 2e5, supportsImages: true, reasoningEfforts: ["low", "medium", "high", "xhigh", "max"], maxOutputTokens: 32e3 },
|
|
34344
|
+
{ id: "balanced-model", name: "Balanced", contextWindow: 256e3, supportsImages: true, reasoningEfforts: ["low", "medium", "high", "xhigh", "max"], maxOutputTokens: 32e3 },
|
|
34321
34345
|
{ id: "primary-model", name: "Primary", contextWindow: 272e3, supportsImages: true, reasoningEfforts: ["high"], maxOutputTokens: 72e3 },
|
|
34322
34346
|
{ id: "deep-model", name: "Deep", contextWindow: 176e3, supportsImages: true, maxOutputTokens: 24e3 },
|
|
34323
34347
|
{
|
|
@@ -34349,14 +34373,14 @@ var WORKBUDDY_FALLBACK_MODELS = [
|
|
|
34349
34373
|
defaultReasoningEffort: "high",
|
|
34350
34374
|
maxOutputTokens: 64e3
|
|
34351
34375
|
},
|
|
34352
|
-
{ id: "deepseek-v4.1-flash", name: "Deepseek-V4.1-Flash", contextWindow: 1e6, supportsImages: true, reasoningEfforts: ["high"], defaultReasoningEffort: "high", maxOutputTokens: 128e3 },
|
|
34376
|
+
{ id: "deepseek-v4.1-flash", name: "Deepseek-V4.1-Flash", contextWindow: 1e6, supportsImages: true, reasoningEfforts: ["low", "medium", "high", "xhigh", "max"], defaultReasoningEffort: "high", maxOutputTokens: 128e3 },
|
|
34353
34377
|
{
|
|
34354
34378
|
// 2026-09 补录:新加坡区的同代模型(-sg 后缀),远端下发且实测能看图。
|
|
34355
34379
|
id: "deepseek-v4.1-flash-sg",
|
|
34356
34380
|
name: "Deepseek-V4.1-Flash",
|
|
34357
34381
|
contextWindow: 1e6,
|
|
34358
34382
|
supportsImages: true,
|
|
34359
|
-
reasoningEfforts: ["high"],
|
|
34383
|
+
reasoningEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
34360
34384
|
defaultReasoningEffort: "high",
|
|
34361
34385
|
maxOutputTokens: 128e3
|
|
34362
34386
|
},
|
|
@@ -34416,7 +34440,11 @@ var WORKBUDDY_FALLBACK_MODELS = [
|
|
|
34416
34440
|
},
|
|
34417
34441
|
// gpt-5.3-codex:远端未下发 maxOutputTokens,故不填(保持 undefined,
|
|
34418
34442
|
// 交由网关默认),不臆造数值。
|
|
34419
|
-
|
|
34443
|
+
// gpt-5.3-codex answered 200 at every level, but returned an EMPTY
|
|
34444
|
+
// reasoning_content at all five — the levels are legal yet not observably
|
|
34445
|
+
// distinct. Declaring the ladder states what may be sent; it does not
|
|
34446
|
+
// claim the model thinks.
|
|
34447
|
+
{ id: "gpt-5.3-codex", name: "GPT-5.3-Codex", contextWindow: 272e3, supportsImages: true, reasoningEfforts: ["low", "medium", "high", "xhigh", "max"] },
|
|
34420
34448
|
{ id: "gemini-3.5-flash", name: "Gemini-3.5-Flash", contextWindow: 1e6, supportsImages: true, reasoningEfforts: ["medium"], maxOutputTokens: 65536 },
|
|
34421
34449
|
{
|
|
34422
34450
|
id: "glm-5.3",
|
|
@@ -34436,7 +34464,7 @@ var WORKBUDDY_FALLBACK_MODELS = [
|
|
|
34436
34464
|
defaultReasoningEffort: "high",
|
|
34437
34465
|
maxOutputTokens: 48e3
|
|
34438
34466
|
},
|
|
34439
|
-
{ id: "kimi-k3", name: "Kimi-K3", contextWindow: 1e6, supportsImages: true, reasoningEfforts: ["medium"], maxOutputTokens: 32e3 },
|
|
34467
|
+
{ id: "kimi-k3", name: "Kimi-K3", contextWindow: 1e6, supportsImages: true, reasoningEfforts: ["low", "medium", "high", "xhigh", "max"], maxOutputTokens: 32e3 },
|
|
34440
34468
|
{
|
|
34441
34469
|
// 2026-09 补录:远端 /v3/config 的 cli agent 白名单里有它,兜底表原先漏了。
|
|
34442
34470
|
id: "kimi-k2.8-preview",
|
|
@@ -34447,7 +34475,7 @@ var WORKBUDDY_FALLBACK_MODELS = [
|
|
|
34447
34475
|
defaultReasoningEffort: "high",
|
|
34448
34476
|
maxOutputTokens: 32e3
|
|
34449
34477
|
},
|
|
34450
|
-
{ id: "kimi-k2.6", name: "Kimi-K2.6", contextWindow: 256e3, supportsImages: true, reasoningEfforts: ["medium"], maxOutputTokens: 32e3 }
|
|
34478
|
+
{ id: "kimi-k2.6", name: "Kimi-K2.6", contextWindow: 256e3, supportsImages: true, reasoningEfforts: ["low", "medium", "high", "xhigh", "max"], maxOutputTokens: 32e3 }
|
|
34451
34479
|
];
|
|
34452
34480
|
var WORKBUDDY = {
|
|
34453
34481
|
id: "workbuddy",
|