dsh-agy-link 0.4.67 → 0.4.74

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,253 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.74 (2026-10-28)
4
+
5
+ ### English
6
+
7
+ - **Feat(warmup): opt-in 7-day weekly quota auto-warmup and manual pre-warming (PR #60).**
8
+ - `WarmupService` activates an idle account's weekly reset timer with a minimal `loadCodeAssist` + one-token `generateContent` probe, only when the weekly bucket is explicitly full and its reset instant has passed (or the bucket is genuinely cold).
9
+ - Adds "Warmup" buttons to the account card and console header. `autoWarmup` (env `DSH_AGY_AUTO_WARMUP`) defaults to **false**: enabling it scans every 5 minutes with 1.5–3s jitter, a 30-minute failure backoff, and skips accounts in cooldown / auth quarantine.
10
+ - Hardened before release: a missing `weeklyFraction` is treated as unknown (never 100%), `cloudaicompanionProject` accepts both string and `{id}` shapes and never falls back to a foreign project id (the old `bamboo-precept-lgxtn` fallback produced 403s), malformed request bodies can no longer fan out to all accounts, and the scheduler cannot overlap itself.
11
+ - **Fix(mcpBridge): pass the required abort signal to bridged tool calls (PR #57).**
12
+ - Every bridged `/call` creates an `AbortController`, passes `signal` to `tools.execute()` (DSH declares it required; tools reading `exec.signal` previously died with `Cannot read properties of undefined (reading 'aborted')`), and aborts when the MCP client goes away.
13
+ - Session keys resolve in both shapes: `session-<uuid>` for top-level web sessions and bare `<uuid>` for sub-agent/fork sessions. A resolvable bare id is never rewritten; only a miss retries with the `session-` prefix.
14
+ - **Feat(mcpBridge): audit-log every bridged call (PR #58).**
15
+ - Each `/call` logs `mcp call <tool> [<outcome>] <ms>` through the plugin logger (journald), with the caller class and failure text. Arguments are never logged.
16
+ - **Docs(mcpBridge): DSH `send_message` can be bridged (PR #58, issue #59).**
17
+ - `send_message` is not restricted: add it to `mcpToolAllowlist` and call it explicitly on the `dsh-tools-<port>` MCP server. The DSH sub-agent footer names agy's native tool, which cannot resolve DSH agent ids — root cause is upstream in `@deepseek-ai/dsh-subagent`; `agent_teams_send_message` remains the reliable path for agy members.
18
+ - **Fix(client): isolate the console overlay to eliminate scroll flicker at the root.**
19
+ - The Antigravity console is a full-viewport overlay that neither froze the page behind it nor suspended the plugin's global DOM scanners, and its backdrop carried a full-viewport `backdrop-filter: blur()`. Every frame of motion behind it (wheel/inertia scrolling, the modal's 3s status polling, scanner DOM injection) was re-sampled through the GPU filter and re-laid-out → the recurring "打开控制台后滚动闪屏" root cause.
20
+ - Removed the full-viewport backdrop blur (stable `rgba` dim instead), added an explicit overlay-isolation contract (`agyModalStore.setOpen()` toggles `html.agy-modal-open`, which locks `[data-conversation-scroll]` with `overflow:hidden` and contains overscroll), and routed decisions/images/reasoning/mermaid scanners through `isInteractionLocked()` (scroll OR overlay) with a catch-up scan on resume and cleanup on teardown.
21
+ - Regression tests pin the contract: no full-viewport `backdrop-filter`, the scroller is locked while the overlay is open, and all four scanners gate on `isInteractionLocked()`.
22
+
23
+ ### 中文
24
+
25
+ - **新增(warmup):7 天周配额自动预热与手动预热(opt-in,PR #60)。**
26
+ - `WarmupService` 只在周额度明确满额且已到重置时间(或确实处于冷启动)时,发送一次极小的 `loadCodeAssist` + 单 token `generateContent` 探活,提前激活 7 天计时器。
27
+ - 账号卡片与顶栏新增「预热」按钮。`autoWarmup`(环境变量 `DSH_AGY_AUTO_WARMUP`)默认 **false**;开启后每 5 分钟巡检一次,带 1.5–3s 随机抖动、30 分钟失败退避,并跳过冷却中/需重新认证的账号。
28
+ - 发版前加固:缺失 `weeklyFraction` 一律视为未知(不再当成 100%);`cloudaicompanionProject` 同时兼容字符串与 `{id}` 结构,且不再回退到外部共享 project id(旧的 `bamboo-precept-lgxtn` 回退会 403);畸形请求体不会触发全账号预热;调度器不会自我重叠。
29
+ - **修复(mcpBridge):为桥接工具调用补上必需的 abort signal(PR #57)。**
30
+ - 每次桥接 `/call` 创建 `AbortController`,把 `signal` 传给 `tools.execute()`(DSH 声明为必填;此前读取 `exec.signal` 的工具会抛 `Cannot read properties of undefined (reading 'aborted')`),并在 MCP 客户端断开时中止。
31
+ - 会话 key 两种形态都能解析:顶层 Web 会话为 `session-<uuid>`,子代理/fork 会话为裸 `<uuid>`;能直接解析的裸 id 绝不会被改写,只有未命中时才重试 `session-` 前缀。
32
+ - **新增(mcpBridge):每次桥接调用记审计日志(PR #58)。**
33
+ - 每次 `/call` 通过插件日志(journald)输出 `mcp call <工具> [<结果>] <毫秒>`,含调用方类别与失败信息,**不记录参数**。
34
+ - **文档(mcpBridge):DSH 的 `send_message` 可被桥接(PR #58,issue #59)。**
35
+ - `send_message` 不受限制:把它加进 `mcpToolAllowlist`,在 `dsh-tools-<port>` 这个 MCP server 上显式调用即可。DSH sub-agent footer 指向的是 agy 原生工具,无法识别 DSH agent id——根因在 `@deepseek-ai/dsh-subagent` 上游;agy 成员仍应使用 `agent_teams_send_message` 上报。
36
+ - **修复(client):从根因消除「打开 Antigravity 控制台后滚动闪屏」。**
37
+ - 控制台是全屏浮层,却既没有冻结背后页面,也没有暂停插件自身的全局 DOM 扫描器,且遮罩带全屏 `backdrop-filter: blur()`。浮层背后的一切移动/重绘(滚轮/惯性滚动、控制台每 3s 状态轮询、扫描器注入)都会每帧被 GPU 滤镜重新采样并重新布局 → 即闪屏根因。
38
+ - 移除全屏遮罩模糊(改用稳定的 `rgba` 半透明遮罩);建立显式「浮层隔离契约」:`agyModalStore.setOpen()` 切换 `html.agy-modal-open`,锁定 `[data-conversation-scroll]`(`overflow:hidden`)并阻断滚动链;decisions/images/reasoning/mermaid 四个扫描器统一走 `isInteractionLocked()`(滚动中或浮层打开),关闭后补扫一次,卸载时清理锁类。
39
+ - 回归测试锁定契约:全屏遮罩不得再出现 `backdrop-filter`、浮层打开时滚动容器必须锁定、四个扫描器必须走 `isInteractionLocked()`。
40
+
41
+ ## 0.4.73 (2026-10-27)
42
+
43
+ ### English
44
+
45
+ - **Fix(digest): preserve chronological order in conversation digest and prevent latest task misplacement.**
46
+ - `buildDigest()` previously placed the latest user task at the top of the digest array before appending older history (`kept.push(taskLine)` followed by `kept.push(...keptOther)`), completely inverting the digest timeline. Agy received the newest request at the beginning of the context and the previous turn's request/response at the bottom, leading the model to treat the previous turn as the freshest instruction and ignore the new request.
47
+ - Corrected assembly order so historical messages precede the latest task (`[...keptOther, taskPart]`) while preserving priority budget reservation for the latest task.
48
+ - **Fix(sessions): prevent false-positive compaction detection from dropping healthy conversation bindings.**
49
+ - `adapter.ts` previously invalidated the session binding whenever `messages.length < binding.lastMessageCount`. Minor fluctuations in message counts (e.g. 1-2 messages dropped by telemetry filtering or message store pruning after reconnection) falsely triggered compaction detection, deleting the valid agy session binding and forcing an unnecessary full-context digest re-seed.
50
+ - Introduced threshold-based compaction detection: true compaction requires substantial history reduction (e.g. drop by >20% or down to <=4 messages), while minor fluctuations only adjust the message watermark without invalidating the agy conversation.
51
+
52
+ ### 中文
53
+
54
+ - **修复(digest):修复会话摘要时间线逆序导致的“最新任务被历史覆盖/失忆”缺陷。**
55
+ - 此前 `buildDigest()` 为给最新用户任务预留空间,先将任务推入数组首位,再追加其余历史记录(`kept.push(taskLine)` 后跟 `kept.push(...keptOther)`),导致摘要内部时间线颠倒:最新任务被排在最开头,而上一轮的指令与回复出现在历史摘要最末尾。agy 接收后将末尾上一轮历史误判为最新要求,进而忽略了用户的最新输入。
56
+ - 修正组装顺序,保证早期历史在前、最新任务在后(`[...keptOther, taskPart]`),同时保留优先为最新任务分配字符配额的设计。
57
+ - **修复(sessions):消除 Compaction 压缩检测假阳性,防止健康会话绑定被误删。**
58
+ - 此前一旦 `messages.length < binding.lastMessageCount` 即判定为历史压缩并清空绑定。当会话闲置重新激活时,遥测过滤或消息存储微调导致的消息数微小减少(如减少 1~2 条)会误触发该逻辑,彻底删除原有的 agy 会话绑定并退化为全量摘要重放。
59
+ - 引入梯度阈值判定:仅在历史大幅度缩减(如减少超 20% 或缩减至 4 条以下)时才判定为 Context Compaction,微小消息变动仅校准水位标尺,避免误断开底层长会话。
60
+
61
+ ## 0.4.72 (2026-10-27)
62
+
63
+ ### English
64
+
65
+ - **Fix(mcpBridge): publish MCP config to the agy 1.4+ registry and carry caller identity (Issue #55, PR #56).**
66
+ - `writeMcpConfig()` previously wrote `<workspace>/.mcp.json`, but `agy` 1.4+ loads MCP servers exclusively from `~/.gemini/config/mcp_config.json` (and per-account `.gemini` sandboxes), causing bridged tool calls to fail with `No MCP servers configured`.
67
+ - Added multi-target publishing supporting the global registry, isolated account homes, and workspace scopes (`mcpConfigScope: 'global' | 'workspace' | 'both'`, plus `mcpConfigPath` override).
68
+ - Port-keyed entries (`dsh-tools-<port>`) prevent collisions between multiple DSH hosts sharing the registry, with clean atomic LIFO restoration on disposal.
69
+ - Exported `DSH_MCP_SESSION` to the agy child process and forwarded it as `X-DSH-Session` to resolve the calling agent, fixing `agent_teams tools require a calling agent (exec.agent was undefined)`.
70
+ - **Fix(credential-slot): eliminate line-barging and concurrent holder race condition in `AsyncMutex`.**
71
+ - Identified and resolved a critical race condition where releasing the mutex dropped `holders` to 0 before the scheduled microtask ran, allowing synchronous callers to barge ahead and grant multiple concurrent holders simultaneously.
72
+ - Implemented direct ownership handoff without dropping holder count when waiters are queued, guaranteeing strict mutual exclusion and FIFO queueing under high concurrency.
73
+ - **Fix(client): eliminate session console first-open flicker.**
74
+ - Session status badge now claims primary modal host synchronously on mount, preventing asynchronous portal remount flicker.
75
+ - Status polling now gracefully relaxes to a 15s interval when idle to keep the cache warm, with complete timer cleanup upon client context disposal.
76
+ - **Fix(mermaid): prevent Dagre 0x0 / 16x16 collapsed SVG artifacts.**
77
+ - Render Mermaid diagrams without an unattached mount container so viewport metrics measure accurately, and detect/heal empty or collapsed SVGs.
78
+ - **Fix(keychain): provision isolated macOS Keychain on account creation.**
79
+ - Automatically provisions isolated login keychains when adding account slots or committing staging accounts on macOS, avoiding SecurityAgent system prompts.
80
+
81
+ ### 中文
82
+
83
+ - **修复(mcpBridge):发布 MCP 配置至 agy 1.4+ 实际读取的注册表并携带调用者身份(Issue #55,PR #56)。**
84
+ - 此前 `writeMcpConfig()` 仅写入工作区 `.mcp.json`,而 `agy` 1.4+ 仅从 `~/.gemini/config/mcp_config.json`(及子账号 `.gemini` 沙箱)加载 MCP 服务,导致会话提示 `No MCP servers configured`。
85
+ - 支持多目标发布(全局注册表、多账号隔离主目录及工作区范围,提供 `mcpConfigScope: 'global' | 'workspace' | 'both'` 及 `mcpConfigPath` 覆盖配置)。
86
+ - 每个桥接实例按端口隔离服务键名(`dsh-tools-<port>`),防止多实例覆盖,并在退出时原子化恢复原有内容。
87
+ - 会话生成时将 `DSH_MCP_SESSION` 注入子进程环境,经由桥接脚本转发为 `X-DSH-Session` 请求头,在宿主端通过 `agents` 服务解析调用者身份,彻底解决 `agent_teams tools require a calling agent (exec.agent was undefined)`。
88
+ - **修复(credential-slot):消除 `AsyncMutex` 的插队竞争与并发持有者缺陷。**
89
+ - 对抗性审计发现:旧实现中 `releaseOne()` 先将 `holders` 归零后通过 `queueMicrotask` 唤醒等待者,在此微任务微小窗口内同步调用的 `acquire()` 会发生插队并导致两个调用者同时进入临界区。
90
+ - 改造为排队时直接所有权移交机制(Direct Handoff),保证严格互斥与 FIFO 先入先出顺序,消除测试套件偶发的 flake。
91
+ - **修复(client):消除控制台首次打开时的宿主交接闪烁。**
92
+ - 会话状态角标在挂载时同步声明主宿主身份,消除异步交接导致的状态闪烁与 Portal 重新渲染。
93
+ - 状态轮询在无激活视窗时平滑降频至 15 秒保活缓存,并在客户端插件卸载时完整清理定时器。
94
+ - **修复(mermaid):修复离屏/虚拟容器中 Dagre 测绘产生的 16x16 / 0x0 折叠 SVG。**
95
+ - 避免将尚未有几何尺寸的容器传递给 Mermaid 渲染,结合 `isInvalidOrCollapsedSvg` 智能识别并修复空图或折叠伪影。
96
+ - **修复(keychain):在 macOS 账号槽位创建与提交时即时初始化独立 Keychain。**
97
+ - 避免 macOS SecurityAgent 在后续刷新凭据或打开终端时弹出系统钥匙串确认弹窗。
98
+
99
+ ## 0.4.71 (2026-10-27)
100
+
101
+ ### English
102
+
103
+ - **Fix: restore Argon quota reporting for isolated Pro-tier accounts (PR #54 regression).**
104
+ - PR #54 established that retrieveUserQuotaSummary with the agy User-Agent returns the separate Gemini 4 Argon quota group for accounts on paid Pro/Ultra plans.
105
+ - Removed premature clearQuotaFamily(account.id, 'argon') in readCliUsageFamilies fast-reject branches, allowing isolated accounts to receive authentic Argon quota from Google's REST API.
106
+ - Stale/orphaned Argon entries on free-tier accounts are now cleared cleanly when an authoritative summary (CLI or REST) succeeds without an Argon quota group.
107
+ - **Feat(quota): organic randomized jitter on sequential multi-account polling.**
108
+ - Replaced the fixed 2000ms sleep between account checks with an organic 2000ms~3500ms randomized jitter delay (staggerJitterMs), avoiding rigid clock-tick request signatures against upstream endpoints.
109
+ - **Fix: every pool row displayed the same account's quota (2026-10-26 report).**
110
+ - Root cause: agy resolves its credential from the platform keystore (Windows Credential Manager, macOS Keychain, Linux Secret Service) BEFORE the per-HOME token file, and that store is a single per-user entry. Handing an isolated HOME to agy -p /usage therefore does not isolate the identity — the CLI always answers for the store's owner, so CLI-first polling filed the owner's numbers under every secondary row (all four rows showed 97% / 55% / 56% with identical reset instants).
111
+ - The CLI is now scoped to the account that can actually own that store, and everything else takes the REST path, which signs with each account's own token file and runs concurrently on every platform.
112
+ - Isolated accounts are rejected BEFORE any keystore read, so a poll no longer pays a PowerShell cold start per account; the owner itself is still proven by sameCredentialGrant, because a running secondary temporarily holds the slot and a CLI read would then return that secondary's numbers.
113
+
114
+ ### 中文
115
+
116
+ - **修复:恢复子账号(如 Pro 订阅账号)的 Argon 额度显示(PR #54 回归)。**
117
+ - PR #54 证明了发送 agy User-Agent 时,Google REST API retrieveUserQuotaSummary 会正常返回 Pro/Ultra 账号独立的 Gemini 4 Argon 额度组。
118
+ - 移除了 readCliUsageFamilies 快速拒绝分支中对 argon 的误清理,子账号回落走 REST 时可完整展示其真实的 Argon 额度。
119
+ - 免费账号或降级账号上残留的伪 Argon 额度,现在由权威汇总(CLI 或 REST 成功返回且不含 Argon 组)统一精准清理。
120
+ - **功能(quota):多账号顺序轮询增加有机随机抖动。**
121
+ - 账号间的固定 2000ms 错峰间隔升级为 2000ms~3500ms 随机抖动(staggerJitterMs),避免向 Google Cloud Code 发送过于机械刻板的定频网络请求。
122
+ - **修复:号池每一行都显示了同一个账号的额度(2026-10-26 反馈)。**
123
+ - 根因:agy 解析凭据时**优先读系统钥匙串**(Windows 凭据管理器 / macOS Keychain / Linux Secret Service),且该存储是「每个 OS 用户只有一条」。因此给 agy -p /usage 一个隔离 HOME 并不能隔离身份——CLI 永远回答存储主人的额度,CLI 优先轮询就把主人的数字写进了每个子账号行(四行全部 97% / 55% / 56%,重置时刻完全相同)。
124
+ - 现在 CLI 只用于「真正能拥有该存储」的那个账号,其余账号全部走 REST —— 用各自的 token 文件签名,全平台并发执行。
125
+ - 隔离账号在**读取钥匙串之前**就被快速拒绝,轮询不再为每个账号付一次 PowerShell 冷启动;持有者本身仍要过 sameCredentialGrant 校验,因为子账号运行期间会临时占用该槽,此时 CLI 会返回子账号的数字。
126
+
127
+ ## 0.4.70 (2026-10-25)
128
+
129
+ ### English
130
+
131
+ - **Fix: the 5-hour quota row showed the weekly numbers (2026-10-22 report).**
132
+ - `fetchAvailableModels` returns a per-model `quotaInfo` with NO window label. For some accounts it carries the 5-hour bucket, for others the WEEKLY bucket: live `daoxzeex@gmail.com` reported `0.1736 / reset 2026-10-24T18:19:18Z` on every Gemini model, which is exactly the weekly bucket of `retrieveUserQuotaSummary`.
133
+ - The merge took "the lowest remaining across all sources" and filed it into the family 5-hour slot, so the GUI painted `5-hour quota 19% / Weekly 19%` with the SAME weekly reset on both rows (the filed screenshot) while the account actually had 82% of its 5-hour quota left.
134
+ - The reset instant now decides the window (`sameQuotaWindow`, 5-minute tolerance): a reading that matches the summary weekly reset tightens ONLY the weekly row; one that matches the 5-hour reset tightens the 5-hour row; an unmatchable reading keeps the old conservative behaviour. Eligibility is unchanged — the weekly row still drives the 5% drain check.
135
+ - **Fix: a queued turn no longer looks frozen for 15 minutes.**
136
+ - Windows has ONE machine-global agy credential slot, so sessions serialize: a healthy long run in one workspace made two other sessions sit with a completely empty transcript for `dispatchTimeoutMinutes` (15m) and then silently re-queue for another cap — indistinguishable from a hang (2026-10-22: the promo-tcn sessions, while a 15-minute run in another project held the slot).
137
+ - The wait now streams a session-visible `[agy] waiting for the agy credential slot …` line into the transcript, with an escalating cadence (20s → 60s → 3m → 9m) so even a 15-minute queue leaves a handful of lines. Re-queues after a cap are announced too.
138
+ - A generator abandoned while waiting (Stop / stream closed) now hands a late grant back instead of leaking a concurrency slot.
139
+ - **Fix: the machine-wide credential slot is held only until agy has READ it — different accounts now run concurrently.**
140
+ - Measured on agy 1.4.3 with a live 14-minute run: print mode resolves its credential ONCE at startup (`printmode.go:403 "not authenticated, trying silent auth"` → `:405 "silent auth succeeded"` at +3.2s) and performs zero further token/auth work for the rest of the run. Holding the Windows slot for the whole run was pure serialization, and it is what parked the 2026-10-22 promo-tcn sessions for 15 minutes behind another project's run.
141
+ - `armSlotHandoff` releases the lease on the child's FIRST output line (45s ceiling for a slow startup, run-end fallback, idempotent), while the identity is still installed under the mutex immediately before each spawn — so a later switcher cannot change what an already-started run read.
142
+ - `JETSKI_OAUTH_TOKEN` (the only env-based credential route in the binary) was probed with a bogus token: the run still authenticated from the keyring, so true per-process injection remains unavailable upstream. Recorded in KNOWN-GAPS with the measurement.
143
+ - **Fix: a rejected credential-slot write now refuses the run instead of billing the account that happens to be installed.**
144
+ - The Windows write is a PowerShell cold start that compiles a C# P/Invoke shim; its old 8-second ceiling made it time out and return `false` **without touching the slot** on a loaded machine (reproduced 2026-10-23: delete then write reported false and left the slot empty). All four install sites ignored that boolean, so the next spawn authenticated as the PREVIOUS account — silent cross-account quota spend, the exact corruption class the module exists to prevent.
145
+ - Install sites now verify the write (one retry, then `SlotUnavailableError` → the turn fails visibly); teardown/restore paths log loudly instead of failing a finished turn. Writes get a 30s ceiling; reads stay at 8s because they sit on the poll path.
146
+ - New test: a write that always reports false refuses the run, leaves the slot untouched and does not leak the mutex.
147
+
148
+ ### 中文
149
+
150
+ - **修复:5 小时额度行显示成了周额度(2026-10-22 反馈)。**
151
+ - `fetchAvailableModels` 的 per-model `quotaInfo` 不带窗口标签:有的账号返回 5 小时桶,有的账号返回**周**桶(实测 `daoxzeex@gmail.com` 每个 Gemini 模型都返回 `0.1736 / reset 2026-10-24T18:19:18Z`,正是 `retrieveUserQuotaSummary` 里的周桶)。
152
+ - 旧合并逻辑「取所有来源里最低的余量」把它塞进了 5 小时行,界面于是显示 `5 小时额度 19% · 周额度 19%`、两行 reset 完全相同(反馈截图),而该账号 5 小时其实还剩 82%。
153
+ - 现在由 reset 时刻判定窗口归属(`sameQuotaWindow`,5 分钟容差):命中周 reset 的读数只收紧周行;命中 5 小时 reset 的收紧 5 小时行;无法判定的读数保持原有保守行为。可用性判定不变——周行依旧参与 5% 耗尽检查。
154
+ - **修复:排队中的回合不再看起来卡死 15 分钟。**
155
+ - Windows 只有一个全局 agy 凭据槽,会话之间必须串行:另一个工作区一次正常的长任务就能让其它会话的转录区**整整 15 分钟一片空白**(`dispatchTimeoutMinutes`),随后再静默重排一轮——和卡死无法区分(2026-10-22 的 promo-tcn 两个会话就是被另一个项目 15 分钟的运行挡住)。
156
+ - 等待期间现在会向会话流里写入可见的 `[agy] 正在等待 agy 凭据槽 …` 提示,节奏递增(20s → 60s → 3m → 9m),15 分钟排队也只留几行;超时重排同样会写明。
157
+ - 等待中被 Stop / 关流放弃的请求,现在会把迟到的许可归还,而不是漏掉一个并发槽。
158
+ - **修复:全局凭据槽只占用到 agy 读完凭据为止——不同账号现在可以真正并发。**
159
+ - 实测 agy 1.4.3 一次 14 分钟的运行:print 模式只在启动瞬间解析一次凭据(`printmode.go:403` 未认证 → `:405` silent auth 成功,+3.2 秒),之后 14 分钟**再无任何 token/auth 操作**。把 Windows 全局槽占满整个 run 纯属多余串行——2026-10-22 的 promo-tcn 两个会话正是被另一个项目那 14 分钟的运行堵了 15 分钟。
160
+ - 新增 `armSlotHandoff`:在子进程输出**第一行**时立刻交还租约(启动慢则 45 秒上限、run 结束兜底、幂等),而身份安装依旧在每次 spawn 前、在互斥锁内完成,因此后来的切换不可能改变已启动 run 读到的凭据。
161
+ - 二进制里唯一的进程级凭据入口 `JETSKI_OAUTH_TOKEN` 已用假 token 实测:仍然从系统 keyring 认证成功,说明上游还不支持真正的进程级注入;该结论连同测法已写入 KNOWN-GAPS。
162
+ - 补充实测:**把全局凭据条目删掉后,agy 确实会改读家目录凭据文件**(假 token → `UNAUTHENTICATED 401`)。但 keyring 在写入侧同样优先(`Failed to save token to keyring, falling back to file`),任何一次成功运行都会把全局条目写回来,所以"永久空槽 + 纯文件隔离"仍不成立;凭据隔离目前只是读取侧能力。
163
+ - **修复:凭据槽写入被拒绝时,拒绝启动,而不是用"当前碰巧装着"的账号去计费。**
164
+ - Windows 侧写入是一次 PowerShell 冷启动(Add-Type 编译 C# P/Invoke),旧实现的 8 秒上限在机器繁忙时会**超时并返回 false,而槽位根本没被改动**(2026-10-23 实测:删除后再写入返回 false,槽位保持空)。四个安装点都没检查这个布尔值,于是紧接着的 spawn 会以**上一个账号**的身份认证 —— 静默跨账号扣额度,正是本模块要防的那类污染。
165
+ - 现在安装点会校验写入(失败重试一次,仍失败则抛 `SlotUnavailableError`,让这一轮可见地失败);收尾/恢复路径改为大声告警而不毁掉已完成的轮次。写入超时提到 30 秒,读取保持 8 秒(在轮询路径上)。
166
+ - 新增测试:写入恒返回 false 时必须拒绝运行、槽位保持原状、互斥锁不泄漏。
167
+
168
+ ## 0.4.69 (2026-10-23)
169
+
170
+ ### English
171
+
172
+ - **Feat: Google risk-control hardening and CLI-first quota architecture.**
173
+ - Implemented CLI-first quota detection via official `agy -p /usage`: 100% genuine Go TLS fingerprint and client handshake, eliminating Node.js-level direct REST calls (`v1internal:retrieveUserQuotaSummary`) that trigger JA3/JA4 fingerprint mismatch warnings.
174
+ - Purged internal test endpoints (`daily-cloudcode-pa.sandbox.googleapis.com` and `autopush-cloudcode-pa.sandbox.googleapis.com`) from `AGY_ENDPOINTS` to prevent sandbox probe alarms on consumer accounts.
175
+ - Token refresh is fully delegated to the native `agy` CLI Go runtime; Node.js host process no longer issues direct refresh requests to `oauth2.googleapis.com/token`.
176
+ - Background quota polling interval widened from 15m to 60m (`quotaPollIntervalMs: 60m`), and account polling incorporates a 2-second stagger sleep to avoid simultaneous process bursts.
177
+ - **Feat: Full concurrency on Windows credential slot via global primary account alignment (Scheme A).**
178
+ - Resolved Windows single-slot mutex serialization: concurrent sessions align to the current primary healthy candidate, hitting the `parallelSameAccount: true` fast path for 100% lock-free concurrency across sessions.
179
+ - Single-direction migration to the next candidate only occurs when the active account encounters a 429 quota exhaustion.
180
+ - **Feat: Two-tier dual-loop test framework aligned with academic-agent & test suite speedup.**
181
+ - Introduced `pnpm run test:fast` (powered by `scripts/fast-test.mjs`) for inner TDD vibe loops (0.5s~5s), running only Git-affected test files or specified module keywords (`pnpm run test:fast <keyword>`).
182
+ - Tuned simulated asynchronous delay timers in `test/adapter.test.ts` and `fake-agy.mjs` from 5000ms/8000ms down to 300ms/400ms, speeding up adapter unit test execution by ~4x.
183
+ - **Feat: Scroll guard & tool view expansion state persistence.**
184
+ - Added `scroll-guard.ts` to suspend DOM mutations and heavy scans (decisions, images, reasoning, mermaid) during active scrolling and smooth momentum settling.
185
+ - Preserved user expanded/collapsed tool card states across TanStack Virtualizer dynamic remount cycles.
186
+
187
+ ### 中文
188
+
189
+ - **新功能:针对 Google 敏感风控强化防护与 CLI 优先配额体系。**
190
+ - 配额查询全面转向官方 `agy -p /usage` 解析:100% 走官方原生 Go 二进制握手与正规 TLS 指纹,消除 Node.js 层使用 `undici` 直接裸调 Google 内部 `v1internal` 接口带来的 JA3/JA4 客户端指纹断层风险。
191
+ - 从 `AGY_ENDPOINTS` 中彻底移除 `daily-cloudcode-pa.sandbox.googleapis.com` 与 `autopush-cloudcode-pa.sandbox.googleapis.com` 内部测试端点,杜绝异常沙箱端点探测对正式 Google 账号的风控隐患。
192
+ - OAuth 令牌刷新完全交由 `agy` 官方 Go 运行时在日常使用中原生完成,Node.js 宿主停止主动向 `oauth2.googleapis.com/token` 发送换票请求。
193
+ - 默认后台巡检周期放宽至 60 分钟(`quotaPollIntervalMs: 60m`),多账号后台轮询增加 2 秒错峰休眠,避免瞬时并发拉起 CLI 进程造成的系统负载颠簸。
194
+ - **新功能:Windows 凭证槽位并发性能全面解锁(方案 A:全局主导账号收敛)。**
195
+ - 解决 Windows 系统下因全局唯一定制凭证槽位 `gemini:antigravity` 导致的跨 Session 串行卡顿问题:所有会话默认对齐到号池主导活跃健康账号,命中 `parallelSameAccount: true` 快速通道,实现跨会话 100% 无锁全并发执行。仅当主导账号 429 配额耗尽时,全局指针单向整体迁移至下一账号。
196
+ - **新功能:对齐 academic-agent 的两级双环极速测试架构与用例调优。**
197
+ - 新增 `pnpm run test:fast` 极速内环测试工具(`scripts/fast-test.mjs`),实现基于 Git 变动的秒级精准回归(0.5s~5s)以及按模块关键字直跑(`pnpm run test:fast <keyword>`)。
198
+ - 优化 `adapter.test.ts` 与 `fake-agy.mjs` 中的异步等待延迟(由 5000ms/8000ms 降至 300ms/400ms),单测执行速度提升近 4 倍,保障日常开发极速反馈心流。
199
+ - **新功能:页面平滑滚动守卫与工具卡片展开状态持久化。**
200
+ - 增加 `scroll-guard.ts` 滚动守卫机制,在用户主动滚屏及平滑滑动期间挂起 DOM 探测(decisions/images/reasoning/mermaid),彻底杜绝滚屏时内容突变抖动与闪烁。
201
+ - 工具卡片折叠/展开状态跨 TanStack-Virtual 虚拟列表重新挂载自动持久化保留(`expandedToolBlockKeys`)。
202
+
203
+ ## 0.4.68 (2026-10-23)
204
+
205
+ ### English
206
+
207
+ - **Feat: Dynamic model picker reflects active account eligibility (Gemini 4 Argon isolation).**
208
+ - Accounts in the pool now discover and cache models independently (`ModelCatalog` keyed by `accountId`).
209
+ - When switching the active account in the UI or pool, the DSH composer model picker automatically refreshes (`llm/adapters-updated`).
210
+ - If the active account lacks Argon access (free/standard tier), `gemini-4-argon` is dynamically excluded from the picker to prevent generation errors. When an account with Pro/Ultra subscription is active, Argon models are immediately restored.
211
+ - **Fix: Eliminate page scrolling and violent screen flickering in Antigravity client.**
212
+ - Root cause: In `src/client/decisions.ts`, decision chip toolbar was repeatedly injected into TanStack-Virtual dynamic measurement containers because container-level marker `data-agy-decisions-scanned` was checked but never set, and re-rendering reconstructed heading nodes. The item height expansion triggered scroll position adjustments, which caused a re-render and re-injection flywheel.
213
+ - In addition, settings nav row `MutationObserver` watched the entire subtree including `characterData` and re-triggered on its own marker writes without an attribute filter, and account button busy states repainted the entire account list instead of scoping to the target account.
214
+ - Resolved with container-level idempotence guards (`data-agy-decisions-scanned` written before mutation), pre-checking existing toolbar containers, filtering nav mutation observer strictly to marker attribute changes, and scoping button busy states and shimmer animations to target accounts.
215
+ - **Fix: Send agy CLI User-Agent in quota requests so Argon quota group is returned (PR #54, Fixes #53).**
216
+ - Google's Cloud Code backend requires a genuine `agy` CLI User-Agent header (format `Antigravity/<version> (darwin; arm64)`) to return the `Gemini 4 Argon` quota bucket in `retrieveUserQuotaSummary`. Without this header, only generic Gemini and 3P groups were returned.
217
+ - Quota service now detects the installed `agy` CLI version and passes the proper User-Agent header in HTTP requests. When an account transitions or drops dedicated groups, obsolete cooldowns and stale last-known-good entries are cleanly evicted.
218
+ - **Fix: DSH Host 100% CPU busy loop on macOS caused by copying active SQLite WAL files (Fixes #52).**
219
+ - Root cause: `loadAgyDbData()` previously copied `conversation.db`, `-wal`, and `-shm` to a temporary directory before querying tool step parameters and model thoughts. On macOS (and other BSD-derived libuv platforms), when an active SQLite WAL file is checkpointed or truncated to 0 during `copyFile`, libuv's `uv_fs_copyfile` enters an infinite busy loop in a worker thread, causing the Host process to permanently consume 100% of a CPU core.
220
+ - Solution: Replaced file copying with direct SQLite reads (`node:sqlite` DatabaseSync with `readOnly: true`, falling back to `sqlite3` CLI with `-cmd ".timeout 2000" -cmd "PRAGMA query_only = ON;"`). SQLite's native WAL architecture provides atomic, consistent MVCC read snapshots concurrently with active writers without blocking or file-copying races.
221
+ - Added regression test covering active WAL querying, WAL truncation/checkpointing during active use, sqlite3 CLI fallback, and zero-byte database handling.
222
+ - **Fix: Transport failures outlast a real egress outage, and no longer rotate a shared-egress pool.**
223
+ - Live case (2026-10-21 16:34–16:48): the machine's proxy answered `CONNECT 200` and then killed TLS for ~14 minutes. Every `agy` run on every account died in its startup probe with `Post "https://daily-cloudcode-pa.googleapis.com/v1internal:loadCodeAssist": EOF`, which agy wraps as `Eligibility check failed: …`. DSH retried twice inside ~4 seconds and gave up — while the Antigravity desktop's own language server simply kept retrying and recovered.
224
+ - Transport (`NETWORK`) failures now state their own backoff via `providerRetryAfterMs`: **5s → 15s → 30s** for consecutive failures in one session, reset on success or any non-transport failure, and treated as fresh after 10 minutes of quiet. `providerRetryPolicy` grew to 3 retries with `maxDelayMs: 45s` (the retry plugin *drops* a retry whose requested delay exceeds that cap, so it must sit above the longest transport delay).
225
+ - A transport failure is only account-scoped when accounts leave through **different** proxies. With a single shared egress (every account on the config/system proxy, including pool accounts seeded with a copy of the system proxy) the retry stays on the current account: the sticky pointer is not advanced, the conversation binding is kept, and the account-rotation budget stays available for real quota/eligibility hops. The in-step failover of a settled recording also skips the budget without changing accounts.
226
+ - Non-transport retries keep their 2s/4s starting schedule; nothing else about classification (AUTH vs NETWORK vs ELIGIBILITY) changed.
227
+
228
+ ### 中文
229
+
230
+ - **新功能:模型选择框按当前活动账号资格动态隔离(Gemini 4 Argon 自动感知)。**
231
+ - 号池内账号支持按账号独立发现并缓存模型目录(`ModelCatalog` 按 `accountId` 隔离)。
232
+ - 在前端或号池切换当前活动账号后,DSH 输入框模型选择列表即时联动刷新(`llm/adapters-updated`)。
233
+ - 若当前活动账号无 Argon 权限(标准/免费层),输入框模型列表中自动剔除 `gemini-4-argon`,防止误选调用报错;当切回 Pro/Ultra 订阅账号时,Argon 模型自动呈现。
234
+ - **修复:解决 Antigravity 插件设置及历史页面来回滚动疯狂闪屏问题。**
235
+ - 根因定位:在 `src/client/decisions.ts` 中,决策按钮增强器容器级标记 `data-agy-decisions-scanned` 此前只读未写,且在 TanStack-Virtual 动态虚拟列表中,重新挂载渲染会重建 heading 节点。工具条反复注入导致项高度变化,进而触发虚拟列表重算 scrollTop 并再次触发重渲染注入飞轮。
236
+ - 此外,设置导航图标的 `MutationObserver` 监听范围过广且在修改属性时自身被反复触发,且操作账号按钮的全局 busy 状态导致 40+ 个元素属性全量刷新并引发重排重绘。
237
+ - 修复方案:在变更 DOM 前对容器写入 `data-agy-decisions-scanned` 并严格预检容器已有工具条;导航 Observer 限制仅监听对应属性变更(`attributeFilter`)并剥离 `characterData`;按钮 loading 状态与额度进度条 shimmer 动画改为账号级别作用域隔离。
238
+ - **修复:配额请求中上报 agy CLI 原生 User-Agent 以获取 Argon 额度分组(合并 PR #54,修复 #53)。**
239
+ - Google Cloud Code 后端要求在 `retrieveUserQuotaSummary` 请求中携带真实的 `agy` CLI User-Agent(格式如 `Antigravity/<version> (darwin; arm64)`)才会返回 `Gemini 4 Argon` 独立额度桶。缺少该头时后端仅下发普通 Gemini 与 3P 组。
240
+ - 配额探测服务现在自动探测本地 `agy` 版本并携带该头发送请求;当账号升级或移出专用桶时,旧版冷却记录与失效缓存自动淘汰。
241
+ - **修复:macOS 下复制活跃 SQLite WAL 时触发 libuv `copyFile` 忙循环导致 DSH Host 持续 100% CPU(修复 #52)。**
242
+ - 原因分析:`loadAgyDbData()` 此前在读取工具参数和思考内容时,顺序将 `conversation.db`、`-wal` 和 `-shm` 复制到临时目录。在 macOS(以及其他基于 BSD 的 libuv 平台)上,当活跃 WAL 文件在复制过程中被 SQLite checkpoint 截短为 0 字节时,libuv 的 `uv_fs_copyfile` 工作线程会陷入无限忙循环,导致 DSH Host 进程持续耗尽单核 100% CPU。
243
+ - 修复方案:完全移除临时目录与文件复制逻辑,改为直接通过 SQLite 引擎一致性读取(优先使用 `node:sqlite` 的 `DatabaseSync` 只读模式并开启 `PRAGMA query_only = ON;` 与 `busy_timeout`,后备使用 `sqlite3` CLI)。SQLite WAL 模式天然支持多读一写并发 MVCC 快照,既消除了文件截短忙循环与临时文件 I/O 争用,又保证了读取一致性。
244
+ - 补充回归测试:覆盖活跃 WAL 读取、WAL 截短/检查点处理、sqlite3 CLI 后备路径及 0 字节文件防御。
245
+ - **修复:网络抖动时重试不再 4 秒内放弃,共用出口的号池不再被无意义轮换。**
246
+ - 实测案例(2026-10-21 16:34–16:48):本机代理对 `CONNECT` 返回 200 后直接掐断 TLS 长达约 14 分钟。每个账号上每次 `agy` 运行都在启动探测阶段死于 `Post "https://daily-cloudcode-pa.googleapis.com/v1internal:loadCodeAssist": EOF`,被 agy 包成 `Eligibility check failed: …`。DSH 约 4 秒内重试两次即宣告失败,而 Antigravity 桌面端自己的 language server 只是一直重试,网络恢复后照常工作。
247
+ - 传输类(`NETWORK`)失败现在通过 `providerRetryAfterMs` 自带退避:同一会话连续失败按 **5s → 15s → 30s** 递增;成功或出现非传输类失败即归零,静默超过 10 分钟后重新从 5s 起算。`providerRetryPolicy` 提升为 3 次重试、`maxDelayMs: 45s`(重试插件对**超过上限**的请求延迟会直接放弃该次重试,因此上限必须高于最长退避)。
248
+ - 只有当各账号走**不同代理**时,传输失败才算账号级问题。共用同一出口时(所有账号都走配置/系统代理,含新建号池账号被写入系统代理副本的情况)重试留在当前账号:不推进粘性指针、保留会话绑定、也不消耗本该留给配额/资格跳转的轮换预算;已结算记录的步内失败切换同样不再计数、不再换号。
249
+ - 非传输类重试仍从 2s/4s 起步;AUTH / NETWORK / ELIGIBILITY 的判定逻辑未做任何改动。
250
+
3
251
  ## 0.4.67 (2026-10-20)
4
252
 
5
253
  ### English
package/README.md CHANGED
@@ -152,6 +152,8 @@ dsh plugin --profile web add dsh-agy-link
152
152
  | `longToolIdleMinutes` | `DSH_AGY_LONG_TOOL_IDLE_MINUTES` | `240` | 存在未完成工具步骤(ACTIVE)时的空闲预算(分钟);`0` = 不抬高预算,卡死的工具按 `timeoutMs` 快速失败 |
153
153
  | `workspaceRoot` | `DSH_AGY_WORKSPACE_ROOT` | 会话 cwd | agy 工作区根目录(默认跟随当前会话工作区) |
154
154
  | `suppressAgyAutoUpdate` | `DSH_AGY_SUPPRESS_AUTOUPDATE` | Windows `true`,其他平台 `false` | 每次 spawn 前刷新 agy 的更新检查时间戳,阻断 `--bg-updater` 子进程链(Windows 闪窗根因)。关闭后 agy 恢复自动更新检查 |
155
+ | `autoWarmup` | `DSH_AGY_AUTO_WARMUP` | `false` | 7 天周配额自动预热(opt-in):周额度到达重置时间或冷启动时发送极小探活请求,提前激活 7 天计时器。开启后每 5 分钟巡检一次;该路径直连 Google 内部端点,请自行评估风控风险 |
156
+ | `warmupModels` | `DSH_AGY_WARMUP_MODELS` | `gemini-3.8-flash,claude-sonnet-4-6` | 自动预热使用的模型列表(逗号分隔);空数组表示不自动预热 |
155
157
 
156
158
  ---
157
159
 
@@ -330,6 +332,8 @@ dsh plugin --profile web add dsh-agy-link
330
332
  | `longToolIdleMinutes` | `DSH_AGY_LONG_TOOL_IDLE_MINUTES` | `240` | Idle budget while a tool step is ACTIVE (minutes); `0` keeps the normal `timeoutMs` budget so wedged tools fail fast |
331
333
  | `workspaceRoot` | `DSH_AGY_WORKSPACE_ROOT` | session cwd | Working directory root |
332
334
  | `suppressAgyAutoUpdate` | `DSH_AGY_SUPPRESS_AUTOUPDATE` | `true` on Windows, `false` elsewhere | Refreshes agy's update-check timestamp before every spawn so its `--bg-updater` child chain (the Windows console-flash root cause) never starts. `false` restores agy's own auto-update checks |
335
+ | `autoWarmup` | `DSH_AGY_AUTO_WARMUP` | `false` | Opt-in 7-day weekly quota warmup: sends a minimal probe when the weekly window has reset or is cold, activating the 7-day timer. Scans every 5 minutes; this path talks directly to Google's internal endpoints, so assess the risk-control tradeoff yourself |
336
+ | `warmupModels` | `DSH_AGY_WARMUP_MODELS` | `gemini-3.8-flash,claude-sonnet-4-6` | Model list used for warmup probes (comma-separated); an empty list disables automatic warmup |
333
337
 
334
338
  ---
335
339
 
package/dist/bridge.mjs CHANGED
@@ -1,14 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  // dsh-agy-link MCP bridge script: a minimal stdio MCP server with zero
3
- // dependencies. agy launches it per the workspace .mcp.json; it forwards
3
+ // dependencies. agy launches it from its MCP registry; it forwards
4
4
  // tools/list and tools/call to the plugin loopback endpoint whose URL and
5
- // token arrive via env. Protocol: newline-delimited JSON-RPC 2.0.
5
+ // token arrive via env. DSH_MCP_SESSION carries the spawning DSH session so
6
+ // the host can resolve the calling agent (issue #55).
7
+ // Protocol: newline-delimited JSON-RPC 2.0.
6
8
  import { request as httpRequest } from 'node:http'
7
9
  import process from 'node:process'
8
10
  import { createInterface } from 'node:readline'
9
11
 
10
12
  const URL_BASE = process.env.DSH_MCP_URL || ''
11
13
  const TOKEN = process.env.DSH_MCP_TOKEN || ''
14
+ const SESSION = process.env.DSH_MCP_SESSION || ''
12
15
  const PROTOCOL = '2024-11-05'
13
16
 
14
17
  function post(path, body) {
@@ -28,6 +31,7 @@ function post(path, body) {
28
31
  'Content-Type': 'application/json',
29
32
  'Content-Length': Buffer.byteLength(data),
30
33
  Authorization: 'Bearer ' + TOKEN,
34
+ ...(SESSION === '' ? {} : { 'X-DSH-Session': SESSION }),
31
35
  },
32
36
  timeout: 600000,
33
37
  });