@eddyskywalker/dsh-chatgpt-subscription 0.39.2 → 0.40.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 +18 -0
- package/README.md +33 -1
- package/lib/client.js +301 -0
- package/lib/client.js.map +1 -1
- package/lib/index.js +345 -9
- package/lib/types/client/CompactionRouteBundleConfig.d.ts +19 -0
- package/lib/types/client/CompactionRouteBundleConfig.d.ts.map +1 -0
- package/lib/types/client/CompactionRouteCard.d.ts +43 -0
- package/lib/types/client/CompactionRouteCard.d.ts.map +1 -0
- package/lib/types/client/api.d.ts +3 -1
- package/lib/types/client/api.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/locales-ru/hub.d.ts.map +1 -1
- package/lib/types/client/locales.d.ts +27 -1
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/host/agent-preset.d.ts.map +1 -1
- package/lib/types/host/compaction-route/index.d.ts +132 -0
- package/lib/types/host/compaction-route/index.d.ts.map +1 -0
- package/lib/types/host/preferences.d.ts.map +1 -1
- package/lib/types/host/routes.d.ts.map +1 -1
- package/lib/types/host/subagent-model-authorization.d.ts +53 -1
- package/lib/types/host/subagent-model-authorization.d.ts.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/shared/compaction-route.d.ts +63 -0
- package/lib/types/shared/compaction-route.d.ts.map +1 -0
- package/lib/types/shared/contracts.d.ts +26 -0
- package/lib/types/shared/contracts.d.ts.map +1 -1
- package/lib/types/shared/preferences.d.ts +2 -0
- package/lib/types/shared/preferences.d.ts.map +1 -1
- package/package.json +1 -1
- package/presets/dispatch/README.md +8 -2
- package/presets/dispatch/agent.cordis.yml +11 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.40.0 - 2026-10-25
|
|
6
|
+
|
|
7
|
+
- **[compaction] 新增「压缩路由」:把上下文压缩的摘要请求改派到指定 provider/model**
|
|
8
|
+
- **为什么在宿主层改道**:自动压缩的摘要默认用对话自身的模型生成,而「换模型」在宿主里属于 preset 的压缩配置——那份配置归 preset 所有,本插件不能把它变成设置项。因此改为在 `llm/stream` 瀑布层按 `purpose: 'compaction'` 识别并改派,一处生效即覆盖所有 preset,而不只是本插件自带的那一份。
|
|
9
|
+
- **三条行为约束**:① **发之前先拒** —— 目标模型不接受视频、而待压上下文里含视频块时,请求先被拦下、仍由对话模型执行,避免先付费再失败;② **先缓冲再回退** —— 替代线路的摘要先收集完整,只有完整的一份才下发,半截摘要一律丢弃并回退到对话模型,同时记录这次回退(运维看不见的回退就是没人同意的成本);③ **不施加输出上限** —— 宿主默认索要 65 536 token 的摘要,而官方客户端根本不传上限,改派时该上限被去掉,交由线路自己决定。
|
|
10
|
+
- **设置面**:新增选择卡与只读结果(`CompactionRouteCard` / `CompactionRouteBundleConfig`),目录来自运行时的 `llm.listProviders` / `llm.listModels` 且**每次请求现读**——加载时未登录、之后才登录的线路仍然会出现;单条线路未登录或超时只会让该线路缺席,不会把整张列表清空。偏好存成单个对象(无目标 = 一个缺失值,而不是两个可能互相矛盾的字符串),目标为 `null` 时路由完全不介入。
|
|
11
|
+
- **端点**:新增 `GET …/compaction/candidates`,返回可选的 provider/model 目录(含各模型的输入模态)。
|
|
12
|
+
- **测试**:新增 `test/compaction-route.test.ts`、`test/compaction-route-card.test.tsx`、`test/compaction-route-bundle-config.test.tsx`,并扩充 `test/preferences.test.ts` 与 `test/client-registration.test.ts`。
|
|
13
|
+
|
|
14
|
+
- **[dispatch] 修好「配置了子代理却派不出子代理」的死锁:preset 用着 harness 已改名的旧键,守卫再补上按工具实际参数的自适应判定**
|
|
15
|
+
- **现象**:在开了「设置 → 子代理 → 允许 Agent 为 Subagent 选择模型」的机器上,调度模式(dispatch preset)无论怎么写都被拒:不写 `provider`/`model` → 守卫回「this Session requires an explicit child model」并点名父路由;写了 → `tool-subagent` 回 `child model selection is disabled for this tool instance`;而 `list_subagent_models` 在 PTC 的 `run_code` 里根本不是函数,连授权清单都取不到。三个出口同时堵死。
|
|
16
|
+
- **根因(一处配置 + 一处假设)**:harness **0.2.1-rc.1** 把 `@deepseek-ai/dsh-tool-subagent` 的 `modelSelectionSettings` 拆成了两个键——`modelSelection`(才暴露 `provider`/`model`/`reasoning_effort`)与 `modelDiscovery`(才注册 `list_subagent_models`),两者默认都 false 且不再互相隐含。本插件的 dispatch 模板与 bundled preset 仍写旧键,在被测安装(`dsh-tool-subagent@0.2.1-rc.3`)里该键**整个包里 0 命中**,于是被配置层丢弃、两个开关同时留在关闭态:工具既没有路由参数、也没有发现工具。而守卫把它记在 explicit 模式下(`DEFAULT_DELEGATION_TOOLS`),对未写明成对路由的调用一律拒绝——它无从知道「这个工具压根没有那两个参数」,因为这个模式来自配置而不是工具。
|
|
17
|
+
- **改法(配置,三处同步)**:`presets/dispatch/agent.cordis.yml`、模板 `src/host/agent-preset.ts`、以及已安装的 `~/.dsh/.agent-presets/dispatch/agent.cordis.yml` 都把 `modelSelectionSettings: true` 换成 `modelSelection: true` + `modelDiscovery: true`,并在文件头注释里记下这次改名与「只写一个就会变成哑的」。因为 `syncOnePreset` 是内容比对(`src/host/preset-sync.ts:345-351`),下次 Host 启动会把旧安装的 preset 自动刷成正确版本,不需要单独的迁移代码。
|
|
18
|
+
- **改法(守卫自适应,`src/host/subagent-model-authorization.ts`)**:新增 `RouteParameterProbe` 与 `routeParameterProbe(ctx)`,在拒绝前通过宿主的 `tools.definitions(agent)` 读一次该工具当前参数表。**没有 `provider`/`model` → 按 inherit 模式校验它实际继承到的路由**(该拒绝照样拒绝,只是理由变成模型能照做的:`inheritRouteDenialReason`),完整成对路由 → `inheritOverrideReason`,只给一半 → 新增 `routeFieldsUnavailableReason`(明说这是挂载配置问题、不是参数问题,并给出会发生什么);**有 → 维持 explicit 模式**;注册表读不到定义或该 scope 抛错 → 退回配置模式,不退化成放行。inherited 校验抽成 `inheritedRouteDenial` 供两条分支共用,避免两份实现漂移。
|
|
19
|
+
- **测试**:`test/subagent-model-authorization.test.ts` 由 27 例增至 32 例,新增 `route-parameter adaptivity` 五条——无路由参数时按继承路由拒绝且理由不含「requires an explicit child model」、继承路由合法时放行、成对与半对路由各自给出对应理由、注册表答不出/声明有路由时维持 explicit、以及 `routeParameterProbe` 对参数表、缺失工具、无注册表、抛错 scope 的四种读法。`test/agent-preset.test.ts` 的行断言改为 `{ modelSelection: true, modelDiscovery: true }`。
|
|
20
|
+
- **文档**:根 `README.md` 补「自适应」一段并把 preset 键名改对;`presets/dispatch/README.md` 的「依赖的设置」补上「两个键必须成对写入、漏一个会让本模式变哑」。
|
|
21
|
+
- **补一条被漏掉的事实(R2 与 README)**:`subagent_fork` 的准入判据是**父子代理实际在跑的路由是否在允许清单内**(`inheritedRouteOf` 取会话最新请求头),与 `list_subagent_models` 是否存在、委派工具是否暴露 `provider`/`model` **都无关**。这意味着上面那个死锁状态下 fork 一直可用,而本轮最初的判断「派不出子代理」把 spawn 的故障扩大成了全部委派失效。R2 两份拷贝同字节补三句:父路由已授权时 fork 无需路由参数与发现工具、该优先用 fork 的条件(需要本对话上下文)与不该用的条件(要换模型或要干净上下文)、以及**不得从一条坏掉的显式路由路径推出「无法委派」——先试一次 fork,拒绝理由会写出父路由与授权清单**。根 `README.md` 的 inherit 段补一张三态表。测试:`test/agent-preset.test.ts` 的 R2 用例新增三条断言钉住这三句。
|
|
22
|
+
|
|
5
23
|
## 0.39.2 - 2026-10-24
|
|
6
24
|
- **[codex] 账号卡登录行不再换行:三个入口改用短标签,完整语义留在 aria-label,窄宽度下省略而不是折行**
|
|
7
25
|
- **现象**:ChatGPT 订阅页账号卡右上角那一行有三个按钮(添加账号 / 用设备代码添加账号 / 导入本机 Codex CLI 登录),在正常设置面板宽度下第三个被挤到下一行,右边还被裁掉一截。
|
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
- [号池存储后端(`poolStorage`)](#号池存储后端poolstorage)
|
|
14
14
|
- [安装](#安装)
|
|
15
15
|
- [使用](#使用)
|
|
16
|
+
- [压缩模型(可选,默认关闭)](#压缩模型可选默认关闭)
|
|
16
17
|
- [推理塌缩守卫(熔断,默认关闭)](#推理塌缩守卫熔断默认关闭)
|
|
17
18
|
- [Command Code 线路](#command-code-线路)
|
|
18
19
|
- [Claude(订阅)线路](#claude订阅线路)
|
|
@@ -555,6 +556,18 @@ DSH 设置页的「Subagent」卡片会把勾选的模型写成会话级的允
|
|
|
555
556
|
|
|
556
557
|
**inherit 模式(`subagentModelInheritTools`,默认 `['subagent_fork', 'spawn_teammate']`)**:有些委派工具**按设计**就没有路由参数,子代理必然跑在调用者自己的路由上——`subagent_fork`(复用对话与 KV Cache)与 Agent Teams 的 `spawn_teammate`(成员一律继承 Lead 的路由)都是这一类。「工具不能选」不等于「这个选择被授权」,所以守卫改为校验它**实际继承到的路由**:允许清单内有该路由就放行,没有就拒绝,并说明该工具无法改路由、要么改用能显式指定路由的 `subagent`、要么先把本会话切到清单内的模型。设为 `[]` 可关闭 inherit 模式的全部校验。
|
|
557
558
|
|
|
559
|
+
**fork 的准入与发现工具无关**,这是它最重要的性质:`subagent_fork` 不需要 `provider`/`model`,也不需要 `list_subagent_models`,判据只有「调用者当前实际在跑的路由是否在允许清单内」(取会话最新一次请求头,中途换模型判定随之更新)。
|
|
560
|
+
|
|
561
|
+
| 会话状态 | `subagent_fork` | 说明 |
|
|
562
|
+
|---|---|---|
|
|
563
|
+
| 没有记录允许清单 | 可用,且不校验 | `allowed.length === 0` 时守卫直接放行 |
|
|
564
|
+
| 有清单,父路由在清单内 | 可用 | 主代理本身就是被勾选的模型即为这一类 |
|
|
565
|
+
| 有清单,父路由不在清单内 | 拒绝,且不可自救 | 换 `subagent` 显式指定清单内路由,或先把会话切到清单内模型 |
|
|
566
|
+
|
|
567
|
+
因此「发现工具缺失」或「委派工具不暴露路由参数」都**不构成**派不出子代理的理由:先试一次 fork,拒绝理由会直接写出父路由与授权清单。反过来,fork 也不该被当成通用更优解——它不能换模型,且继承整段已完成的轮次,只适合需要主代理上下文的复审与接续。
|
|
568
|
+
|
|
569
|
+
**自适应:工具到底有没有路由参数,是运行时读出来的,不是配置里假设的。** 上面两种模式是配置,而工具的参数是事实——harness 改键、或 preset 写错一个键,都会让一个被当作 explicit 的工具根本没有 `provider`/`model`(典型是 0.2.1-rc.1 把 `modelSelectionSettings` 拆成 `modelSelection` + `modelDiscovery` 之后,仍带旧键的 preset)。此时若继续要求显式路由,模型拿到的理由无法照做,而工具自己还会抛 `child model selection is disabled for this tool instance`。守卫因此会在拒绝前通过宿主工具注册表的 `definitions(agent)` 读一次该工具的参数表:**没有路由参数就按 inherit 模式校验它实际继承到的路由**(该拒绝照样拒绝,只是理由变成可执行的),**有就维持 explicit 模式**;注册表读不到定义时退回配置模式,不退化成放行。
|
|
570
|
+
|
|
558
571
|
守卫装在 DSH 工具注册表的调度入口上,因此 **`run_code`(代码模式 / PTC)里通过 SDK 调用的 `tools["subagent"]` 同样被拦截**:该子调用走的是与直接调用相同的 `prepare → guard → dispatch` 流水线,拒绝理由以 `ToolCallError` 抛回程序。也就是说模型无法靠把委派写进代码里绕过白名单(`test/subagent-model-authorization-ptc.test.ts` 用真实 `ToolRuntime` + 假 code runtime 验证了放行、缺省拒绝与越权拒绝三条路径)。
|
|
559
572
|
|
|
560
573
|
配置项(插件行 `config`,全部可省略):
|
|
@@ -570,7 +583,7 @@ DSH 设置页的「Subagent」卡片会把勾选的模型写成会话级的允
|
|
|
570
583
|
|
|
571
584
|
改动只在设置卡片里保存过的勾选生效:设置改动只影响之后新建的会话(DSH 的会话快照语义),已运行的会话继续使用它自己记录的那份列表。
|
|
572
585
|
|
|
573
|
-
> **调度模式如何知道自己是哪种姿态。** `list_subagent_models` 与 `provider`/`model` 参数由 preset 行静态挂载(`
|
|
586
|
+
> **调度模式如何知道自己是哪种姿态。** `list_subagent_models` 与 `provider`/`model` 参数由 preset 行静态挂载(`modelSelection: true` + `modelDiscovery: true`),所以**工具在场不等于校验生效**——真正决定校验的是会话记录的那份允许列表,没有记录时守卫对一切放行。为了让模型**读到**这个事实而不是从「工具在」推断出「已生效」,`list_subagent_models` 的返回被 `tools/post-execute` 原地补一段:有列表时附上本会话全部 `{ provider, model }` 拆解对,没有列表时附上 `UNRESTRICTED_ROUTING_NOTICE`(写明「本会话没有允许清单生效、路由不受限、省略路由是正当的」)。调度模式的 R2 据此分成三态——`[State: Listed]` / `[State: Unlisted]` / `[State: Unmounted]`,只有 `[State: Unmounted]` 能作为 R8 fallback 的理由。
|
|
574
587
|
|
|
575
588
|
> 升级提示:从 0.3.2 起,带有允许列表的会话里**任何**未写明 `provider` + `model` 的委派都会被拒绝(此前只有写明且不在列表内的路由会被拒绝)。这是有意的行为变更——它消除了「子代理悄悄继承主代理模型」的漏洞;把 `subagentModelAuthorization` 设为 `false` 可回到 DSH 原始行为。
|
|
576
589
|
|
|
@@ -589,6 +602,25 @@ DSH 设置页的「Subagent」卡片会把勾选的模型写成会话级的允
|
|
|
589
602
|
|
|
590
603
|
**区域**:默认使用中国大陆主机(`auth.kimi.com` / `api.kimi.com`)。国际账号可设置 `DSH_KIMI_CODE_OAUTH_HOST=https://auth.kimi.ai` 与 `DSH_KIMI_CODE_BASE_URL=https://api.kimi.ai/coding` 后重新登录;环境变量同时会钉住区域,设置页会显示当前解析到的主机。
|
|
591
604
|
|
|
605
|
+
## 压缩模型(可选,默认关闭)
|
|
606
|
+
|
|
607
|
+
上下文压缩的**摘要**默认由会话自己的模型写,也就继续消耗那条线路的额度。想在压缩时改用另一个更快/更便宜的模型,用设置页的「压缩模型」卡片;留空即跟随会话模型,路由完全不动。
|
|
608
|
+
|
|
609
|
+
**为什么不直接在宿主里配。** 在 DSH 里「压缩用哪个模型」属于 preset 的压缩配置,那份配置随 preset 分发、不是本插件能暴露的设置项。所以本插件改在 `llm/stream` 瀑布层按 `purpose: 'compaction'` 识别摘要调用再改派——一处生效即覆盖**所有** preset,而不只是本插件自带的那一份。
|
|
610
|
+
|
|
611
|
+
**四条刻意的行为:**
|
|
612
|
+
|
|
613
|
+
| 行为 | 原因 |
|
|
614
|
+
|---|---|
|
|
615
|
+
| 目标模型不接受**视频**、而待压上下文里有视频块时**不发送**,仍由会话模型完成压缩 | 视频块在没有视频能力的线路上没有 wire 兜底,发出去只会先付费再失败 |
|
|
616
|
+
| 目标模型不接受**图片**时,摘要里的截图被替换为占位文本 | 摘要本身不需要像素,替换比整段回退更省 |
|
|
617
|
+
| 替代线路的摘要**先收完整再下发**,半截摘要丢弃并回退会话模型 | 宿主会把适配器异常变成终止 `finish` 块而不是抛错,半截摘要没有价值 |
|
|
618
|
+
| 改派时**去掉宿主默认的输出上限**(65 536 token) | 官方客户端不传上限,交给线路自己决定 |
|
|
619
|
+
|
|
620
|
+
每次回退都会记日志——看不见的回退就是没人同意的成本。
|
|
621
|
+
|
|
622
|
+
**选择列表**来自运行时目录且**每次请求现读**:加载页面时未登录、之后才登录的线路照样会出现;单条线路未登录或超时只会让该线路缺席,不会把整张列表清空。
|
|
623
|
+
|
|
592
624
|
## 推理塌缩守卫(熔断,默认关闭)
|
|
593
625
|
|
|
594
626
|
本线的“熔断器”就是**推理塌缩守卫**(`src/host/reasoning-collapse-guard/index.ts`)。一个长推理流可能退化成复读——反复绕过同一小撮短语、既不调工具也不收尾,一直到输出上限把这一轮截断(曾经有一次会话就这样烧掉 128,000 输出 token 并返回空答案)。守卫订阅 `llm/stream` 瀑布,对**尾部推理窗口**同时算两个信号:
|
package/lib/client.js
CHANGED
|
@@ -930,6 +930,10 @@ window.__ModuleLoader__.load({
|
|
|
930
930
|
updatePreferences(patch) {
|
|
931
931
|
return post$5(`${ROUTE_PREFIX}/preferences/update`, patch);
|
|
932
932
|
}
|
|
933
|
+
/** Every route a compaction summary may be pointed at, read per call. */
|
|
934
|
+
compactionCandidates() {
|
|
935
|
+
return post$5(`${ROUTE_PREFIX}/compaction/candidates`, {});
|
|
936
|
+
}
|
|
933
937
|
events(loginId) {
|
|
934
938
|
return new EventSource(`${ROUTE_PREFIX}/login/events?loginId=${encodeURIComponent(loginId)}`);
|
|
935
939
|
}
|
|
@@ -18563,6 +18567,259 @@ window.__ModuleLoader__.load({
|
|
|
18563
18567
|
});
|
|
18564
18568
|
}
|
|
18565
18569
|
//#endregion
|
|
18570
|
+
//#region src/client/CompactionRouteCard.tsx
|
|
18571
|
+
/**
|
|
18572
|
+
* Choose the model an automatic compaction summarizes on.
|
|
18573
|
+
*
|
|
18574
|
+
* The route lives in this plugin's preferences and is applied by the host's
|
|
18575
|
+
* `llm/stream` listener, so a saved choice takes effect on the next compaction
|
|
18576
|
+
* without a restart. Both selects are needed together: a model id alone belongs
|
|
18577
|
+
* to no route.
|
|
18578
|
+
*
|
|
18579
|
+
* The picker loads its directory on demand rather than on mount: opening the
|
|
18580
|
+
* settings page must not cost a request to every line (that page is opened for
|
|
18581
|
+
* unrelated reasons all the time), and the card already knows the saved route
|
|
18582
|
+
* from the status it was rendered with.
|
|
18583
|
+
*/
|
|
18584
|
+
function CompactionRouteCard({ api, preferences, disabled = false, labels, onSaved }) {
|
|
18585
|
+
const [picking, setPicking] = (0, react.useState)(false);
|
|
18586
|
+
const [candidates, setCandidates] = (0, react.useState)(null);
|
|
18587
|
+
const [loadError, setLoadError] = (0, react.useState)(null);
|
|
18588
|
+
const [provider, setProvider] = (0, react.useState)("");
|
|
18589
|
+
const [model, setModel] = (0, react.useState)("");
|
|
18590
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
18591
|
+
const [notice, setNotice] = (0, react.useState)(null);
|
|
18592
|
+
const [error, setError] = (0, react.useState)(null);
|
|
18593
|
+
const load = (0, react.useCallback)(async () => {
|
|
18594
|
+
try {
|
|
18595
|
+
const value = await api.compactionCandidates();
|
|
18596
|
+
setCandidates(value);
|
|
18597
|
+
setLoadError(null);
|
|
18598
|
+
} catch (cause) {
|
|
18599
|
+
setLoadError(cause instanceof Error ? cause.message : String(cause));
|
|
18600
|
+
}
|
|
18601
|
+
}, [api]);
|
|
18602
|
+
(0, react.useEffect)(() => {
|
|
18603
|
+
if (picking && candidates === null) load();
|
|
18604
|
+
}, [
|
|
18605
|
+
picking,
|
|
18606
|
+
candidates,
|
|
18607
|
+
load
|
|
18608
|
+
]);
|
|
18609
|
+
(0, react.useEffect)(() => {
|
|
18610
|
+
const saved = preferences?.compactionRoute ?? null;
|
|
18611
|
+
if (saved === null) return;
|
|
18612
|
+
setProvider((current) => current === "" ? saved.provider : current);
|
|
18613
|
+
setModel((current) => current === "" ? saved.model : current);
|
|
18614
|
+
}, [preferences?.compactionRoute]);
|
|
18615
|
+
const providers = candidates?.providers ?? [];
|
|
18616
|
+
const models = providers.find((entry) => entry.id === provider)?.models ?? [];
|
|
18617
|
+
const selected = models.find((entry) => entry.id === model);
|
|
18618
|
+
const savedProvider = preferences?.compactionRoute?.provider ?? "";
|
|
18619
|
+
const savedModel = preferences?.compactionRoute?.model ?? "";
|
|
18620
|
+
const canSave = provider !== "" && model !== "" && (provider !== savedProvider || model !== savedModel) && !busy && !disabled;
|
|
18621
|
+
const providerOptions = savedProvider === "" || providers.some((entry) => entry.id === savedProvider) ? providers : [{
|
|
18622
|
+
id: savedProvider,
|
|
18623
|
+
name: savedProvider
|
|
18624
|
+
}, ...providers];
|
|
18625
|
+
const modelOptions = model !== "" && !models.some((entry) => entry.id === model) ? [{
|
|
18626
|
+
id: model,
|
|
18627
|
+
name: model,
|
|
18628
|
+
modalities: []
|
|
18629
|
+
}, ...models] : models;
|
|
18630
|
+
const currentLabel = savedProvider === "" ? labels.follow : `${savedProvider} / ${savedModel}`;
|
|
18631
|
+
const save = async (target) => {
|
|
18632
|
+
setBusy(true);
|
|
18633
|
+
setError(null);
|
|
18634
|
+
setNotice(null);
|
|
18635
|
+
try {
|
|
18636
|
+
onSaved(await api.updatePreferences({ compactionRoute: target }));
|
|
18637
|
+
setNotice(labels.saved);
|
|
18638
|
+
setPicking(false);
|
|
18639
|
+
} catch (cause) {
|
|
18640
|
+
setError(cause instanceof Error ? cause.message : String(cause));
|
|
18641
|
+
} finally {
|
|
18642
|
+
setBusy(false);
|
|
18643
|
+
}
|
|
18644
|
+
};
|
|
18645
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
18646
|
+
className: "dsha-group",
|
|
18647
|
+
children: [
|
|
18648
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
18649
|
+
className: "dsha-grouphead",
|
|
18650
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: labels.title })
|
|
18651
|
+
}),
|
|
18652
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
18653
|
+
className: "dsha-muted",
|
|
18654
|
+
children: labels.hint
|
|
18655
|
+
}),
|
|
18656
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
18657
|
+
className: "dsha-pref-row",
|
|
18658
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: currentLabel }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
18659
|
+
className: "dsha-btn",
|
|
18660
|
+
type: "button",
|
|
18661
|
+
disabled: busy || disabled,
|
|
18662
|
+
onClick: () => setPicking((current) => !current),
|
|
18663
|
+
children: labels.change
|
|
18664
|
+
})]
|
|
18665
|
+
}),
|
|
18666
|
+
picking ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
18667
|
+
loadError !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
18668
|
+
className: "dsh-codex-error",
|
|
18669
|
+
role: "alert",
|
|
18670
|
+
children: loadError
|
|
18671
|
+
}) : null,
|
|
18672
|
+
candidates !== null && providers.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
18673
|
+
className: "dsh-codex-empty",
|
|
18674
|
+
children: labels.empty
|
|
18675
|
+
}) : null,
|
|
18676
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
18677
|
+
className: "dsha-pref-row",
|
|
18678
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: labels.provider }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
18679
|
+
className: "dsha-select",
|
|
18680
|
+
"aria-label": labels.provider,
|
|
18681
|
+
value: provider,
|
|
18682
|
+
disabled: busy || disabled,
|
|
18683
|
+
onChange: (event) => {
|
|
18684
|
+
setProvider(event.currentTarget.value);
|
|
18685
|
+
setModel("");
|
|
18686
|
+
},
|
|
18687
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
18688
|
+
value: "",
|
|
18689
|
+
children: "—"
|
|
18690
|
+
}), providerOptions.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
18691
|
+
value: entry.id,
|
|
18692
|
+
children: entry.name
|
|
18693
|
+
}, entry.id))]
|
|
18694
|
+
})]
|
|
18695
|
+
}),
|
|
18696
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
18697
|
+
className: "dsha-pref-row",
|
|
18698
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: labels.model }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
18699
|
+
className: "dsha-select",
|
|
18700
|
+
"aria-label": labels.model,
|
|
18701
|
+
value: model,
|
|
18702
|
+
disabled: busy || disabled || provider === "",
|
|
18703
|
+
onChange: (event) => setModel(event.currentTarget.value),
|
|
18704
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
18705
|
+
value: "",
|
|
18706
|
+
children: "—"
|
|
18707
|
+
}), modelOptions.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
18708
|
+
value: entry.id,
|
|
18709
|
+
children: entry.name
|
|
18710
|
+
}, entry.id))]
|
|
18711
|
+
})]
|
|
18712
|
+
}),
|
|
18713
|
+
selected !== void 0 && selected.modalities.length > 0 && !selected.modalities.includes("video") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
18714
|
+
className: "dsh-codex-warning",
|
|
18715
|
+
children: labels.noVideo
|
|
18716
|
+
}) : null,
|
|
18717
|
+
selected !== void 0 && selected.modalities.length > 0 && !selected.modalities.includes("image") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
18718
|
+
className: "dsh-codex-warning",
|
|
18719
|
+
children: labels.noImage
|
|
18720
|
+
}) : null,
|
|
18721
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
18722
|
+
className: "dsha-actions",
|
|
18723
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
18724
|
+
className: "dsha-btn dsha-btn-primary",
|
|
18725
|
+
type: "button",
|
|
18726
|
+
disabled: !canSave,
|
|
18727
|
+
onClick: () => void save({
|
|
18728
|
+
provider,
|
|
18729
|
+
model
|
|
18730
|
+
}),
|
|
18731
|
+
children: labels.save
|
|
18732
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
18733
|
+
className: "dsha-btn",
|
|
18734
|
+
type: "button",
|
|
18735
|
+
disabled: busy || disabled || savedProvider === "",
|
|
18736
|
+
onClick: () => void save(null),
|
|
18737
|
+
children: labels.clear
|
|
18738
|
+
})]
|
|
18739
|
+
})
|
|
18740
|
+
] }) : null,
|
|
18741
|
+
notice !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
18742
|
+
className: "dsh-codex-success",
|
|
18743
|
+
role: "status",
|
|
18744
|
+
children: notice
|
|
18745
|
+
}) : null,
|
|
18746
|
+
error !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
18747
|
+
className: "dsh-codex-error",
|
|
18748
|
+
role: "alert",
|
|
18749
|
+
children: [
|
|
18750
|
+
labels.failed,
|
|
18751
|
+
": ",
|
|
18752
|
+
error
|
|
18753
|
+
]
|
|
18754
|
+
}) : null
|
|
18755
|
+
]
|
|
18756
|
+
});
|
|
18757
|
+
}
|
|
18758
|
+
//#endregion
|
|
18759
|
+
//#region src/client/CompactionRouteBundleConfig.tsx
|
|
18760
|
+
/**
|
|
18761
|
+
* Compaction-model settings on this plugin's own page in the DSH plugin
|
|
18762
|
+
* manager.
|
|
18763
|
+
*
|
|
18764
|
+
* The setting is cross-line, so it does not belong inside any one provider's
|
|
18765
|
+
* tab: the plugin's page is where an operator configures *this plugin*, and the
|
|
18766
|
+
* bundle-config slot places it between the package description and the rows the
|
|
18767
|
+
* bundle declares.
|
|
18768
|
+
*
|
|
18769
|
+
* The slot is keyed by the bundle's package name, so the page dispatches it for
|
|
18770
|
+
* this package only — no subject filter is needed. It also renders its
|
|
18771
|
+
* occupants in the `summary` view (a one-liner beside the card); bundle
|
|
18772
|
+
* configuration has nothing to say there and renders null.
|
|
18773
|
+
*
|
|
18774
|
+
* @module dsh-chatgpt-subscription/compaction-route-bundle-config
|
|
18775
|
+
*/
|
|
18776
|
+
/** The package whose page this entry configures. */
|
|
18777
|
+
const COMPACTION_ROUTE_PACKAGE = "@eddyskywalker/dsh-chatgpt-subscription";
|
|
18778
|
+
/**
|
|
18779
|
+
* Render the compaction-model card on this package's bundle page only.
|
|
18780
|
+
*
|
|
18781
|
+
* Split in two so the hooks below never mount for the summary view: that view
|
|
18782
|
+
* is a one-liner drawn beside the card, and fetching this plugin's preferences
|
|
18783
|
+
* for it would be a request nobody asked for.
|
|
18784
|
+
*/
|
|
18785
|
+
function CompactionRouteBundleConfig(props) {
|
|
18786
|
+
if (props.view !== "page") return null;
|
|
18787
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OwnedSection, { t: props.t });
|
|
18788
|
+
}
|
|
18789
|
+
function OwnedSection({ t }) {
|
|
18790
|
+
const [preferences, setPreferences] = (0, react.useState)(null);
|
|
18791
|
+
const [api] = (0, react.useState)(() => new SubscriptionApi());
|
|
18792
|
+
(0, react.useEffect)(() => {
|
|
18793
|
+
let live = true;
|
|
18794
|
+
api.status().then((status) => {
|
|
18795
|
+
if (live) setPreferences(status.preferences);
|
|
18796
|
+
}).catch(() => void 0);
|
|
18797
|
+
return () => {
|
|
18798
|
+
live = false;
|
|
18799
|
+
};
|
|
18800
|
+
}, [api]);
|
|
18801
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CompactionRouteCard, {
|
|
18802
|
+
api,
|
|
18803
|
+
preferences,
|
|
18804
|
+
labels: {
|
|
18805
|
+
title: t("compactionRoute"),
|
|
18806
|
+
hint: t("compactionRouteHint"),
|
|
18807
|
+
provider: t("compactionRouteProvider"),
|
|
18808
|
+
model: t("compactionRouteModel"),
|
|
18809
|
+
save: t("compactionRouteSave"),
|
|
18810
|
+
clear: t("compactionRouteClear"),
|
|
18811
|
+
change: t("compactionRouteChange"),
|
|
18812
|
+
follow: t("compactionRouteFollow"),
|
|
18813
|
+
saved: t("compactionRouteSaved"),
|
|
18814
|
+
failed: t("compactionRouteFailed"),
|
|
18815
|
+
noVideo: t("compactionRouteNoVideo"),
|
|
18816
|
+
noImage: t("compactionRouteNoImage"),
|
|
18817
|
+
empty: t("compactionRouteEmpty")
|
|
18818
|
+
},
|
|
18819
|
+
onSaved: setPreferences
|
|
18820
|
+
});
|
|
18821
|
+
}
|
|
18822
|
+
//#endregion
|
|
18566
18823
|
//#region src/client/locales.ts
|
|
18567
18824
|
const NS = "dsh-chatgpt-subscription";
|
|
18568
18825
|
const dictionaries$1 = {
|
|
@@ -18600,6 +18857,19 @@ window.__ModuleLoader__.load({
|
|
|
18600
18857
|
proxyModeAuto: "系统代理(自动检测)",
|
|
18601
18858
|
proxyModeCustom: "自定义代理",
|
|
18602
18859
|
proxyModeDirect: "直连(禁用代理)",
|
|
18860
|
+
compactionRoute: "压缩模型",
|
|
18861
|
+
compactionRouteHint: "自动压缩时由哪个模型来写摘要。留空则继续用会话自己的模型,也就继续消耗那条线路的额度。",
|
|
18862
|
+
compactionRouteProvider: "线路",
|
|
18863
|
+
compactionRouteModel: "模型",
|
|
18864
|
+
compactionRouteSave: "保存",
|
|
18865
|
+
compactionRouteClear: "恢复会话模型",
|
|
18866
|
+
compactionRouteChange: "设置",
|
|
18867
|
+
compactionRouteFollow: "跟随会话模型(默认)",
|
|
18868
|
+
compactionRouteSaved: "已保存,下次压缩生效。",
|
|
18869
|
+
compactionRouteFailed: "保存失败",
|
|
18870
|
+
compactionRouteNoVideo: "该模型不接受视频:含视频的会话会回落到会话模型完成压缩。",
|
|
18871
|
+
compactionRouteNoImage: "该模型不接受图片:摘要里的截图会被替换为占位文本。",
|
|
18872
|
+
compactionRouteEmpty: "没有可用的线路——先登录任意一条线路再回来设置。",
|
|
18603
18873
|
customProxyUrl: "自定义代理地址",
|
|
18604
18874
|
customProxyUrlHint: "支持 HTTP/HTTPS/SOCKS5 代理地址,例如 http://127.0.0.1:7890",
|
|
18605
18875
|
customProxyUrlPlaceholder: "例如: http://127.0.0.1:7890",
|
|
@@ -18797,6 +19067,19 @@ window.__ModuleLoader__.load({
|
|
|
18797
19067
|
proxyModeAuto: "System Proxy (Auto-detect)",
|
|
18798
19068
|
proxyModeCustom: "Custom Proxy",
|
|
18799
19069
|
proxyModeDirect: "Direct (No Proxy)",
|
|
19070
|
+
compactionRoute: "Compaction model",
|
|
19071
|
+
compactionRouteHint: "Which model writes the summary when the context is compacted. Leave it unset to keep the conversation's own model — and that line's quota.",
|
|
19072
|
+
compactionRouteProvider: "Line",
|
|
19073
|
+
compactionRouteModel: "Model",
|
|
19074
|
+
compactionRouteSave: "Save",
|
|
19075
|
+
compactionRouteClear: "Use the conversation model",
|
|
19076
|
+
compactionRouteChange: "Change",
|
|
19077
|
+
compactionRouteFollow: "Follows the conversation model (default)",
|
|
19078
|
+
compactionRouteSaved: "Saved; it applies to the next compaction.",
|
|
19079
|
+
compactionRouteFailed: "Save failed",
|
|
19080
|
+
compactionRouteNoVideo: "This model takes no video: a session that carries video falls back to the conversation model.",
|
|
19081
|
+
compactionRouteNoImage: "This model takes no images: screenshots become placeholder text in the summary.",
|
|
19082
|
+
compactionRouteEmpty: "No route is available — sign in to one first.",
|
|
18800
19083
|
customProxyUrl: "Custom Proxy URL",
|
|
18801
19084
|
customProxyUrlHint: "Supports HTTP/HTTPS/SOCKS5 proxy URLs, e.g. http://127.0.0.1:7890",
|
|
18802
19085
|
customProxyUrlPlaceholder: "e.g. http://127.0.0.1:7890",
|
|
@@ -19674,6 +19957,19 @@ window.__ModuleLoader__.load({
|
|
|
19674
19957
|
proxyModeAuto: "Системный прокси (автоопределение)",
|
|
19675
19958
|
proxyModeCustom: "Свой прокси",
|
|
19676
19959
|
proxyModeDirect: "Напрямую (без прокси)",
|
|
19960
|
+
compactionRoute: "Модель сжатия",
|
|
19961
|
+
compactionRouteHint: "Какая модель пишет сводку при сжатии контекста. Если не выбрана, используется модель диалога — и её лимит.",
|
|
19962
|
+
compactionRouteProvider: "Линия",
|
|
19963
|
+
compactionRouteModel: "Модель",
|
|
19964
|
+
compactionRouteSave: "Сохранить",
|
|
19965
|
+
compactionRouteClear: "Модель диалога",
|
|
19966
|
+
compactionRouteChange: "Изменить",
|
|
19967
|
+
compactionRouteFollow: "Как в диалоге (по умолчанию)",
|
|
19968
|
+
compactionRouteSaved: "Сохранено; применится при следующем сжатии.",
|
|
19969
|
+
compactionRouteFailed: "Не удалось сохранить",
|
|
19970
|
+
compactionRouteNoVideo: "Модель не принимает видео: диалог с видео вернётся к модели диалога.",
|
|
19971
|
+
compactionRouteNoImage: "Модель не принимает изображения: скриншоты в сводке станут текстом-заглушкой.",
|
|
19972
|
+
compactionRouteEmpty: "Нет доступных линий — сначала войдите в любую.",
|
|
19677
19973
|
customProxyUrl: "Адрес своего прокси",
|
|
19678
19974
|
customProxyUrlHint: "Поддерживаются адреса HTTP/HTTPS/SOCKS5, например http://127.0.0.1:7890",
|
|
19679
19975
|
customProxyUrlPlaceholder: "например http://127.0.0.1:7890",
|
|
@@ -53503,6 +53799,11 @@ window.__ModuleLoader__.load({
|
|
|
53503
53799
|
label: () => t("hubTitle"),
|
|
53504
53800
|
locale: NS
|
|
53505
53801
|
}, ProviderHubSectionWrapper));
|
|
53802
|
+
ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
|
|
53803
|
+
name: "plugins.bundle.config",
|
|
53804
|
+
key: COMPACTION_ROUTE_PACKAGE,
|
|
53805
|
+
locale: NS
|
|
53806
|
+
}, CompactionRouteBundleConfig));
|
|
53506
53807
|
ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
|
|
53507
53808
|
name: "conversation.input.right",
|
|
53508
53809
|
id: "codex-subscription-quota",
|