dsh-agy-link 0.4.42 → 0.4.43

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 CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.43 (2026-10-06)
4
+
5
+ ### English
6
+
7
+ - **Fix (Windows): Chinese tool output is no longer mangled (issue #37).** Windows pipes carry bytes in the child's console code page: on a zh-CN system agy captures pwsh output as CP936 (GBK), and the plugin decoded every stream as UTF-8 — so the Chinese refusal text arrived as mojibake, and because that text is what agy stores in its conversation database the damage was permanent. stdout/stderr are now read as raw bytes and decoded **per line** through the console code page (`chcp` probe → GBK/Big5/Shift_JIS/EUC-KR/Windows-125x), falling back to UTF-8 whenever a line is valid UTF-8; POSIX behaviour is bit-for-bit unchanged (the probe returns `utf-8`). Lines — not chunks — are the decoding unit because a multi-byte character can straddle a chunk boundary but never a newline.
8
+ - **Fix: `dsh plugin add git+https://…` installs now build the plugin (issue #38).** `dist/` is not committed and only `prepack` existed, which never runs for git dependencies — so a hub/git install produced a package without `dist/` and failed to load. A `prepare` script now builds on git installs (npm/pnpm install devDependencies for git deps first); npm consumers are unaffected because `prepare` does not run for published tarballs. The install also aborted **earlier** than that: `@deepseek-ai/dsh-client-locale` was declared as a **non-optional** peer, so npm's automatic peer installation tried to fetch the unpublished `@deepseek-ai/dsh-paths@^0.0.1-rc.1` chain from the public registry and failed with a 404 — every `@deepseek-ai/*` peer is now optional (the host provides them at runtime and the bundle keeps them external), matching how third-party DSH plugins declare theirs.
9
+ - **Fix: the live "User location is not supported for the API use" refusal is now recognized, explained and routed around.** agy exits 3 with a `FAILED_PRECONDITION (code 400)` result envelope for this refusal, but the classifier only knew the older issue-#32 wording — so it surfaced as a bare `PROCESS_EXIT`, the sticky account never moved, and every retry hit the same unsupported location (hence the recurring failure). The signature is now matched, the failure carries a dedicated, retryable `ELIGIBILITY` code plus the actionable hint, and the refused account is rotated off the sticky pointer before the error surfaces (no cooldown penalty) — the automatic retry is then served by an account whose own proxy/egress region is supported.
10
+ - **Add: rotation now drains the account whose quota expires soonest (use-it-or-lose-it).** Sequential fallback used to advance in pool order, which could leave an account sitting on 60 % of a 5-hour window that resets in 20 minutes — that residual is discarded at the reset, so it was wasted. When the sticky account is drained (hard quota, cooldown, silent timeout) the next account is now the eligible one whose quota window refreshes **soonest** (5-hour window or weekly cap, whichever comes first); ties and accounts with no quota metadata yet keep the previous cyclic order, so a drained slot never bounces back to the primary, and rotation still never cools an account down on its own.
11
+ - **Fix: quota exhaustion and silent timeouts now retry onto the next account instead of dead-ending.** A hard server-issued quota / rate-limit refusal was classified as the generic `AGY_ERROR`, which the provider retry policy never retried — the account cooled down and the sticky pointer advanced, but the turn still surfaced a user-visible error while a healthy account sat idle. It now carries a dedicated, retryable `RATE_LIMIT` code, so the automatic retry is served by the next account in the pool. Deterministic `AGY_ERROR`s (bad region, empty request, pool fully cooled) deliberately stay single-shot. A **silent** timeout (no stdout event at all — unreachable Google or a wedged credential) now rotates the sticky pointer to the next account for the retry *without* a cooldown, so one account's black hole cannot masquerade as "all accounts in cooldown". Finally, `autoFallbackModel` is no longer useless: the hardcoded `gemini-3.5/3.6-flash` list downgraded *within* the exhausted family (all Gemini models share one quota pool), so a drained Google family could never be rescued — it now fails over across families (Gemini ⇄ Claude ⇄ GPT-OSS), which have separate quotas.
12
+ - **Fix: Antigravity console and chat toolbar now immediately display the active account when switching or after quota exhaustion.** When an account exhausted its 5h or weekly quota or hit rate limits, the scheduler advanced to the next backup account, but `lastActiveAccountId` was not proactively updated, leaving the console showing an outdated badge or no badge at all. `recordFailure` and `updateAccountQuotas` now proactively advance the active pointer immediately, self-heal client badges away from cooldown/quarantined accounts, clean up pointers when accounts are deleted/disabled/quarantined, and the chat toolbar button now visibly displays the active account alias (`AGY · <alias>`) in real-time. Furthermore, caller-aborted runs (`outcome.aborted`) are exempt from recording rate-limit failures, preventing ghost cooldowns (`"agy run aborted by caller"`) from poisoning pool rotation, and any historical abort cooldowns are automatically purged on startup.
13
+ - **Fix & Add: Mermaid diagram rendering with bundled local assets and universal block sniffing.** DSH renders fences with unknown grammar (like `mermaid`) as localized code blocks (labeled "代码块" in Chinese or "Code block" in English) with stripped or non-standard language attributes. Diagram detection now universally scans `.md-code-block` elements, filters known programming languages, and sniffs Mermaid diagrams from the content. Furthermore, `mermaid.min.js` (v11.17.2) is now served directly from the local plugin endpoint (`/plugins/agy-link/mermaid.min.js`), completely eliminating external CDN timeouts, failures, or blocking.
14
+ - **Fix: code cards referenced a non-existent font token, so the code font silently fell back to the hardcoded stack.** `ROW_CSS` spelled the DSH code-font token as `--dsw-font-family-code`, but the real token is `--ds-font-family-code` (no `w`). The mistyped name has zero definitions anywhere in DSH, so every `var(--dsw-font-family-code, <fallback>)` resolved to its hardcoded fallback (`ui-monospace, SFMono-Regular, Menlo, Consolas, monospace`), which carries no CJK coverage — Chinese text inside agy tool cards rendered in the OS default (SimSun / Microsoft YaHei) instead of the host's configured code font. The three affected selectors are `.agy-tv-card` (whose `font-family` is inherited by the header, copy button and body), `.agy-tv-preview` and `.agy-tv-diff-stat`.
15
+ - **Fix: tool-hop continuations no longer re-attach every historical screenshot.** When a continuation carried no new image, the prompt builder fell back to collecting attachments from *all* earlier user turns, so the same 4–5 UI screenshots were re-staged and re-referenced on every step (observed: 61 staged files for 5 unique images in one session). Image forwarding is now watermark-aware (`SessionBinding.sentImageIds`): only attachments the current agy conversation has not seen are staged. Staged files are also keyed by attachment id instead of call index, so a legitimate re-reuse overwrites rather than accumulates.
16
+ - **Fix: account / model switches now transfer the prior conversation properly.** A brand-new agy conversation (account switch, model switch, post-compaction re-seed) used to be seeded with only `digestMaxChars` (8K), silently dropping most of a long session. First contact now uses the new `contextTransferMaxChars` budget (default 660K, sized for 1M-token-class models), and the attachments that carry the visual context ride along once via the same image watermark.
17
+
18
+ ### 中文 (Chinese)
19
+
20
+ - **修复(Windows):中文工具输出不再乱码(issue #37)。** Windows 管道里的字节使用**子进程的控制台代码页**:中文系统上 agy 捕获到的 pwsh 输出是 CP936(GBK),而插件把整条流按 UTF-8 解码——中文报错于是变成乱码;又因为这段文本正是 agy 写入会话库的内容,损坏不可追回。现在 stdout/stderr 以**原始字节**读取并按**行**用控制台代码页解码(`chcp` 探测 → GBK/Big5/Shift_JIS/EUC-KR/Windows-125x),凡是合法的 UTF-8 行仍按 UTF-8 处理;POSIX 行为逐字节不变(探测直接返回 `utf-8`)。以「行」而非「块」为解码单位,是因为多字节字符可能跨块但绝不会跨换行。
21
+ - **修复:`dsh plugin add git+https://…` 安装现在会构建插件(issue #38)。** 仓库里没有提交 `dist/`,而只有 `prepack`——它**不会**在 git 依赖安装时执行,所以市场/git 安装得到的包没有 `dist/`,加载失败。现在新增 `prepare` 脚本,git 安装时会构建(npm/pnpm 会先为 git 依赖安装 devDependencies);从 npm 安装的用户不受影响,因为 `prepare` 不会对已发布的 tarball 执行。安装其实在更早一步就已经失败:`@deepseek-ai/dsh-client-locale` 曾被声明为**非可选** peer,npm 的自动 peer 安装因此去公共 registry 拉取未发布的 `@deepseek-ai/dsh-paths@^0.0.1-rc.1` 依赖链并报 404 —— 现在所有 `@deepseek-ai/*` peer 都标为 optional(运行时由宿主提供,打包时保持 external),与第三方 DSH 插件的声明方式一致。
22
+ - **修复:线上「User location is not supported for the API use」区域拒绝现在被正确识别、解释并绕开。** 该拒绝报文是 agy 以退出码 3 + `FAILED_PRECONDITION (code 400)` 结果信封抛出的,而分类器只认得 issue #32 时期的旧措辞——于是它被当成裸 `PROCESS_EXIT`,粘性账号也不会移动,每次重试都撞同一个不支持的地区,所以你会觉得"总是出现这个问题"。现在该签名已被匹配,失败会带专用且**可重试**的 `ELIGIBILITY` 错误码与可操作提示,同时在错误抛出前把被拒账号从粘性指针上换掉(**不扣冷却**),自动重试就会落到代理/出口地区受支持的账号上。
23
+ - **新增:轮换优先使用「快过期」的账号(不用就浪费)。** 顺次兜底此前按号池顺序推进,可能让某账号 5 小时窗口里还剩 60%、20 分钟后就要刷新——这部分余量在刷新时会被直接丢弃,等于白扔。现在当粘性账号耗尽(硬限流 / 冷却 / 静默超时)时,轮换目标改为**额度窗口刷新最近**的可用账号(5 小时窗口与周额度取更早者);时间相同或尚无额度信息时沿用原有循环顺序,既避免耗尽后回弹到主账号,也保证轮换本身不会给账号扣冷却。
24
+ - **修复:额度耗尽与静默超时不再「直接失败」,而是自动重试到下一个账号。** 服务器硬性限流/额度拒绝此前被归为通用 `AGY_ERROR`,而重试策略从不重试该错误码——账号确实进了冷却、粘性指针也确实推进了,但这一轮仍然把错误抛给用户,明明还有健康账号闲着。现在它使用独立的可重试错误码 `RATE_LIMIT`,自动重试即由池中下一个账号接管。确定性的 `AGY_ERROR`(区域不可用、空请求、全池冷却)刻意保持不重试。**静默超时**(全程零 stdout 事件——连不上 Google 或凭证卡死)现在会推进粘性指针到下一个账号供重试,且**不给账号扣冷却**,避免单个账号的黑洞被误报成「所有账号都在冷却」。另外 `autoFallbackModel` 不再是废功能:原先硬编码的 `gemini-3.5/3.6-flash` 是**同族内**降级(所有 Gemini 模型共用一个额度池),Google 族耗尽时永远救不回来;现在改为**跨族兜底**(Gemini ⇄ Claude ⇄ GPT-OSS,各自独立额度)。
25
+ - **修复:账号额度用尽或切号后,Antigravity 控制台与会话工具栏即时准确展示当前使用账号。** 当首选账号 5 小时/周额度用尽或触发限流冷却时,调度器虽能在内部切到次选账号,但此前 `lastActiveAccountId` 未被主动更新,导致管理控制台的绿色「当前使用」徽标停留在旧账号上或未点亮。现在 `recordFailure` 与 `updateAccountQuotas` 在耗尽时主动推进激活指针,前端直接信赖后端权威计算的当前账号;会话顶栏按钮直接常驻显示当前账号(如 `AGY · g98`),一目了然;同时修复了用户主动中断(Stop / 插话抢占)被误判为限流并给账号扣上“幽灵冷却”(`"agy run aborted by caller"`)的严重缺陷,启动时自动清理历史幽灵冷却。
26
+ - **修复与新增:Mermaid 图表离线本地直出与通用代码块识别。** DSH 的 Markdown 渲染管线没有 Mermaid 语法高亮,未识别的围栏会被渲染为“代码块”(英文 "Code block")且语言标识可能丢失或非标准。探测机制现升级为全覆盖扫描 `.md-code-block`,排除常见编程语言后智能探测图表语法;并将 `mermaid.min.js` (v11.17.2) 随插件本地内置并由插件端口直出(`/plugins/agy-link/mermaid.min.js`),彻底告别外网 CDN 超时、连接挂起或被阻断导致的图表无法渲染问题。
27
+ - **修复:代码卡片引用了一个不存在的字体 token,导致代码字体静默回落到硬编码兜底栈。** `ROW_CSS` 里把 DSH 的代码字体 token 写成了 `--dsw-font-family-code`,而真实 token 是 `--ds-font-family-code`(无 `w`)。这个拼错的名称在整个 DSH 代码库里零定义,因此每一处 `var(--dsw-font-family-code, <兜底>)` 都取到硬编码兜底值 `ui-monospace, SFMono-Regular, Menlo, Consolas, monospace`——该栈不含中文字形,导致 agy 工具卡片里的中文以系统默认字体(宋体 / 微软雅黑)渲染,而非宿主配置的代码字体。受影响的三个选择器是 `.agy-tv-card`(其 `font-family` 会向下继承到标题栏、复制按钮与正文)、`.agy-tv-preview` 与 `.agy-tv-diff-stat`。
28
+ - **修复:工具往返续跑不再重复附加全部历史截图。** 当续跑请求里没有新图片时,提示词组装会回退成收集**全部**历史 user 轮次的附件,于是同样的 4~5 张 UI 截图在每一步都被重新落盘、重新引用(实测一个会话 61 个暂存文件只有 5 份唯一内容)。图片转发现在按水位去重(`SessionBinding.sentImageIds`):只暂存当前 agy 会话还没见过的附件。暂存文件名也改为按 attachment id 而不是调用序号,合法重用会覆盖而不是堆积。
29
+ - **修复:切换账号 / 切换模型后能正确带上此前的对话上下文。** 新建 agy 会话(切账号、切模型、压缩后重播种)此前只用 `digestMaxChars`(8K)做种子摘要,长会话的历史被大量丢掉。首次接入现在使用新的 `contextTransferMaxChars` 预算(默认 660K,面向 1M token 级模型),承载视觉上下文的图片也会通过同一水位机制补发一次。
30
+
31
+
3
32
  ## 0.4.42 (2026-09-30)
4
33
 
5
34
  ### English
@@ -20,7 +49,8 @@
20
49
  - **Fix (macOS): recurring "create login keychain" password popup.** Pool accounts run with an isolated HOME, and macOS has **no login keychain inside it** (`security default-keychain` → "A default keychain could not be found"), so agy's keyring save triggered the `system.keychain.create.loginkc` password dialog on every ~hourly token refresh; denying it fell back to the on-disk token (auth kept working — exit status 45/154 in agy logs). The plugin now provisions a throwaway per-account keychain and sets it as the default under that HOME — idempotently at boot, before every spawn and before every login — so keyring writes are silent and truly per-account.
21
50
  - **Fix: transient network errors no longer flag accounts as "needs re-login".** Both flagging paths were too eager: the run-failure path matched any message containing `/auth/i` (e.g. `oauth2.googleapis.com … i/o timeout` transport noise) and the token-refresh path flagged **any** `token endpoint 400` — including captive-portal / proxy error pages. Both now require an authoritative OAuth state (`invalid_grant` / `invalid_client` / `revoked` / `disabled` / `unauthorized_client`) and transport-shaped failures are exempt — even a genuine AUTH classification is not trusted when the message is transport-shaped. A false flag also **removed the account from pool rotation** (`selectAccount` skips authRequired), so this was silent capacity loss plus a scary badge that the next manual quota refresh would immediately disprove.
22
51
  - **Add: flagged accounts self-heal in the background.** `markAuthRequired` now stamps `authMarkedAt`; after 10 minutes the background poll includes the flagged account for one token verification — if the stored refresh token still works the flag clears itself (previously this required a manual 刷新额度 click); if it is truly dead the flag is re-stamped. Risk-control exposure: at most one token-endpoint call per 10 minutes per flagged account.
23
- - **Add: "In use" badge in the Antigravity console.** Each account card now shows a green zap badge when the sticky scheduler is currently routing runs to it (per model family, from the pool's `activeAccountIds`), answering "which account is agy using right now" at a glance. Localized (zh/en/pt-BR/es).
52
+ - **Add: "In use" badge in the Antigravity console.** Exactly **one** account card shows the green zap badge: the account of the most recent agy spawn (`lastActiveAccountId`), localized (zh/en/pt-BR/es). The per-family scheduler pointers (`activeAccountIds`) are an internal scheduling detail and can legitimately sit on families that are not running anything — deriving the badge from them lit up two cards at once, which reads as "two accounts in use".
53
+ - **Fix: a steer replacement now waits for the run it killed to exit.** A new prompt arriving while a run is live preempts that run and resumes the same agy conversation (`--conversation <id>`); the replacement spawn previously raced the abort, so two agy processes could briefly append to one conversation. The spawn now waits (bounded, 3.5 s) for the aborted run to settle.
24
54
  ## 0.4.40 (2026-09-28)
25
55
 
26
56
  ### English
@@ -38,7 +68,8 @@
38
68
  - **修复(macOS):反复弹出“创建登录钥匙串”密码框。** 池账号使用隔离 HOME,而隔离 HOME 里**没有登录钥匙串**(`security default-keychain` → "A default keychain could not be found"),agy 每次约每小时的 token 续期写入都会触发 `system.keychain.create.loginkc` 密码弹窗;取消后回退磁盘 token(认证不受影响——agy 日志里的 exit status 45/154 即此)。插件现在会为每个池账号预置一个专属的一次性钥匙串并设为该 HOME 的默认——在启动时、每次拉起前、每次登录前幂等执行——钥匙串写入从此静默,且真正做到按账号隔离。
39
69
  - **修复:瞬时网络错误不再把账号标记为「需重新登录」(误判修复)。** 两条标记路径都过于激进:run 失败路径会匹配任何包含 `/auth/i` 的报错(比如 `oauth2.googleapis.com … i/o timeout` 这种纯传输噪音);token 刷新路径会把**任何** `token endpoint 400` 都当成认证死亡——包括代理/门户返回的错误页。现在两条路径都要求出现权威的 OAuth 状态(`invalid_grant` / `invalid_client` / `revoked` / `disabled` / `unauthorized_client`),传输型失败一律豁免——即使带 AUTH 分类,只要报错文本是传输形态也不标记。误判的代价不只是吓人的徽标:被标记的账号会被调度器**排除出轮换**,等于静默损失一个账号,直到手动「刷新额度」把它证伪。
40
70
  - **新增:被标记的账号后台自动复验。** `markAuthRequired` 现在会记录 `authMarkedAt`;10 分钟后后台轮询会把该账号放进来做一次 token 验证——刷新令牌若仍可用,标记自动清除(以前必须手动点「刷新额度」);若真的失效则重新盖章。风控代价:每个被标记账号至多每 10 分钟一次 token endpoint 调用。
41
- - **新增:控制台「当前使用」标识。** 账号卡片现在会显示绿色 ⚡「当前使用」徽标(基于号池调度器的 `activeAccountIds`,按模型族标注),一眼看出 agy 当前正在用哪个账号。四种语言(zh/en/pt-BR/es)均已本地化。
71
+ - **新增:控制台「当前使用」标识。** 现在**只会有一张**账号卡片显示绿色 ⚡「当前使用」徽标:最近一次真正拉起 agy 的账号(`lastActiveAccountId`),四种语言(zh/en/pt-BR/es)均已本地化。按模型族的调度指针(`activeAccountIds`)只是内部调度细节,可能停在当前并没有跑任何东西的模型族上——用它做徽标会一次点亮两张卡,看起来像"两个账号在用"。
72
+ - **修复:插话(steer)替换进程现在会等被抢占的 run 真正退出。** run 还在跑时来一条新提示词会抢占该 run 并续用同一个 agy 会话(`--conversation <id>`);此前替换进程与 abort 存在竞争,极端情况下会有两个 agy 进程短暂同时往同一个会话追加。现在拉起前会有界等待(3.5 秒)被抢占的 run 收尾。
42
73
  ## 0.4.40 (2026-09-28)
43
74
 
44
75
  ### 中文 (Chinese)