dsh-connect-agnes-token-plan 0.13.0 → 0.13.2
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 +19 -0
- package/README.md +18 -12
- package/client.js +38 -2
- package/docs/ADR.md +21 -0
- package/docs/DSH-PLUGIN.md +1 -1
- package/docs/README.md +4 -3
- package/docs/SETUP.md +15 -12
- package/docs/TROUBLESHOOTING.md +89 -0
- package/lib/agnescode-stats-store.js +66 -19
- package/lib/index.js +179 -5
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -23,6 +23,25 @@
|
|
|
23
23
|
|
|
24
24
|
## [Unreleased]
|
|
25
25
|
|
|
26
|
+
## [0.13.2] — 2026-10-23
|
|
27
|
+
|
|
28
|
+
本版两件事:**网关新上的模型默认就能发图**,以及**余额胶囊上会多一段「· N 新模型」**。两处都只影响 AgnesCode(桌面端)这条线。
|
|
29
|
+
|
|
30
|
+
### 变更:新模型默认能发图,胶囊会告诉你网关多了哪个模型
|
|
31
|
+
|
|
32
|
+
- **改(识图)**:网关新上的模型不再默认被当成纯文本。旧版只认「官方文档表里写过的」那几个,表里没有的模型就不给图片入口——看不到任何提示,只能自己发现。现在没证据的模型**默认放开图片输入**,面板把它标成「可看图·未实测」;确认看不见图的模型才被收回去(记在一份否决清单里,带实测日期)。
|
|
33
|
+
- **新(胶囊)**:小浣熊/AgnesCode 的余额胶囊上会多一段「· N 新模型」。它比的是插件自己记的「这台机器见过哪些模型」,所以只在网关真的新上了模型时出现。新模型名字在 tooltip 和展开卡片里,最多列三个。标记保持 7 天,不弹窗、不需要点确认;装上新版后的第一次运行会把当时的目录当作「早就见过」,不会把整张表报成新的。
|
|
34
|
+
- **说明**:两处都只影响 AgnesCode 这条线,商汤 Token Plan 侧的面板与胶囊不变。
|
|
35
|
+
|
|
36
|
+
## [0.13.1] — 2026-10-22
|
|
37
|
+
|
|
38
|
+
本版只改一件事:**插件卡的标题**。中文叫「Agnes 模型接入」,英文叫「Agnes Model Access」,与「商汤模型接入」「魔搭模型接入」同构。
|
|
39
|
+
|
|
40
|
+
### 变更:插件卡标题换成「Agnes 模型接入」
|
|
41
|
+
|
|
42
|
+
- **改(插件卡·中英文界面)**:标题此前是「Agnes Token Plan 接入全家桶」——「接入全家桶」是内部叫法,英文那版「Agnes Token Plan Connect」也不是一句英文。现在中文叫「Agnes 模型接入」,英文叫「Agnes Model Access」,与同族插件一眼能对上。
|
|
43
|
+
- **说明**:插件的包名与市场条目没有改(仍是 `dsh-connect-agnes-token-plan`),按旧名搜索照旧找得到;面板里的字也没动。
|
|
44
|
+
|
|
26
45
|
## [0.13.0] — 2026-10-21
|
|
27
46
|
|
|
28
47
|
本版新增 **AgnesCode 扫码登录**:桌面端没装、没登录也能用这条线。另外修了促销标丢失、并发记录丢失、令牌被悄悄改写三处问题,并扩大了凭据脱敏面。
|
package/README.md
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
<h1 align="center">dsh-connect-agnes-token-plan</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center"><b>把 Agnes(爱思)接进 DSH 的 Plugins 页插件卡,并在会话输入框常驻一枚用量胶囊。</b></p>
|
|
4
|
+
|
|
5
|
+
<p align="center"><a href="#安装">安装</a> · <a href="#它是怎么工作的">工作原理</a> · <a href="CHANGELOG.md">更新日志</a> · <a href="https://github.com/eghrhegpe/dsh-connect-agnes-token-plan/issues">问题反馈</a></p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://www.npmjs.com/package/dsh-connect-agnes-token-plan"><img src="https://img.shields.io/npm/v/dsh-connect-agnes-token-plan?style=flat-square&label=npm&color=cb3837" alt="npm version"></a> <a href="https://www.npmjs.com/package/dsh-connect-agnes-token-plan"><img src="https://img.shields.io/npm/d18m/dsh-connect-agnes-token-plan?style=flat-square&label=downloads&color=cb3837" alt="npm downloads"></a> <a href="https://github.com/eghrhegpe/dsh-connect-agnes-token-plan/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/eghrhegpe/dsh-connect-agnes-token-plan/ci.yml?branch=main&style=flat-square&label=tests" alt="test status"></a>
|
|
9
|
+
<a href="LICENSE"><img src="https://img.shields.io/github/license/eghrhegpe/dsh-connect-agnes-token-plan?style=flat-square" alt="MIT license"></a> <a href="https://github.com/eghrhegpe/dsh-connect-agnes-token-plan/stargazers"><img src="https://img.shields.io/github/stars/eghrhegpe/dsh-connect-agnes-token-plan?style=flat-square" alt="GitHub stars"></a> <a href="https://dshfind.com/plugins/eghrhegpe/dsh-connect-agnes-token-plan"><img src="https://dshfind.com/api/badge/eghrhegpe/dsh-connect-agnes-token-plan" alt="dshfind plugin"></a>
|
|
10
|
+
</p>
|
|
2
11
|
|
|
3
12
|
把 Agnes 接进 DSH:**Plugins 页**的插件卡(三个 tab),外加**会话输入框右侧的一枚用量胶囊**。**DSH** 即 **DeepSeek Harness**;**Agnes**(爱思)是全模态大模型平台,本插件对接其**控制台 API** 与**推理 API**。
|
|
4
13
|
|
|
@@ -9,7 +18,7 @@
|
|
|
9
18
|
> 2. **三种「红」是正常的,不是回归**:① `test/wiring.test.mjs` 等 peer 套件报 `cannot resolve the peer dependency` —— 那是 DSH runtime 自带的包不在本仓库,属环境问题,先查 `test/peer-roots.mjs`;② 没装 `dsh` CLI 时 e2e 会 SKIP,属预期;③ 若在 **23:00–00:00** 之间单独跑 `render.test.mjs` 报红,那是历史上的午夜窗口缺陷(已在 0.10.0 修复,见 [docs/PITFALLS.md](docs/PITFALLS.md) §48),当前安装版本已含,无需升级。
|
|
10
19
|
> 3. **见到「奇怪」的写法先别定罪**——本仓多数刻意的取舍都在 [docs/PITFALLS.md](docs/PITFALLS.md) / [docs/ADR.md](docs/ADR.md) 有明文出处。**动手前先 grep 一遍 docs/**。
|
|
11
20
|
>
|
|
12
|
-
> 协作会话的完整纪律见 [AGENTS.md](AGENTS.md)
|
|
21
|
+
> 协作会话的完整纪律见 [AGENTS.md](https://github.com/eghrhegpe/dsh-connect-agnes-token-plan/blob/master/AGENTS.md)(**不随 npm 包发布**,registry 页面上请从 GitHub 打开)。
|
|
13
22
|
|
|
14
23
|
- **积分额度**——登录后实时查看积分余额、额度窗口与每模型消耗;设了 `AGNES_PASSWORD` 则令牌到期自动重登,之后无需再管;
|
|
15
24
|
- **接入 API**——把 Agnes 模型注册为 DSH provider,参与对话、出图与出视频;
|
|
@@ -66,7 +75,8 @@
|
|
|
66
75
|
|
|
67
76
|
- 读数与面板**同源**:控制台百分比优先用平台自己报的 `usage_pct`、缺失才回落 `已用 / 上限`,**不做钳制**(超额窗口就显示 >100%,那是事实);桌面端画的是**存量**,所以**不造百分比**——网关只公布积分,没有「上限」可以当分母;
|
|
68
77
|
- 两条上游**互不借用**:`agnescode` 的会话只读它自己的路由、绝不画控制台窗口,反之亦然;切到**别的 provider**(其它插件)的模型时胶囊直接消失,也不再发任何请求;
|
|
69
|
-
- 读不到时**保留上一次数字并标「上次数据」**,不会清零或伪装成 0%;余额里的 `0` 是「烧完了」的读数(红色告警),`—`
|
|
78
|
+
- 读不到时**保留上一次数字并标「上次数据」**,不会清零或伪装成 0%;余额里的 `0` 是「烧完了」的读数(红色告警),`—` 才是「这次没读到」——这个区别是刻意的;
|
|
79
|
+
- 网关新上了模型时,余额胶囊会多一段**「· N 新模型」**(名字在 tooltip 与展开卡里,最多三个,保留 7 天)。这里的「新」指**这台机器第一次见到它**,不是上游的上架时间——网关目录没有这个字段。装上新版后的第一次运行只把当时的目录记下来当基线,不会把整张表报成新的。
|
|
70
80
|
|
|
71
81
|

|
|
72
82
|
|
|
@@ -100,26 +110,22 @@
|
|
|
100
110
|
|
|
101
111
|
## 运维诊断:这台机器现在挂没挂 provider?
|
|
102
112
|
|
|
103
|
-
provider /
|
|
104
|
-
|
|
105
|
-
```powershell
|
|
106
|
-
npm run doctor # 人读:每个 profile 的 provider / draw / video / agnescode 开关与模型清单
|
|
107
|
-
npm run doctor:json # 机器读:JSON(可进你的巡检 / 工单脚本)
|
|
108
|
-
```
|
|
113
|
+
provider / 出图 / 视频 / AgnesCode 开关的生效值存在插件私有状态文件里(`$DSH_HOME/state/<name>/`),不在任何配置或路由上;`npm run doctor` 只读状态文件、不碰凭据,Host 没起也能跑(人读 / 机器读两个口径见 [docs/SETUP.md](docs/SETUP.md) §6.1)。
|
|
109
114
|
|
|
110
115
|
## 文档
|
|
111
116
|
|
|
112
117
|
- [docs/README.md](docs/README.md) — 全量文档索引(每篇按「内容 / 何时查」路由;本表只列主文档)
|
|
113
118
|
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) — 架构与数据流
|
|
114
|
-
- [docs/SETUP.md](docs/SETUP.md) —
|
|
119
|
+
- [docs/SETUP.md](docs/SETUP.md) — 配置字段、改动后重启
|
|
120
|
+
- [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) — 排障入口(按面板界面原文查)
|
|
115
121
|
- [docs/AUTH.md](docs/AUTH.md) — 登录 / 重登 / 节流设计
|
|
116
122
|
- [docs/API.md](docs/API.md) — 路由与控制台端点
|
|
117
123
|
- [docs/AGNES-API.md](docs/AGNES-API.md) — Agnes 接口全集(控制台额度侧 + 推理侧)
|
|
118
124
|
- [docs/TESTING.md](docs/TESTING.md) — 测试体系
|
|
119
|
-
- [docs/PITFALLS.md](docs/PITFALLS.md) — 真实踩坑(61
|
|
125
|
+
- [docs/PITFALLS.md](docs/PITFALLS.md) — 真实踩坑(61 条;数字由 `docs.test.mjs` 的 `PITFALLS_REFS` 与文件内编号钉住)
|
|
120
126
|
- [CHANGELOG.md](CHANGELOG.md) — 版本变化
|
|
121
127
|
|
|
122
|
-
AI 协作会话请先读 [AGENTS.md](AGENTS.md)
|
|
128
|
+
AI 协作会话请先读 [AGENTS.md](https://github.com/eghrhegpe/dsh-connect-agnes-token-plan/blob/master/AGENTS.md)(在仓库里,**不在 npm 包里**)。人类贡献者先读 [SETUP.md](docs/SETUP.md) 与 [ARCHITECTURE.md](docs/ARCHITECTURE.md) §2 理解结构与改动代价,改代码前再读 `AGENTS.md` 的红线与 [TESTING.md](docs/TESTING.md) 的 `npm test` 门禁。
|
|
123
129
|
|
|
124
130
|
## 诚实声明
|
|
125
131
|
|
package/client.js
CHANGED
|
@@ -264,6 +264,12 @@ var dsh_connect_agnes_token_plan_client = (function() {
|
|
|
264
264
|
"agnescode.models": "模型({count})",
|
|
265
265
|
"agnescode.fallbackRoster": "未取到实时目录,以下为内置清单,倍率截至 {asOf}。",
|
|
266
266
|
"agnescode.memberOnly": "会员",
|
|
267
|
+
"agnescode.visionAssumed": "可看图·未实测",
|
|
268
|
+
"agnescode.visionAssumedTitle": "网关没声明这个模型能否看图,插件默认放开图片入口(未实测)。真发过去模型说看不到,就把它的 id 记进否决清单。",
|
|
269
|
+
"agnescd.newModels": "新模型",
|
|
270
|
+
"agnescd.newModelsRow": "本机新见",
|
|
271
|
+
"agnescd.newModelsTitle": "本机首次见到的模型:",
|
|
272
|
+
"agnescd.newModelsSep": "、",
|
|
267
273
|
"agnescode.rateTitle": "平台公布的积分消耗倍率(0 为免费)",
|
|
268
274
|
"agnescode.registeredPill": "已注册",
|
|
269
275
|
"agnescode.rosterHint": "勾选决定哪些模型推送进 DSH 模型列表,改动即时生效。",
|
|
@@ -524,6 +530,12 @@ var dsh_connect_agnes_token_plan_client = (function() {
|
|
|
524
530
|
"agnescode.models": "Models ({count})",
|
|
525
531
|
"agnescode.fallbackRoster": "Live catalogue unavailable — showing the built-in list, rates as of {asOf}.",
|
|
526
532
|
"agnescode.memberOnly": "member",
|
|
533
|
+
"agnescode.visionAssumed": "vision (unverified)",
|
|
534
|
+
"agnescode.visionAssumedTitle": "The gateway did not declare whether this model sees images, so picture input is allowed by default and this is unverified. If the model answers it cannot see the image, add its id to the vision denylist.",
|
|
535
|
+
"agnescd.newModels": "new",
|
|
536
|
+
"agnescd.newModelsRow": "New here",
|
|
537
|
+
"agnescd.newModelsTitle": "First seen on this machine: ",
|
|
538
|
+
"agnescd.newModelsSep": ", ",
|
|
527
539
|
"agnescode.rateTitle": "Credit multiplier as published by the platform (0 = free)",
|
|
528
540
|
"agnescode.registeredPill": "Registered",
|
|
529
541
|
"agnescode.rosterHint": "Ticking decides which models are pushed into DSH's model list; the change takes effect immediately.",
|
|
@@ -1562,6 +1574,10 @@ var dsh_connect_agnes_token_plan_client = (function() {
|
|
|
1562
1574
|
},
|
|
1563
1575
|
pillReading: { fontVariantNumeric: "tabular-nums" },
|
|
1564
1576
|
pillStale: { color: "var(--dsw-alias-label-secondary)" },
|
|
1577
|
+
pillNewTag: {
|
|
1578
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
1579
|
+
flexShrink: 0
|
|
1580
|
+
},
|
|
1565
1581
|
pillErrorText: { color: "var(--dsw-alias-state-error-primary)" },
|
|
1566
1582
|
pillTierOk: { background: "var(--dsw-alias-state-success-primary, #22c55e)" },
|
|
1567
1583
|
pillTierWarn: { background: "var(--dsw-alias-state-warn-primary, #f59e0b)" },
|
|
@@ -4050,7 +4066,10 @@ var dsh_connect_agnes_token_plan_client = (function() {
|
|
|
4050
4066
|
}), h("span", { style: S.spacer }), promo === null ? null : h("span", { style: S.modelPromo }, promo), rate === null ? null : h("span", {
|
|
4051
4067
|
style: S.modelRate,
|
|
4052
4068
|
title: tt("agnescode.rateTitle")
|
|
4053
|
-
}, `×${rate}`), row
|
|
4069
|
+
}, `×${rate}`), row?.vision === true ? h("span", {
|
|
4070
|
+
style: S.modelBadge,
|
|
4071
|
+
title: row?.visionSource === "assumed" ? tt("agnescode.visionAssumedTitle") : void 0
|
|
4072
|
+
}, row?.visionSource === "assumed" ? tt("agnescode.visionAssumed") : tt("llm.rosterVision")) : null, row.memberOnly === true ? h("span", { style: S.modelBadge }, tt("agnescode.memberOnly")) : null), meta === "" ? null : h("div", { style: S.modelMeta }, meta));
|
|
4054
4073
|
})));
|
|
4055
4074
|
}
|
|
4056
4075
|
/**
|
|
@@ -4665,10 +4684,17 @@ var dsh_connect_agnes_token_plan_client = (function() {
|
|
|
4665
4684
|
const permanent = partOf(state, "permanentBalance");
|
|
4666
4685
|
const split = timeLimited === null || permanent === null ? null : `${tt("agnescode.balanceTimeLabel")} ${count(timeLimited)} · ${tt("agnescode.balancePermanentLabel")} ${count(permanent)}`;
|
|
4667
4686
|
const usageText = agnescodeUsageText(state?.usage ?? null, tt);
|
|
4687
|
+
const newIds = (Array.isArray(state?.newModelIds) ? state.newModelIds : []).filter((id) => typeof id === "string" && id !== "");
|
|
4688
|
+
const shownNames = newIds.map((id) => {
|
|
4689
|
+
const row = state?.models?.find((model) => model.id === id);
|
|
4690
|
+
return row?.name ?? row?.id ?? id;
|
|
4691
|
+
}).slice(0, 3);
|
|
4692
|
+
const newModelTitle = newIds.length === 0 ? null : `${tt("agnescd.newModelsTitle")}${shownNames.join(tt("agnescd.newModelsSep"))}${newIds.length > shownNames.length ? "…" : ""}`;
|
|
4668
4693
|
const title = [
|
|
4669
4694
|
tt("composer.balanceTitle"),
|
|
4670
4695
|
split,
|
|
4671
4696
|
why,
|
|
4697
|
+
newModelTitle,
|
|
4672
4698
|
stale ? tt("composer.stale") : null
|
|
4673
4699
|
].filter(Boolean).join(" · ");
|
|
4674
4700
|
const { popoverId, open, setOpen, rootRef, cardRef, triggerRef } = usePillPopover();
|
|
@@ -4693,6 +4719,10 @@ var dsh_connect_agnes_token_plan_client = (function() {
|
|
|
4693
4719
|
key: "why",
|
|
4694
4720
|
style: S.pillPopMuted
|
|
4695
4721
|
}, why) : null,
|
|
4722
|
+
newIds.length > 0 ? h("div", {
|
|
4723
|
+
key: "new-models",
|
|
4724
|
+
style: S.pillPopMuted
|
|
4725
|
+
}, `${tt("agnescd.newModelsRow")} ${shownNames.join(tt("agnescd.newModelsSep"))}${newIds.length > shownNames.length ? "…" : ""}`) : null,
|
|
4696
4726
|
stale ? h("div", {
|
|
4697
4727
|
key: "stale",
|
|
4698
4728
|
style: S.pillPopMuted
|
|
@@ -4716,7 +4746,13 @@ var dsh_connect_agnes_token_plan_client = (function() {
|
|
|
4716
4746
|
popoverOpen: open,
|
|
4717
4747
|
popoverId,
|
|
4718
4748
|
buttonRef: triggerRef,
|
|
4719
|
-
children: h("span", {
|
|
4749
|
+
children: [h("span", {
|
|
4750
|
+
key: "reading",
|
|
4751
|
+
style: S.pillReading
|
|
4752
|
+
}, pending ? "…" : balance === null ? "—" : `${count(balance)} ${tt("composer.balanceUnit")}`), newIds.length === 0 ? null : h("span", {
|
|
4753
|
+
key: "new-models",
|
|
4754
|
+
style: S.pillNewTag
|
|
4755
|
+
}, ` · ${newIds.length} ${tt("agnescd.newModels")}`)]
|
|
4720
4756
|
}), open ? h("div", {
|
|
4721
4757
|
ref: cardRef,
|
|
4722
4758
|
id: popoverId,
|
package/docs/ADR.md
CHANGED
|
@@ -206,3 +206,24 @@
|
|
|
206
206
|
- **受影响的文档**:`src/host/agnescode-models.ts`(`agnescodeThinkingLevelMap(id)` per-model + 模块头决策 1)、`test/agnescode.test.mjs`(新增 agnes-2.5-pro 断言块)、`docs/ROADMAP.md` §6.3.1(新增 2026-10-18 探针行)、`CHANGELOG.md`([Unreleased])。ADR-009 原文按考古纪律保留不改写。
|
|
207
207
|
- **与既有条目的关系**:细化 ADR-009 的档位映射(该裁定以 3.0-flash 探针为基础,未覆盖 2.5-pro 的阶梯差异);不改变 `docs/DSH-LLM-DEVELOP.md` §4 的通用裁法(Token Plan 侧 `thinkingLevelMapFor` 未动)。
|
|
208
208
|
|
|
209
|
+
## ADR-018 agnescode 识图默认放行:没证据的行也收图,但面板必须说「未实测」
|
|
210
|
+
|
|
211
|
+
- **日期**:2026-10-22
|
|
212
|
+
- **状态**:现行(实现见 `src/host/agnescode-models.ts` 的 `AGNESCODE_VISION_DENYLIST` / `agnescodeVisionVerdict` / `agnescodeVisionSource`,`agnescodeRoster` 把 `vision` 与 `visionSource` 一起透出;面板 chip 见 `src/client/agnescode-tab.ts`;门禁见 `test/agnescode.test.mjs` 的判定阶梯段与 `test/dom-render.test.mjs` 的 chip 段)
|
|
213
|
+
- **裁定**:判定阶梯四级——**实测否决集 → 行内声明 → `PROBED_VISION` 实测表 → 默认放行(`assumed`)**。前三级给面板一个朴素 chip;`assumed` 必须画成「可看图·未实测」并带 tooltip;否决项画 `vision:false`、无 chip。换言之:**默认值是「给它图片」,唯一能否掉它的是带日期实测的否决条目**。
|
|
214
|
+
- **理由**:网关 `/models` 的每一行都只写 `model_type: "text"`,从不说自己能不能看图(`fetchAgnescodeCatalog` 因此钉死 `vision:false`——那是 wire 事实,不是判决)。旧逻辑把「文档表里没有」读成「不支持」,于是网关每上一次新模型,图片入口就静默消失一次:用户看不到任何提示,只能自己发现并来报。这条失效方向是不可见的(能力缺失不像错报那样会在会话里露头),而错报在当前网关是可观测的:模型收到图片后自述看不见,HTTP 仍是 200。
|
|
215
|
+
- **代价与护栏**:默认放行把失效方向翻成「错报」,但没有翻成「谎报」——`visionSource` 让面板把未实测的行标出来,读者因此知道这是插件的默认,不是平台的声明。否决集是唯一维护面:发现某模型自述看不见,就把它带日期加进 `AGNESCODE_VISION_DENYLIST`。
|
|
216
|
+
- **边界**:只动 agnescode 这条上游;Token Plan 侧 `identifyVisionModel`(按 `/v1/models` 的 `input_modalities` 判定)不受影响,两侧判定函数不合并。`AGNESCODE_FALLBACK_MODELS` 的 `vision` 列是手写快照,不经过本判定函数;`visionSource` 只影响面板显示,不改变 descriptor 的 `input` 数组长度约定(`vision:true` → `["text","image"]`)。
|
|
217
|
+
- **复核条件**:若网关开始在 `/models` 里声明模态(`input_modalities` 或视觉标签),第二级(`declared`)就会接管绝大多数行,`assumed` 退化为稀有位——那时本条仍然成立,只是很少被用到。若哪天实测发现「未实测默认可看图」在本网关的错报代价高到不可接受(例如图片被静默吞掉且模型照常编造),把默认改回 `false` 并把本条的 `assumed` 换成一个显式的 `unknown` chip。
|
|
218
|
+
|
|
219
|
+
## ADR-019 网关新增模型走胶囊的「· N 新模型」,不弹窗、不留确认态
|
|
220
|
+
|
|
221
|
+
- **日期**:2026-10-22
|
|
222
|
+
- **状态**:现行(实现见 `src/host/agnescode-roster-seen-store.ts` 的首次见到历史、`src/host/routes/agnescode.ts` 的 `newModelIds` 差分、`src/client/composer-usage-pill.ts` 的 AgnesCode 胶囊段;门禁见 `test/agnescode.test.mjs` 的两个新段与 `test/dom-render.test.mjs` 的胶囊三帧)
|
|
223
|
+
- **裁定**:网关目录里出现本机没见过的 model id 时,**只在胶囊上多一段「· N 新模型」**(tooltip 与 popover 给名字,最多列三个),不弹窗、不阻塞、不留「已读」按钮。判定基准是插件自己写的首次见到文件 `agnescode-roster-seen.json`:**首次写入是播种(整表记为已见、一条都不报)**,之后新增的 id 记为「现在」并保持 7 天,离开目录的 id 从表里删除(重新上架算新)。
|
|
224
|
+
- **理由**:DSH 的 Host 面没有通知/弹窗服务,插件的可见面只有插件卡与胶囊;而胶囊本来就是读者扫余额的地方,把「网关多了个模型」放在余额旁边是同一类信息(都关于「现在能用什么」)。播种是必须的:升级后的第一次运行会把当时整张目录写成历史,否则每个老用户都会被告知全部七个模型都是新的——那是噪音,不是提醒。
|
|
225
|
+
- **为什么不是弹窗 / 已读态**:弹窗需要用户处理,而「多了个模型」不需要处理;已读态需要一个点击,等于把一个纯信息变成一次任务。7 天窗口是提醒的寿命:够长到用户会看见,够短到不会永远挂着。重上架的 id 再报一次,因为那说明这一行背后的东西换过手,值得知道。
|
|
226
|
+
- **边界**:只在 `modelsSource === "live"` 时差分——回退快照表是插件自己的六行文本,拿它差分会在登录/离线那几帧凭空造出「新模型」。差分对象是**整张 live 目录**,不是用户勾选的子集:某个模型是不是网关的新闻,与这台机器勾了哪些框无关;拿子集差分还会让一个上架很久的模型在「被勾上」的那天冒充新模型。`newModelIds` 缺席(旧生产者、离线、首次播种)时胶囊什么都不画,不画空段、不留悬空分隔符。该标记只出现在 agnescode provider 被选中的胶囊上;Token Plan 侧名册没有这个字段。
|
|
227
|
+
- **复核条件**:若网关开始提供「上架时间」字段,差分就应改成读那个字段,本文件降级为离线兜底;若 DSH 之后提供通知服务,这段可以迁过去,胶囊只留计数。
|
|
228
|
+
|
|
229
|
+
|
package/docs/DSH-PLUGIN.md
CHANGED
|
@@ -25,7 +25,7 @@ DSH 插件是一段在 **Host**(桌面版或 `dsh web`)进程内运行的代
|
|
|
25
25
|
{
|
|
26
26
|
"name": "dsh-connect-agnes-token-plan",
|
|
27
27
|
"icon": "./icon.svg", // Plugins 页插件卡图标:Host 读此字段按 SVG/PNG/JPEG/WebP 渲染(相对路径、落在 files 内、≤256 KiB,任一不满足会让整条元数据报错)
|
|
28
|
-
"version": "0.13.
|
|
28
|
+
"version": "0.13.2",
|
|
29
29
|
"main": "./lib/index.js", // Host 半边入口:src/host/*.ts 经 tsdown 打成的单条 bundle
|
|
30
30
|
"exports": {
|
|
31
31
|
".": "./lib/index.js", // 源码在 src/host/,lib/ 为纯构建产物(随库提交,git/市场直装零构建)
|
package/docs/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
AI 协作会话从根目录 [AGENTS.md](../AGENTS.md) 进入:验证怎么跑、红线、文档地图都在那里,本目录提供细节。
|
|
6
6
|
|
|
7
|
-
阅读顺序建议:先 [ARCHITECTURE.md](./ARCHITECTURE.md) 建立整体认知,再按需查 [SETUP.md](./SETUP.md) / [AUTH.md](./AUTH.md) / [API.md](./API.md);改动代码前读 [TESTING.md](./TESTING.md) 与 [CONTRIBUTING.md](./CONTRIBUTING.md)。
|
|
7
|
+
阅读顺序建议:先 [ARCHITECTURE.md](./ARCHITECTURE.md) 建立整体认知,再按需查 [SETUP.md](./SETUP.md) / [AUTH.md](./AUTH.md) / [API.md](./API.md);**它坏了、只有一句报错**则直接进 [TROUBLESHOOTING.md](./TROUBLESHOOTING.md);改动代码前读 [TESTING.md](./TESTING.md) 与 [CONTRIBUTING.md](./CONTRIBUTING.md)。
|
|
8
8
|
|
|
9
9
|
| 文档 | 内容 | 何时查 |
|
|
10
10
|
|---|---|---|
|
|
@@ -12,13 +12,14 @@ AI 协作会话从根目录 [AGENTS.md](../AGENTS.md) 进入:验证怎么跑
|
|
|
12
12
|
| [ROADMAP.md](./ROADMAP.md) | 战略执行路线图(2026-09-29 起):P0 解耦与契约回归(§2,`index.js` 控制面拆模块 + 推理契约自动化回归)、429 全局自愈、§5「多 Key 池」纠偏、文档精炼、明确不做的边界 | 定吸收顺序 / 优先级、拍板侵入性、防范围漂移 |
|
|
13
13
|
| [DSH-PLUGIN.md](./DSH-PLUGIN.md) | DSH 插件机制总览(bundle 结构、Loader 条目、cordis.patch.yml、安装重启、peer 依赖、与兄弟插件关系) | 理解「这是一个 DSH 插件」、对照 dsh-connect-qoder 范本 |
|
|
14
14
|
| [DSH-LLM-DEVELOP.md](./DSH-LLM-DEVELOP.md) | **LLM / provider 接入开发指南**:三个 peer 分工、注册三件套、adapter 组装、descriptor 契约(含 maxTokens 兜底坑)、live-contract 探针纪律、排查速查 | 把某平台接成 DSH provider、排查模型路由故障、定钉值策略 |
|
|
15
|
-
| [SETUP.md](./SETUP.md) | 安装、配置字段表、改动后必须重启 Host
|
|
15
|
+
| [SETUP.md](./SETUP.md) | 安装、配置字段表、改动后必须重启 Host、首次使用;排障已迁出(见下一行) | 装环境、改配置、排「跑的是旧代码」 |
|
|
16
|
+
| [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) | **排障入口,按界面原文索引**:面板上那句话属于哪一层、第一动作是什么、细节在哪份文档的哪一节;含「看着像坏了其实是设计」清单;不发明症状码(理由在 §4) | 只知道一句报错或界面文案、不知道是哪个模块;接手别人「它坏了」的问题 |
|
|
16
17
|
| [AUTH.md](./AUTH.md) | 一跳账号密码登录、无 refresh token(重登即续期)、密码明文过 TLS 与不落盘纪律、登录 trace、防锁号节流 | 改登录/重登、排查登录失败 |
|
|
17
18
|
| [API.md](./API.md) | 本地路由(`snapshot` / `account` / `api-key` / `provider` / `models` / `draw` / `video` / `agnescode` 共 8 组)、控制台端点、配置端点清单 | 对接路由、看返回结构、调端点 |
|
|
18
19
|
| [PROVIDER-HOT-RELOAD.md](./PROVIDER-HOT-RELOAD.md) | 提供方注册开关:从「配置字段 + 重启」到「面板开关 + 立即生效」的设计决策与同类插件调研 | 改 provider 注册、理解开关语义 |
|
|
19
20
|
| [TESTING.md](./TESTING.md) | 离线测试体系、`panel-decision.js` 机制、已知缺口 | 跑测试、理解测试为什么这样写 |
|
|
20
21
|
| [AGNES-API.md](./AGNES-API.md) | **现行接口事实源**:控制台额度侧(端点表、响应信封、四窗口额度模型、套餐目录与 uuid 匹配)+推理侧(provider `agnes-token-plan`、key `AGNES_TOKEN_PLAN_API_KEY`、思考档位 safe-set、live-contract 护栏) | 改额度/推理接口、排「打到错网关」、对 live-contract 基线 |
|
|
21
|
-
| [PITFALLS.md](./PITFALLS.md) |
|
|
22
|
+
| [PITFALLS.md](./PITFALLS.md) | 真实踩坑(现象→根因→修法);**条数以文件内 `## N.` 编号为准,别在此写死** | 改代码前避坑、理解防御性代码的来由 |
|
|
22
23
|
| [CONTRIBUTING.md](./CONTRIBUTING.md) | 提交约定、红线(凭据/`upstream/` 不进库)、仓库整洁 | 准备提交、清理历史误跟踪 |
|
|
23
24
|
| [REFERENCES.md](./REFERENCES.md) | **参照件索引与纪律**:`upstream/` 容器里谁是谁(来源 / 版本 / 许可 / 承重在哪)——AgnesCode 线六件参照件与本机桌面端快照、只吸收事实不复制代码的红线 | 查「这条事实当初从哪来」、新增参照件、防再次「纯探测不落盘」 |
|
|
24
25
|
| [CHANGELOG.md](../CHANGELOG.md) | 公开行为变化的版本记录(非 git log 替代) | 看「这个版本改了什么」 |
|
package/docs/SETUP.md
CHANGED
|
@@ -115,16 +115,19 @@ AgnesCode tab 读的是**另一个应用**(AgnesCode 桌面端)已经登录
|
|
|
115
115
|
|
|
116
116
|
---
|
|
117
117
|
|
|
118
|
-
## 6.
|
|
118
|
+
## 6. 出问题了
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
120
|
+
信号与处置的整张表已迁到 **[TROUBLESHOOTING.md](./TROUBLESHOOTING.md)**(按面板上的**界面原文**索引:面板读不出来、登录被拒、模型目录为空、以及「看着像坏了其实是设计」的清单)。
|
|
121
|
+
|
|
122
|
+
本文件只管装、配、重启;排障从那份进。`shapeWarnings`、`quota.error` 按源降级、「可看图」为空是「读不到」等接口侧含义,那份表指到 [AGNES-API.md](./AGNES-API.md) 与 [ADR.md](./ADR.md),不在此重复。
|
|
123
|
+
|
|
124
|
+
### 6.1 查这台机器的开关生效值(doctor)
|
|
125
|
+
|
|
126
|
+
provider / 出图 / 视频 / AgnesCode 四个开关的生效值存在插件私有状态文件里(`$DSH_HOME/state/<name>/`),不在任何配置或路由上;`npm run doctor` 只读状态文件、不碰凭据,Host 没起也能跑:
|
|
127
|
+
|
|
128
|
+
```powershell
|
|
129
|
+
npm run doctor # 人读:每个 profile 的 provider / draw / video / agnescode 开关与模型清单
|
|
130
|
+
npm run doctor:json # 机器读:JSON(可进你的巡检 / 工单脚本)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
doctor 的只读范围与实现见 [PITFALLS.md](./PITFALLS.md) §22 与 [ROADMAP.md](./ROADMAP.md) 的落地坐标。
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# 排障入口(按界面原文查)
|
|
2
|
+
|
|
3
|
+
当手上只有一句话(面板上的一句话、快照里的一个字段、日志里的一行),没有模块名时,从本文件进。
|
|
4
|
+
|
|
5
|
+
**本文件不解释机制**,只做一件事:把那句话翻译成「属于哪一层、下一步看哪份文档的哪一节」。机制细节的唯一定义处是 [AUTH.md](./AUTH.md)(登录/节流)、[AGNES-API.md](./AGNES-API.md)(接口事实)、[API.md](./API.md)(路由与快照结构)、[PITFALLS.md](./PITFALLS.md)(现象→根因→修法)。这里复制它们的表,只会多出一份会分叉的副本(`docs.test.mjs` 的 `TABLES` 门禁也是这样要求的)。
|
|
6
|
+
|
|
7
|
+
怎么用:
|
|
8
|
+
|
|
9
|
+
1. 面板文案是**原文**(取自 `src/client/i18n.ts` 的 `zh` 字典,键集真源;英文界面同一键的值不同,但键一样),方括号里是文案键——按文案键 grep 能直接定位渲染处与判定条件,比按中文句子搜更稳。
|
|
10
|
+
2. 先看 §1(面板上的话);面板整块是空的、或者只有接口没有界面,看 §2。
|
|
11
|
+
3. §3 是「看着像坏了、其实是设计」的清单。**这里的东西不要去修**。
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. 面板上的话 → 下一步
|
|
16
|
+
|
|
17
|
+
### 1.1 整块读不出来 / 配置类
|
|
18
|
+
|
|
19
|
+
| 界面原文 | 含义 | 第一动作 | 细节 |
|
|
20
|
+
|---|---|---|---|
|
|
21
|
+
| 插件配置有误:{error} [`panel.configError` / 快照码 `config_error`] | 挂载期校验失败,几乎只有「端点字段不是合法 http(s) 绝对地址」一种 | 对照 [SETUP.md](./SETUP.md) §3 的配置表检查 `cordis.patch.yml` 的端点类字段,改后重装 / 重载 Host | [SETUP.md](./SETUP.md) §3 |
|
|
22
|
+
| 读取失败:{error} [`panel.error`] | 快照路由本身失败,不是上游失败 | 到 **Host 终端 stdout** 与 `$DSH_HOME/logs/` 找同一时刻的堆栈——浏览器 F12 里没有 Host 半边 | [PITFALLS.md](./PITFALLS.md) §42 |
|
|
23
|
+
| Agnes 控制台暂时无法读取,通常下一次自动刷新即可恢复… [`panel.consoleTransient` / 快照码 `console_error`] | 传输层瞬时失败,设计上交给下一轮轮询 | 什么都不做,等一轮;连续出现才查网络与平台状态 | [ARCHITECTURE.md](./ARCHITECTURE.md) §5(软失败不变量) |
|
|
24
|
+
| Agnes 控制台未连接,本页的额度与用量都读不到。 [`quota.consoleOffline`] | `overview` 源失败(软降级),其余源的数据仍可能在 | 按提示到本页下方卡片登录;不要当成插件坏了 | [ADR.md](./ADR.md) ADR-007 |
|
|
25
|
+
| 读取 {source} 失败:{message} [`quota.error`] | **单个**额度源(series / subscription / plans / overview)降级,互不牵连 | 先看 `{message}` 里平台自己的话;只有五个源同时失败才是平台级问题 | [AGNES-API.md](./AGNES-API.md) §2、[ARCHITECTURE.md](./ARCHITECTURE.md) §5 |
|
|
26
|
+
| 上游返回的结构可能有变:{detail} [`panel.shapeDrift`] | 控制台响应结构与预期不符(字段改名等)——**接口变更的第一信号** | 对照 [AGNES-API.md](./AGNES-API.md) §2 核字段,别当成「暂无数据」;持续出现记进 PITFALLS | [AGNES-API.md](./AGNES-API.md) §2 |
|
|
27
|
+
| 暂无额度数据。 [`quota.none`] | 读到了响应但没有可用窗口,通常是没登录或账号无额度 | 先确认登录态(§1.2),再看控制台网页端是否同样为空 | [AGNES-API.md](./AGNES-API.md) §5 |
|
|
28
|
+
| 未能识别当前套餐——控制台没有返回可匹配的套餐信息… [`quota.planUnknown`] | 订阅 payload 里没有可匹配的 uuid / 名称;**这是渲染文案键,不是快照信号字段** | 属正常显示:下方「套餐对比」是平台公开目录,不依赖登录 | [AGNES-API.md](./AGNES-API.md) §5 |
|
|
29
|
+
|
|
30
|
+
### 1.2 登录与账号类
|
|
31
|
+
|
|
32
|
+
| 界面原文 | 含义 | 第一动作 | 细节 |
|
|
33
|
+
|---|---|---|---|
|
|
34
|
+
| 需要重新登录 [`auth.needsLogin`] | 令牌被拒且环境里已无密码可自动重登 | 面板表单填一次账号密码 | [AUTH.md](./AUTH.md) §8 |
|
|
35
|
+
| 控制台令牌已失效…Host 会用已保存的账号重新登录一次… [`panel.jwtExpired`] | 一跳重登已触发(Agnes 不发 refresh token) | 等一轮刷新;反复出现按「重登没用」查 | [AUTH.md](./AUTH.md) §5、[PITFALLS.md](./PITFALLS.md) §46 |
|
|
36
|
+
| 令牌自动续期中 [`auth.selfRenew`] | 正常状态,不是告警 | 不用处理 | [AUTH.md](./AUTH.md) §5 |
|
|
37
|
+
| Host 已停止自动重试:上一次登录被拒… [`auth.parked`] | 凭据型拒绝,或自造退避次数用尽,已**停车**(`needsUserAction`) | 核对账号密码后手动登录;等倒计时不会有结果 | [AUTH.md](./AUTH.md) §7、[PITFALLS.md](./PITFALLS.md) §52 |
|
|
38
|
+
| 账号已被锁定。…期间面板不会自动重试。 [`auth.locked`] | 平台锁号;插件刻意不再敲门以免延长锁定 | 去控制台网页端确认账号状态,**别**在面板反复点登录 | [AUTH.md](./AUTH.md) §7 |
|
|
39
|
+
| 平台要求等待约 {minutes} 分钟后再试… [`auth.retryAfter`] | 平台声明了等待窗口,照单全收、不截短 | 等满窗口;期间任何手动重试都可能重新触发 | [AUTH.md](./AUTH.md) §7 |
|
|
40
|
+
| 需要额外验证(短信/图形验证码)… [`auth.verification`] | 自动化登录无法完成验证码 | 先在浏览器登录一次,再回面板 | [AUTH.md](./AUTH.md) §7 |
|
|
41
|
+
| 登录未完成:{reason} [`auth.failed`] | 分类器没认出这句话(兜底 `LOGIN_FAILED`) | 看 `{reason}` 原文与 `$DSH_HOME/logs/` 的 trace;这类形状值得记进 PITFALLS | [AUTH.md](./AUTH.md) §6、§8 |
|
|
42
|
+
| 注意:当前 Host 没有凭据服务… [`auth.ephemeral`] | 缺 `@deepseek-ai/dsh-credentials`,账号只在内存 | 接受「重启需重登」,或换带凭据服务的 Host | [SETUP.md](./SETUP.md) §1 |
|
|
43
|
+
|
|
44
|
+
### 1.3 推理 / 出图 / 视频 / AgnesCode 类
|
|
45
|
+
|
|
46
|
+
| 界面原文 | 含义 | 第一动作 | 细节 |
|
|
47
|
+
|---|---|---|---|
|
|
48
|
+
| 模型目录暂时读不到:{error}(额度与聊天不受影响) [`llm.catalogFailed`] | 没有 API Key → 目录没拉(`catalogAvailable: false`)或目录端点失败;**额度面板不受牵连** | 面板「API Key」卡保存 Key(免费版 `sk-` 或 Token Plan `cpk-`),或用户级 env 配 `AGNES_TOKEN_PLAN_API_KEY`;下一轮 poll 自动恢复,无需重启 | [AGNES-API.md](./AGNES-API.md) §7.1 |
|
|
49
|
+
| 提供方注册失败:{error} [`llm.error`] | 向 DSH 注册 provider 失败(Host 级动作) | 看 `{error}`;注册三件套与 descriptor 契约见 DSH-LLM-DEVELOP | [DSH-LLM-DEVELOP.md](./DSH-LLM-DEVELOP.md) |
|
|
50
|
+
| 额度耗尽 [`llm.rosterExhausted`] | 模型花名册侧标记,不等于 402/429 已发生 | 区分额度不足(402)与速率上限(429),后者等一分钟即可 | [AGNES-API.md](./AGNES-API.md)、[PITFALLS.md](./PITFALLS.md) §45 |
|
|
51
|
+
| 当前 API Key 目录里暂无出图模型;出图不可用。 [`draw.noCandidates`] | 目录里没识别出图模型(名称兜底也没命中) | 换有出图权限的 Key,或显式设 `drawModelId` | [SETUP.md](./SETUP.md) §3(`drawEnabled` / `drawModelId`) |
|
|
52
|
+
| 当前 API Key 目录里暂无视频模型;视频不可用。 [`video.noCandidates`] | 同上,视频侧 | 显式设 `videoModelId`(V2.0 / 2.5 家族均可) | [SETUP.md](./SETUP.md) §3、[AGNES-API.md](./AGNES-API.md) §7.5 |
|
|
53
|
+
| 未关联——请先在 AgnesCode 桌面端登录… [`agnescode.notLogged`] | 本插件不发起登录,只读桌面 App 的会话文件 | 桌面 App 里登录一次,再点「检测本机登录态」 | [SETUP.md](./SETUP.md) §3「AgnesCode 已知限制」 |
|
|
54
|
+
| 本机登录态采集目前仅支持 Windows… [`agnescode.harvestWindowsOnly`] | 采集侧只有 DPAPI 一条路 | 非 Windows 用「扫码登录」 | [AGNESCODE-SCAN-LOGIN.md](./AGNESCODE-SCAN-LOGIN.md) |
|
|
55
|
+
| 扫码登录失败:{error} [`agnescode.loginFail`] | 设备流换 token 失败 | 看 `{error}`;换 token 端点实测记录在扫码登录文档 | [AGNESCODE-SCAN-LOGIN.md](./AGNESCODE-SCAN-LOGIN.md) |
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 2. 快照 / 日志里的信号(没有界面文案)
|
|
60
|
+
|
|
61
|
+
| 信号 | 含义 | 第一动作 | 细节 |
|
|
62
|
+
|---|---|---|---|
|
|
63
|
+
| 快照**没有 `auth` 字段**,只有 `totals` / `recent` | 跑的是旧代码(Host 半边只在启动时加载一次) | 完全退出 DSH(**含托盘**)再启动 | [SETUP.md](./SETUP.md) §4 |
|
|
64
|
+
| `shapeWarnings` 非空 | 控制台返回结构与预期不符(字段改名等)——**接口变更的第一信号** | 对照接口文档核字段,别当成「暂无数据」 | [AGNES-API.md](./AGNES-API.md) §2 |
|
|
65
|
+
| `config_error`(面板顶部) | 挂载期配置错误 | 同 §1.1 第一行 | [SETUP.md](./SETUP.md) §3 |
|
|
66
|
+
| Host 日志 `unknown config key(s) ignored: …` | patch 里有拼错的键(**不**升为 `config_error`,刻意如此) | 改掉拼写;遗留键可不理,但别指望它生效 | [SETUP.md](./SETUP.md) §3 |
|
|
67
|
+
| 浏览器 F12 里没有任何插件报错 | 两半进程:Host 报错在 `dsh web` 终端 stdout 与 `$DSH_HOME/logs/` | 换地方看,不是「没有日志」 | [PITFALLS.md](./PITFALLS.md) §42 |
|
|
68
|
+
| 桌面上看着改了、实际没生效 | 桌面端是安装副本、web 是 symlink;两者还是不同 profile、不同端口 | 先确认你在看哪个进程,再查三层装载 | [PITFALLS.md](./PITFALLS.md) §22、[SETUP.md](./SETUP.md) §4 注记 |
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 3. 看着像坏了,其实是设计
|
|
73
|
+
|
|
74
|
+
先读这一节,能省掉一半的「修」。这些行为都有明确理由,**不要当成缺陷去打补丁**:
|
|
75
|
+
|
|
76
|
+
- **没有「登出」按钮**:能停的是 provider 开关,不是令牌。「删除已存 JWT」是隐私动作,被刻意移出日常操作;真正吊销只能在桌面 App 里登出 / 改密码。见 [SETUP.md](./SETUP.md) §3。
|
|
77
|
+
- **「可看图」清单为空**:Agnes 目录条目**不带** `input_modalities`(实测只有 `id`/`object`/`created`/`owned_by`/`supported_endpoint_types` 五个字段),`identifyVisionModel` 只能读目录 → 恒 false。这是**「读不到」而不是「没有」**——官方文档(`AGNES-API-docs/`)明说 `agnes-3.0-flash` / `agnes-2.5-pro` / `agnes-2.5-flash` 支持「文本 + 图像 URL 输入」,所以空清单不能当作「真没有可看图模型」的证据。见 [AGNES-API.md](./AGNES-API.md) §7.1。
|
|
78
|
+
- **「可看图」整行消失**:不是被裁掉,是没有 API Key → 目录没拉 → 视觉清单无从生成。见 §1.3 第一行。
|
|
79
|
+
- **改了设置不重启也生效,但工具挂载要等下次启动**:开关与勾选走热生效;agent 工具没有 unregister 语义,挂载 / 缺席只发生在 Host 启动时。见 [PROVIDER-HOT-RELOAD.md](./PROVIDER-HOT-RELOAD.md)。
|
|
80
|
+
- **拼错的配置键只记日志、不报错**:老 patch 里的历史键不该把面板打成配置错误。见 [SETUP.md](./SETUP.md) §3。
|
|
81
|
+
- **429 与额度耗尽不是一件事**:402 是额度不足,429 多为速率上限(订阅用尽也可能回 429)。见 [AGNES-API.md](./AGNES-API.md)。
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 4. 为什么这份文件没有「症状码」
|
|
86
|
+
|
|
87
|
+
兄弟仓 `dsh-connect-sensenova-token-plan` 的排障入口是按**稳定症状码**组织的(`SYMPTOM` / `SYMPTOM_HINT` + `doctor.report.symptoms`,并由 `test/doctor.test.mjs` 双向钉住文档锚点)。本仓目前只有下半层:`CODE`(平台失败码,进 `body.code`)与 `doctor` 的 scope 事实(`providerPanel` / `drawPanel` / `unreadable` / `writable`),**没有从「用户现象」到「失败码」的映射层**。
|
|
88
|
+
|
|
89
|
+
所以本文件按**界面原文 + 文案键**索引,不发明一套本仓代码里不存在的码——那种码没有任何东西守着,会静默腐烂(本仓对「文档里的量化断言必须可复现」的纪律见 [CONTRIBUTING.md](./CONTRIBUTING.md))。要升级成兄弟仓那套,需要同时动 `src/host/codes.ts`(加 `SYMPTOM` / `SYMPTOM_HINT`)、`src/host/doctor.ts`(`report.symptoms`)与 `test/doctor.test.mjs`(锚点双向钉住),属另案。
|
|
@@ -2087,28 +2087,73 @@ function agnescodeRequestHeaders() {
|
|
|
2087
2087
|
return { ...AGNESCODE_BASE_HEADERS };
|
|
2088
2088
|
}
|
|
2089
2089
|
/**
|
|
2090
|
-
*
|
|
2091
|
-
*
|
|
2092
|
-
*
|
|
2093
|
-
*
|
|
2094
|
-
*
|
|
2095
|
-
*
|
|
2096
|
-
*
|
|
2097
|
-
*
|
|
2098
|
-
*
|
|
2099
|
-
*
|
|
2100
|
-
*
|
|
2101
|
-
*
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2090
|
+
* Models confirmed on this gateway to CANNOT read an image, in a LIVE probe
|
|
2091
|
+
* with the same picture + question the allow side was measured with (a live
|
|
2092
|
+
* re-probe runs against this set, so the day a gateway routing change makes a
|
|
2093
|
+
* denied model able to see, the baseline turns and the entry comes out).
|
|
2094
|
+
* Membership is a dated measurement, never a design call about the model.
|
|
2095
|
+
*
|
|
2096
|
+
* STARTS EMPTY: nothing about this gateway is known to be blind. An entry is
|
|
2097
|
+
* added only when a user reports a model that cannot see the image (with a
|
|
2098
|
+
* probe that proves it), and it leaves the same way. Until a report lands
|
|
2099
|
+
* here, every untagged row is OFFERED image input by default and labelled
|
|
2100
|
+
* unverified — see the companion verdict below.
|
|
2101
|
+
* @type {ReadonlySet<string>}
|
|
2102
|
+
*/
|
|
2103
|
+
const AGNESCODE_VISION_DENYLIST = Object.freeze(/* @__PURE__ */ new Set([]));
|
|
2104
|
+
/**
|
|
2105
|
+
* Whether an AgnesCode roster row accepts image input, together with how the
|
|
2106
|
+
* verdict was reached.
|
|
2107
|
+
*
|
|
2108
|
+
* The BFF's own `/models` rows declare no modality field (only `model_type:
|
|
2109
|
+
* "text"`), so `fetchAgnescodeCatalog` pins `vision: false` — a raw fact about
|
|
2110
|
+
* what the wire said, NOT a verdict. The verdict is made here. The ladder is
|
|
2111
|
+
* asymmetric on purpose: a false "it can see" (an image sent to a model that
|
|
2112
|
+
* ignores it, HTTP 200, no error anywhere upstream) is worse than a hidden
|
|
2113
|
+
* capability, so the DEFAULT is to offer the entry but SAY it is unverified,
|
|
2114
|
+
* and only a dated live probe may deny it:
|
|
2115
|
+
*
|
|
2116
|
+
* 1. `AGNESCODE_VISION_DENYLIST` wins: a row confirmed blind in a live probe is
|
|
2117
|
+
* not offered image input; `source: "denied"` tells the panel to draw no chip.
|
|
2118
|
+
* 2. A row carrying `vision: true` (a future BFF that starts declaring its own
|
|
2119
|
+
* modality) is `declared`.
|
|
2120
|
+
* 3. The `PROBED_VISION` official-docs table (llm-models.ts) — the Agnes family
|
|
2121
|
+
* the platform already proved — is `measured`.
|
|
2122
|
+
* 4. Otherwise: `assumed` (true). A model the docs table never mentioned is
|
|
2123
|
+
* still offered image input — it does not vanish from the image picker —
|
|
2124
|
+
* and the panel marks it unverified. Reading "absent from `PROBED_VISION`"
|
|
2125
|
+
* as "text-only" is what made every newly-listed gateway model silently
|
|
2126
|
+
* lose the attachment entry.
|
|
2105
2127
|
* @param {object} row - one AgnesCode roster row (must carry `id`).
|
|
2128
|
+
* @returns {object} the verdict and how it was reached.
|
|
2129
|
+
*/
|
|
2130
|
+
function agnescodeVisionVerdict(row) {
|
|
2131
|
+
const id = str(row?.id, "");
|
|
2132
|
+
if (id !== "" && AGNESCODE_VISION_DENYLIST.has(id)) return {
|
|
2133
|
+
vision: false,
|
|
2134
|
+
source: "denied"
|
|
2135
|
+
};
|
|
2136
|
+
if (row?.vision === true) return {
|
|
2137
|
+
vision: true,
|
|
2138
|
+
source: "declared"
|
|
2139
|
+
};
|
|
2140
|
+
if (id !== "" && PROBED_VISION[id] === true) return {
|
|
2141
|
+
vision: true,
|
|
2142
|
+
source: "measured"
|
|
2143
|
+
};
|
|
2144
|
+
return {
|
|
2145
|
+
vision: true,
|
|
2146
|
+
source: "assumed"
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
/**
|
|
2150
|
+
* The boolean-only view of {@link agnescodeVisionVerdict}, kept for call sites
|
|
2151
|
+
* and tests that want the capability bit without the provenance.
|
|
2152
|
+
* @param {object} row - one AgnesCode roster row.
|
|
2106
2153
|
* @returns {boolean} whether the row accepts image input.
|
|
2107
2154
|
*/
|
|
2108
2155
|
function agnescodeVisionOf(row) {
|
|
2109
|
-
|
|
2110
|
-
const id = str(row?.id, "");
|
|
2111
|
-
return PROBED_VISION[id] === true;
|
|
2156
|
+
return agnescodeVisionVerdict(row).vision;
|
|
2112
2157
|
}
|
|
2113
2158
|
/**
|
|
2114
2159
|
* The AgnesCode model rows the panel shows and the adapter offers.
|
|
@@ -2128,10 +2173,12 @@ function agnescodeRoster(catalog, maxTokensOverrides) {
|
|
|
2128
2173
|
const multiplier = numZeroOk(row?.multiplier);
|
|
2129
2174
|
const maxOutputLength = agnescodeMaxTokensFor(id, row?.maxOutputLength, maxTokensOverrides);
|
|
2130
2175
|
const displayLabel = str(row?.displayLabel, "");
|
|
2176
|
+
const verdict = agnescodeVisionVerdict(row);
|
|
2131
2177
|
out.push({
|
|
2132
2178
|
id,
|
|
2133
2179
|
name: str(row?.name, id),
|
|
2134
|
-
vision:
|
|
2180
|
+
vision: verdict.vision,
|
|
2181
|
+
visionSource: verdict.source,
|
|
2135
2182
|
memberOnly: row?.memberOnly === true,
|
|
2136
2183
|
...multiplier === void 0 ? {} : { multiplier },
|
|
2137
2184
|
...displayLabel === "" ? {} : { displayLabel },
|
package/lib/index.js
CHANGED
|
@@ -106,7 +106,7 @@ function throttleDir() {
|
|
|
106
106
|
* @param {() => number} now - clock source.
|
|
107
107
|
* @returns {{code: string, parked: boolean, until: number|null, attempt: number}|null}
|
|
108
108
|
*/
|
|
109
|
-
function parse(raw, now) {
|
|
109
|
+
function parse$1(raw, now) {
|
|
110
110
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
111
111
|
const body = raw;
|
|
112
112
|
const version = num(body.version, 0);
|
|
@@ -119,7 +119,7 @@ function parse(raw, now) {
|
|
|
119
119
|
};
|
|
120
120
|
const migrated = migrateToCurrent(body);
|
|
121
121
|
if (migrated === null) return null;
|
|
122
|
-
return parse({
|
|
122
|
+
return parse$1({
|
|
123
123
|
...migrated,
|
|
124
124
|
version: THROTTLE_VERSION
|
|
125
125
|
}, now);
|
|
@@ -158,7 +158,7 @@ function createFileThrottleStore({ dir = throttleDir(), now = Date.now } = {}) {
|
|
|
158
158
|
const file = join(dir, "throttle.json");
|
|
159
159
|
return {
|
|
160
160
|
async read() {
|
|
161
|
-
return parse(await readStateJson(file), now);
|
|
161
|
+
return parse$1(await readStateJson(file), now);
|
|
162
162
|
},
|
|
163
163
|
async write(state) {
|
|
164
164
|
const temporary = temporaryOf(dir, "throttle.json");
|
|
@@ -201,7 +201,7 @@ function createMemoryThrottleStore(now = Date.now) {
|
|
|
201
201
|
let held = null;
|
|
202
202
|
return {
|
|
203
203
|
async read() {
|
|
204
|
-
return parse(held, now);
|
|
204
|
+
return parse$1(held, now);
|
|
205
205
|
},
|
|
206
206
|
async write(state) {
|
|
207
207
|
held = {
|
|
@@ -2260,6 +2260,171 @@ function createFileAgnescodeModelsStore(options = {}) {
|
|
|
2260
2260
|
};
|
|
2261
2261
|
}
|
|
2262
2262
|
|
|
2263
|
+
//#endregion
|
|
2264
|
+
//#region src/host/agnescode-roster-seen-store.ts
|
|
2265
|
+
/**
|
|
2266
|
+
* dsh-connect-agnes-token-plan — when each AgnesCode roster id was FIRST seen.
|
|
2267
|
+
*
|
|
2268
|
+
* WHY this exists (and why it is a separate file, not `agnescode-store.ts`): the
|
|
2269
|
+
* gateway's catalogue rows carry no "listed since" fact — the model list is the
|
|
2270
|
+
* only signal that "a model is new to this user", and it is knowable only by
|
|
2271
|
+
* diffing today's roster against a roster THIS plugin wrote down. In memory
|
|
2272
|
+
* would do for one process, but the composer pill has to survive a restart to
|
|
2273
|
+
* be worth rendering, so the history lives in a state file of its own (the same
|
|
2274
|
+
* discipline as `agnescode-stats-store.ts`: one concern, one file, one reader).
|
|
2275
|
+
*
|
|
2276
|
+
* Semantics, stated once because two consumers read them:
|
|
2277
|
+
* - the FIRST ever write SEEDS: it stamps the whole roster one window back and
|
|
2278
|
+
* announces nothing new — an upgrade must not tell every existing user the
|
|
2279
|
+
* entire roster is brand new;
|
|
2280
|
+
* - an id that LEAVES the roster is dropped, so a model the gateway re-lists
|
|
2281
|
+
* later counts as new again (a re-listing IS a change the reader may want);
|
|
2282
|
+
* - "new" is a WINDOW, not a tick: an id first seen less than
|
|
2283
|
+
* {@link AGNESCODE_ROSTER_NEW_WINDOW_MS} ago stays new across polls, so the
|
|
2284
|
+
* marker does not vanish between two catalogue reads;
|
|
2285
|
+
* - a roster larger than {@link AGNESCODE_ROSTER_SEEN_MAX_IDS} is a shape
|
|
2286
|
+
* drift: nothing is written or announced for its duration (evicting a live
|
|
2287
|
+
* id would re-announce it next poll and the count would oscillate);
|
|
2288
|
+
* - an unrecognised or foreign-version file reads as "no history" (PITFALLS
|
|
2289
|
+
* §23), but the FIRST write over it is REFUSED, not stomped — the ADR-006
|
|
2290
|
+
* discipline. A caller that cannot write degrades to "no marker", never to a
|
|
2291
|
+
* wrong one.
|
|
2292
|
+
*
|
|
2293
|
+
* The marker is only fed from a LIVE roster: the static fallback table is this
|
|
2294
|
+
* plugin's own text, and diffing it would announce our rows as gateway news.
|
|
2295
|
+
*
|
|
2296
|
+
* @module dsh-connect-agnes-token-plan/agnescode-roster-seen-store
|
|
2297
|
+
*/
|
|
2298
|
+
/** Shape version, bumped when the persisted form changes incompatibly. */
|
|
2299
|
+
const AGNESCODE_ROSTER_SEEN_VERSION = 1;
|
|
2300
|
+
/**
|
|
2301
|
+
* How long a first sighting keeps reading as "new". Seven days: long enough that
|
|
2302
|
+
* a user who opens the panel twice a week still catches it, short enough that a
|
|
2303
|
+
* stale marker cannot outlive the model it announces.
|
|
2304
|
+
*/
|
|
2305
|
+
const AGNESCODE_ROSTER_NEW_WINDOW_MS = 6048e5;
|
|
2306
|
+
/**
|
|
2307
|
+
* The largest roster one file will track — a SHAPE-DRIFT guard, not a queue.
|
|
2308
|
+
* A roster past the cap is read as a broken/looping producer: NOTHING is
|
|
2309
|
+
* written or announced for its duration, and the previous map is kept as-is.
|
|
2310
|
+
*/
|
|
2311
|
+
const AGNESCODE_ROSTER_SEEN_MAX_IDS = 200;
|
|
2312
|
+
/** Where the record lives: the SHARED directory, `$DSH_HOME/state/<plugin>`. */
|
|
2313
|
+
function agnescodeRosterSeenDir() {
|
|
2314
|
+
return stateDir(name);
|
|
2315
|
+
}
|
|
2316
|
+
/**
|
|
2317
|
+
* Read one persisted map, keeping only well-formed entries. A non-numeric stamp
|
|
2318
|
+
* is dropped rather than coerced (a coerced `0` would read as "seen since 1970"
|
|
2319
|
+
* and mark nothing new forever). Never throws.
|
|
2320
|
+
* @param {unknown} raw - the persisted or posted value.
|
|
2321
|
+
* @returns {Record<string, number>} the usable entries (possibly empty).
|
|
2322
|
+
*/
|
|
2323
|
+
function normalizeRosterSeen(raw) {
|
|
2324
|
+
const source = raw !== null && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
2325
|
+
const seen = {};
|
|
2326
|
+
for (const [id, stamp] of Object.entries(source)) {
|
|
2327
|
+
if (id === "" || typeof stamp !== "number" || !Number.isFinite(stamp) || stamp <= 0) continue;
|
|
2328
|
+
seen[id] = stamp;
|
|
2329
|
+
}
|
|
2330
|
+
return seen;
|
|
2331
|
+
}
|
|
2332
|
+
/**
|
|
2333
|
+
* Keep the map ONLY while the roster is a sane size: a roster past the cap is a
|
|
2334
|
+
* shape drift, so NOTHING is written or announced and the previous map survives.
|
|
2335
|
+
* @param {Record<string, number>} next - this round's map.
|
|
2336
|
+
* @returns {Record<string, number>|null} the map to persist, or `null` to skip.
|
|
2337
|
+
*/
|
|
2338
|
+
function bounded(next) {
|
|
2339
|
+
return Object.keys(next).length <= 200 ? next : null;
|
|
2340
|
+
}
|
|
2341
|
+
/** Is the parsed payload a shape THIS build recognises (a known version)? */
|
|
2342
|
+
function parse(raw) {
|
|
2343
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
2344
|
+
const body = raw;
|
|
2345
|
+
if (body.version !== 1) return null;
|
|
2346
|
+
return { seen: normalizeRosterSeen(body.seen) };
|
|
2347
|
+
}
|
|
2348
|
+
/**
|
|
2349
|
+
* The file-backed first-sighting history.
|
|
2350
|
+
* @param {object} [options] - `{ dir, now }` (a clock source for the tests).
|
|
2351
|
+
* @returns {object} the store.
|
|
2352
|
+
*/
|
|
2353
|
+
function createFileAgnescodeRosterSeenStore({ dir = agnescodeRosterSeenDir(), now = Date.now } = {}) {
|
|
2354
|
+
const file = join(dir, "agnescode-roster-seen.json");
|
|
2355
|
+
const readThrough = async () => {
|
|
2356
|
+
const raw = await readStateJson(file);
|
|
2357
|
+
return {
|
|
2358
|
+
raw: raw === null ? null : raw,
|
|
2359
|
+
parsed: parse(raw)
|
|
2360
|
+
};
|
|
2361
|
+
};
|
|
2362
|
+
/** Persist one map (atomic). Refuses to stomp a foreign-version file (ADR-006). */
|
|
2363
|
+
const persist = async (seen) => {
|
|
2364
|
+
const { raw } = await readThrough();
|
|
2365
|
+
if (raw !== null && parse(raw) === null) return;
|
|
2366
|
+
const body = JSON.stringify({
|
|
2367
|
+
version: 1,
|
|
2368
|
+
seen,
|
|
2369
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2370
|
+
});
|
|
2371
|
+
const temporary = temporaryOf(dir, "agnescode-roster-seen.json");
|
|
2372
|
+
await ensureStateDir(dir);
|
|
2373
|
+
await writeStateFile(file, body, { temporary });
|
|
2374
|
+
};
|
|
2375
|
+
return {
|
|
2376
|
+
/** The whole first-sighting map. */
|
|
2377
|
+
async seen() {
|
|
2378
|
+
const { parsed } = await readThrough();
|
|
2379
|
+
return parsed?.seen ?? {};
|
|
2380
|
+
},
|
|
2381
|
+
/** Whether this store has ever written a recognisable history. */
|
|
2382
|
+
async isSet() {
|
|
2383
|
+
const { parsed } = await readThrough();
|
|
2384
|
+
return parsed !== null;
|
|
2385
|
+
},
|
|
2386
|
+
/**
|
|
2387
|
+
* Record today's roster and answer which ids still read as new.
|
|
2388
|
+
* @param {string[]} ids - the FULL live roster as the gateway just listed it.
|
|
2389
|
+
* @param {number} [nowMs] - the clock (tests inject it).
|
|
2390
|
+
* @returns {Promise<{newIds: string[]}>} ids first seen inside the window,
|
|
2391
|
+
* oldest first; empty on the seeding write and on a roster with nothing fresh.
|
|
2392
|
+
*/
|
|
2393
|
+
async note(ids, nowMs = now()) {
|
|
2394
|
+
const list = Array.isArray(ids) ? ids.filter((id) => typeof id === "string" && id !== "") : [];
|
|
2395
|
+
const unique = [...new Set(list)];
|
|
2396
|
+
if (unique.length === 0) return { newIds: [] };
|
|
2397
|
+
const nowT = Number.isFinite(nowMs) ? nowMs : now();
|
|
2398
|
+
const { raw, parsed } = await readThrough();
|
|
2399
|
+
if (raw !== null && parsed === null) return { newIds: [] };
|
|
2400
|
+
const previous = parsed?.seen ?? {};
|
|
2401
|
+
const next = {};
|
|
2402
|
+
const seeded = parsed === null && raw === null;
|
|
2403
|
+
for (const id of unique) {
|
|
2404
|
+
const first = previous[id];
|
|
2405
|
+
next[id] = typeof first === "number" && first > 0 ? Math.min(first, nowT) : seeded ? nowT - AGNESCODE_ROSTER_NEW_WINDOW_MS : nowT;
|
|
2406
|
+
}
|
|
2407
|
+
const trimmed = bounded(next);
|
|
2408
|
+
if (trimmed === null) return { newIds: [] };
|
|
2409
|
+
if (seeded || Object.keys(previous).length !== Object.keys(trimmed).length || Object.keys(trimmed).some((id) => previous[id] !== trimmed[id])) await persist(trimmed);
|
|
2410
|
+
return { newIds: seeded ? [] : Object.entries(trimmed).filter(([, first]) => nowT - first < AGNESCODE_ROSTER_NEW_WINDOW_MS).sort((left, right) => left[1] - right[1]).map(([id]) => id) };
|
|
2411
|
+
},
|
|
2412
|
+
/** Forget the history (the next `note` reseeds). */
|
|
2413
|
+
async forget() {
|
|
2414
|
+
const { raw } = await readThrough();
|
|
2415
|
+
if (raw !== null && parse(raw) === null) return;
|
|
2416
|
+
const body = JSON.stringify({
|
|
2417
|
+
version: 1,
|
|
2418
|
+
seen: {},
|
|
2419
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2420
|
+
});
|
|
2421
|
+
const temporary = temporaryOf(dir, "agnescode-roster-seen.json");
|
|
2422
|
+
await ensureStateDir(dir);
|
|
2423
|
+
await writeStateFile(file, body, { temporary });
|
|
2424
|
+
}
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2263
2428
|
//#endregion
|
|
2264
2429
|
//#region src/host/switch-precedence.ts
|
|
2265
2430
|
/**
|
|
@@ -5994,7 +6159,7 @@ function readThroughAgnescode(key, cache, inflight, produce) {
|
|
|
5994
6159
|
* @returns {Function} the `off()` unregister callback.
|
|
5995
6160
|
*/
|
|
5996
6161
|
function registerAgnescodeRoute(ctx, wiring) {
|
|
5997
|
-
const { settings, agnescodeStore, agnescodeSwitch, agnescodePublisher, agnescodeModels, cache, inflight, agnescodeFetch } = wiring;
|
|
6162
|
+
const { settings, agnescodeStore, agnescodeSwitch, agnescodePublisher, agnescodeModels, agnescodeRosterSeen, cache, inflight, agnescodeFetch } = wiring;
|
|
5998
6163
|
let lastHarvest = null;
|
|
5999
6164
|
let agnescodeHarvestInFlight = null;
|
|
6000
6165
|
let agnescodeLoginInFlight = null;
|
|
@@ -6067,6 +6232,12 @@ function registerAgnescodeRoute(ctx, wiring) {
|
|
|
6067
6232
|
const publisherState = agnescodePublisher?.state ?? null;
|
|
6068
6233
|
const enabledModelIds = agnescodeModels !== null && agnescodeModels !== void 0 ? await agnescodeModels.listEnabledIds().catch(() => []) : [];
|
|
6069
6234
|
const { usage, ...rateLimit } = await agnescodeStats.read();
|
|
6235
|
+
let newModelIds;
|
|
6236
|
+
if (liveRoster && agnescodeRosterSeen !== null && agnescodeRosterSeen !== void 0) {
|
|
6237
|
+
const offered = roster.map((r) => r.id).filter((id) => typeof id === "string" && id !== "");
|
|
6238
|
+
const noted = await agnescodeRosterSeen.note(offered);
|
|
6239
|
+
if (noted.newIds.length > 0) newModelIds = noted.newIds;
|
|
6240
|
+
}
|
|
6070
6241
|
return {
|
|
6071
6242
|
ok: true,
|
|
6072
6243
|
enabled: effectiveEnabled,
|
|
@@ -6083,6 +6254,7 @@ function registerAgnescodeRoute(ctx, wiring) {
|
|
|
6083
6254
|
models: roster,
|
|
6084
6255
|
modelsSource: liveRoster ? "live" : "fallback",
|
|
6085
6256
|
...liveRoster ? {} : { modelsAsOf: AGNESCODE_FALLBACK_ASOF },
|
|
6257
|
+
...newModelIds !== void 0 ? { newModelIds } : {},
|
|
6086
6258
|
enabledModelIds,
|
|
6087
6259
|
providerRegistered: publisherState?.registered === true,
|
|
6088
6260
|
...publisherState?.error !== null && publisherState?.error !== void 0 ? { providerError: publisherState.error } : {},
|
|
@@ -7760,6 +7932,7 @@ function apply(ctx, config = {}, deps = {}) {
|
|
|
7760
7932
|
const agnescodeStore = createAgnescodeStore({ credentials: () => ctx.get("credentials") ?? null });
|
|
7761
7933
|
const agnescodeSwitch = createFileAgnescodeStore({ profile });
|
|
7762
7934
|
const agnescodeModels = createFileAgnescodeModelsStore({ profile });
|
|
7935
|
+
const agnescodeRosterSeen = createFileAgnescodeRosterSeenStore();
|
|
7763
7936
|
const { publisher: agnescodePublisher, seed: agnescodeSeed } = wireAgnescodePublisher({
|
|
7764
7937
|
store: agnescodeStore,
|
|
7765
7938
|
panelSwitch: () => agnescodeSwitch.enabled().catch(() => null),
|
|
@@ -7806,6 +7979,7 @@ function apply(ctx, config = {}, deps = {}) {
|
|
|
7806
7979
|
agnescodeStore,
|
|
7807
7980
|
agnescodeSwitch,
|
|
7808
7981
|
agnescodeModels,
|
|
7982
|
+
agnescodeRosterSeen,
|
|
7809
7983
|
agnescodePublisher,
|
|
7810
7984
|
agnescodeFetch: deps.agnescodeFetch ?? ((url, options) => fetch(url, options))
|
|
7811
7985
|
};
|
package/locale/en.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
|
-
"title": "Agnes
|
|
3
|
+
"title": "Agnes Model Access",
|
|
4
4
|
"description": "One-tap connect Agnes language, image and video models into DSH (off by default), with image recognition and thinking level filled in automatically. After signing in to the Agnes console (re-login uses AGNES_PASSWORD), you can see the quota windows (5-hour requests, weekly requests, daily images, video), account-wide cumulative usage, and a usage pill beside the input box. AgnesCode desktop models can be connected too (Windows only), re-reading the desktop app login when the token expires, with rate-limit hits backing off on their own."
|
|
5
5
|
}
|
|
6
6
|
}
|
package/locale/zh.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
|
-
"title": "Agnes
|
|
3
|
+
"title": "Agnes 模型接入",
|
|
4
4
|
"description": "一键接入 Agnes 的语言、生图、视频模型到 DSH(默认关),自动填入图片识别与思考强度;登录 Agnes 控制台后(重登需 AGNES_PASSWORD),可看额度窗口(5 小时请求、每周请求、每日图片、视频)、账号累计用量,以及输入框旁的用量胶囊;也支持接入 AgnesCode 桌面端模型(仅 Windows),令牌到期时重新读取桌面端登录态,撞限额自动退避重试。"
|
|
5
5
|
}
|
|
6
6
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-connect-agnes-token-plan",
|
|
3
3
|
"displayName": "DSH Connect Agnes Token Plan",
|
|
4
4
|
"icon": "./icon.svg",
|
|
5
|
-
"version": "0.13.
|
|
5
|
+
"version": "0.13.2",
|
|
6
6
|
"description": "One-tap connect Agnes language, image and video models into DSH (off by default), with image recognition and thinking level filled in automatically. After signing in to the Agnes console (re-login uses AGNES_PASSWORD), you can see the quota windows (5-hour requests, weekly requests, daily images, video), account-wide cumulative usage, and a usage pill beside the input box. AgnesCode desktop models can be connected too (Windows only), re-reading the desktop app login when the token expires, with rate-limit hits backing off on their own.",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"dsh-plugin",
|