@eddyskywalker/dsh-chatgpt-subscription 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/lib/client.js +121 -1
- package/lib/client.js.map +1 -1
- package/lib/index.js +465 -19
- package/lib/types/client/antigravity/AntigravitySection.d.ts.map +1 -1
- package/lib/types/client/antigravity/locales.d.ts +47 -2
- package/lib/types/client/antigravity/locales.d.ts.map +1 -1
- package/lib/types/client/kimi-code/locales.d.ts +2 -2
- package/lib/types/host/antigravity/account-pool.d.ts +24 -0
- package/lib/types/host/antigravity/account-pool.d.ts.map +1 -1
- package/lib/types/host/antigravity/adapter.d.ts.map +1 -1
- package/lib/types/host/antigravity/cache-stats.d.ts +166 -0
- package/lib/types/host/antigravity/cache-stats.d.ts.map +1 -0
- package/lib/types/host/antigravity/mapper.d.ts +75 -3
- package/lib/types/host/antigravity/mapper.d.ts.map +1 -1
- package/lib/types/host/antigravity/routes.d.ts +1 -1
- package/lib/types/host/antigravity/routes.d.ts.map +1 -1
- package/lib/types/shared/antigravity-contracts.d.ts +76 -0
- package/lib/types/shared/antigravity-contracts.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.17.0 - 2026-10-09
|
|
6
|
+
|
|
7
|
+
- **[Antigravity] 缓存卡片改为只报「整段未命中」并给出它之前的空闲时长**
|
|
8
|
+
- **原来的口径把最有用的信息盖掉了。** 卡片报的是「相比上一次请求,三段前缀里哪一段变了」,而每次发新消息都必然改变 `contents`,于是它**每一次都显示「对话内容变化」**——既报了唯一注定要变的那一项,又把真正花钱的那件事藏了起来。这个口径是我上一版写的,属于设计失误。
|
|
9
|
+
- **现在只在整段未命中时发布归因**(`cachedContentTokenCount === 0`,即整个前缀被重算)。部分命中是对话增长的正常状态,每次新回合都必然带入服务端没见过的 token,`freshTokens > 0` 本身不代表任何东西,因此不再上报。
|
|
10
|
+
- **归因改为「先暂存、后发布」**:请求发出前算出的变化只暂存(`pendingBySession`),等响应报回用量后才决定要不要发布。改动的代价是否为零,只有响应的用量能回答。
|
|
11
|
+
- **新增空闲时长**:记录每个会话上一次**发出请求**的时刻,未命中时一并报出间隔。服务端的缓存按空闲时间过期(实测在 76 秒仍命中、183 秒整段失效),而客户端无法读取那个阈值——这是唯一能在本地观测到它的读数。首次请求没有可比较的前值,因此不填该字段(填 0 会被读成「立刻返回」)。
|
|
12
|
+
- **契约与 UI 同步**:`AntigravityCacheStatsDto.lastDriftCause` → `lastMiss: { cause, idleMs?, at }`;卡片显示「最近一次整段未命中」+「未命中前空闲」,时长按 3m 3s / 45s 这样的人类单位渲染;中英文案与段落说明一并重写。
|
|
13
|
+
- 测试:缓存统计 19 → 22 例(新增「命中时不报」「未命中时发布归因」「记录空闲时长」「首次请求不填空闲」),卡片渲染新增 1 例(全部命中时**不出现**任何未命中行,防止退回「每次都有话说」)。既有断言中 3 处依赖旧口径的已按新契约更新。
|
|
14
|
+
- 验证:`npm run typecheck` 0 错误;全量 `npm test` **2956 passed / 7 skipped, 0 failed**(较基线增 4 例)。
|
|
15
|
+
|
|
16
|
+
- **[Antigravity] 让 Gemini 路线的请求前缀保持逐字节稳定,并给这条线路补上缓存观测**
|
|
17
|
+
- **Antigravity 用的是 Google 服务端隐式缓存,客户端没有任何开关可拨。** 二进制里 `cachedContents` / `ContextCacheConfig` / `ttl` 这些字段确实存在,但都属于它链进来的官方 Go SDK,不是它的调用面:它实际只 POST `/v1internal:streamGenerateContent`,那层消息里不含任何缓存控制字段。所以唯一能提高命中率的手段,就是**不修改**请求前缀——前缀一旦在某个位置被改写,从该位置往后全部失效。
|
|
18
|
+
- **Gemini 路线一直在把每个 SSE text delta 回放成独立 part。** 这条修复此前只做给了 Claude(见 `docs/antigravity-claude-cache-audit.md`:385 个 delta 回放成 385 个 part,命中率因此掉到 68.84%),Gemini 走的是同一份 delta 却漏了。现在抽出纯函数 `mergeAdjacentTextParts`,两条路线各自调用;Claude 专属的 thinking/signature 配对逻辑原地保留,只对 Claude 生效。合并只针对「仅含 `text` 一个键」的相邻 part,带 signature、`thought: true`、`functionCall` 的边界一律不跨越。
|
|
19
|
+
- **图片预算原本按存储字节度量,而缩放发生在预算之后**,导致一组图在缩放有机会生效之前就已经超限、最旧的图照样被替换成占位文本——图片数量仍然变化,前缀照旧被改写,缩放等于没接上。现在 `requestImageBytes` 度量的是**实际要发送的尺寸**(有缩放目标时取 `min(存储字节, 目标上限)`),这是 `host/common/request-images.ts` 已记录过的同一个陷阱。预算仍保持 Antigravity 自己的 12MB,**没有**借用 Kimi 的 1.5MB。
|
|
20
|
+
- **这条线路此前完全不可观测**:`request-diagnostics` 的指纹是进程内临时 HMAC、不落盘,而且只看顶层 `tools/system/messages`,看不到 Antigravity 嵌套的 `request.contents/systemInstruction/tools`。新增的 `cache-stats.ts` 按会话记录命中率,并对三段前缀各取一个稳定指纹,把冷却归因到 `contents` / `systemInstruction` / `tools` / `session-id` / `none`。哈希走键排序后的稳定序列化,否则两个结构相同、键序不同的请求会被报成假漂移。统计纯内存、按 `sessionId + accountId` 分作用域、256 作用域 LRU。
|
|
21
|
+
- **已知局限写进了文件头,而不是留在报告里**:前缀快照按 session 分区、token 总计按 session+account 分区,两者作用域不同,所以**会话中途 429 换号会被报成 `'none'`**——读者不能据此读成「客户端什么都没变」。账号是否真的分区服务端缓存始终是 **INFERRED, not measured**,也正因如此没有把 `account` 提升成漂移原因:在假设未验证前把它写进契约,等于把假设当事实陈述。`'session-id'` 那个枚举同理,文案用「会话标识变化」而不承诺缓存语义。
|
|
22
|
+
- **账号调度缺省改为 sticky**,且只改缺省:`parseAntigravityPoolData` 会把缺失值补成 `'sequential'` 并写回池文件,所以那里的解析器也必须一起改,否则 `routes.ts` 的回退分支是死代码。**已存盘的显式选择继续生效**,不为存量用户做迁移——这条改动的前提(账号=缓存命名空间)尚未验证,不适合在未证实收益的情况下改变既有行为。共享 core `host/common/account-pool.ts` 的策略语义未动,其他线路不受影响。
|
|
23
|
+
- 设置页新增缓存卡片(命中率、累计 cached/fresh、最近一次漂移原因),中英文案齐备;未测得用量时显示为空而不是画一个 0% 的假仪表。
|
|
24
|
+
- 验证:`npm run typecheck` 0 错误;全量 `npm test` **2952 passed / 7 skipped, 0 failed**。新用例在改动前的实现上实测 **12 failed**(用 `git stash` 单独暂存 `mapper.ts` 复现),改后全过。
|
|
25
|
+
- **本次没有证明命中率提升**:Google 的隐式缓存是服务端的,本机无法做 A/B,`readImageRequest` 的真实缩放产物在测试里全是 mock。改动依据是「前缀逐字节稳定」这一机制,与 68.84% 那次的成因同构,但没有可报的线上数字。
|
|
26
|
+
|
|
5
27
|
## 0.16.0 - 2026-10-09
|
|
6
28
|
|
|
7
29
|
- **[WorkBuddy] 模型行加上消耗倍率提示**
|
package/lib/client.js
CHANGED
|
@@ -2510,6 +2510,21 @@ window.__ModuleLoader__.load({
|
|
|
2510
2510
|
save: "保存",
|
|
2511
2511
|
saved: "已保存",
|
|
2512
2512
|
saving: "保存中...",
|
|
2513
|
+
cacheSection: "缓存",
|
|
2514
|
+
cacheDesc: "Antigravity 的缓存由服务端隐式命中,客户端不发送任何缓存控制字段,因此这里用实际读缓存的比例反映效果。下面只报「整段未命中」的那一次,并给出它之前的空闲时长——缓存是按空闲时间过期的,而每次新对话都必然改变内容,把每次变化都报出来等于什么都没说。统计只保存在内存中,并按会话隔离。",
|
|
2515
|
+
cacheEmpty: "本进程还没有带用量回报的请求。",
|
|
2516
|
+
cacheHitRatio: "缓存命中率",
|
|
2517
|
+
cacheCached: "命中 tokens",
|
|
2518
|
+
cacheFresh: "新增 tokens",
|
|
2519
|
+
cacheRequests: "请求数",
|
|
2520
|
+
cacheMiss: "最近一次整段未命中",
|
|
2521
|
+
cacheMissIdle: "未命中前空闲",
|
|
2522
|
+
cacheDriftNone: "前缀未变化",
|
|
2523
|
+
cacheDriftContents: "对话内容变化",
|
|
2524
|
+
cacheDriftSystemInstruction: "系统指令变化",
|
|
2525
|
+
cacheDriftTools: "工具列表变化",
|
|
2526
|
+
cacheDriftSessionId: "会话标识变化",
|
|
2527
|
+
cacheDriftNewSession: "本次会话的首次请求",
|
|
2513
2528
|
quotaSection: "用量与配额",
|
|
2514
2529
|
quotaDesc: "数据来自 Google Cloud Code Assist 配额服务,页面可见时最多每 60 秒刷新一次。",
|
|
2515
2530
|
refreshQuota: "刷新用量",
|
|
@@ -2573,6 +2588,21 @@ window.__ModuleLoader__.load({
|
|
|
2573
2588
|
save: "Save",
|
|
2574
2589
|
saved: "Saved",
|
|
2575
2590
|
saving: "Saving...",
|
|
2591
|
+
cacheSection: "Cache",
|
|
2592
|
+
cacheDesc: "Antigravity's cache is matched implicitly by the service — the client sends no cache-control field — so the served-from-cache share is the only evidence it is working. Only a total miss is reported below, beside how long the client had been idle: the cache expires on idle time, and every new turn changes the conversation, so reporting each change would report the same thing every time. The totals are in-memory and scoped per session.",
|
|
2593
|
+
cacheEmpty: "No request in this process has reported usage yet.",
|
|
2594
|
+
cacheHitRatio: "Cache hit ratio",
|
|
2595
|
+
cacheCached: "Cached tokens",
|
|
2596
|
+
cacheFresh: "Fresh tokens",
|
|
2597
|
+
cacheRequests: "Requests",
|
|
2598
|
+
cacheMiss: "Last total miss",
|
|
2599
|
+
cacheMissIdle: "Idle before that miss",
|
|
2600
|
+
cacheDriftNone: "Prefix unchanged",
|
|
2601
|
+
cacheDriftContents: "Conversation contents changed",
|
|
2602
|
+
cacheDriftSystemInstruction: "System instruction changed",
|
|
2603
|
+
cacheDriftTools: "Tool list changed",
|
|
2604
|
+
cacheDriftSessionId: "Session identifier changed",
|
|
2605
|
+
cacheDriftNewSession: "This session's first request",
|
|
2576
2606
|
quotaSection: "Usage & Quota",
|
|
2577
2607
|
quotaDesc: "Data from Google Cloud Code Assist quota service; refreshes up to once every 60 seconds when visible.",
|
|
2578
2608
|
refreshQuota: "Refresh quota",
|
|
@@ -2599,6 +2629,26 @@ window.__ModuleLoader__.load({
|
|
|
2599
2629
|
//#endregion
|
|
2600
2630
|
//#region src/client/antigravity/AntigravitySection.tsx
|
|
2601
2631
|
const api$11 = createLineApi("/antigravity/api", "Antigravity");
|
|
2632
|
+
/** A gap in human units, so the idle reading is legible without arithmetic. */
|
|
2633
|
+
function idleLabel(ms) {
|
|
2634
|
+
const seconds = Math.round(ms / 1e3);
|
|
2635
|
+
if (seconds < 60) return `${seconds}s`;
|
|
2636
|
+
const minutes = Math.floor(seconds / 60);
|
|
2637
|
+
const rest = seconds % 60;
|
|
2638
|
+
return rest === 0 ? `${minutes}m` : `${minutes}m ${rest}s`;
|
|
2639
|
+
}
|
|
2640
|
+
/** Human label for the cause of the last request that lost the whole cache. */
|
|
2641
|
+
function missLabel(cause, t) {
|
|
2642
|
+
switch (cause) {
|
|
2643
|
+
case "none": return t.cacheDriftNone;
|
|
2644
|
+
case "contents": return t.cacheDriftContents;
|
|
2645
|
+
case "systemInstruction": return t.cacheDriftSystemInstruction;
|
|
2646
|
+
case "tools": return t.cacheDriftTools;
|
|
2647
|
+
case "session-id": return t.cacheDriftSessionId;
|
|
2648
|
+
case "new-session": return t.cacheDriftNewSession;
|
|
2649
|
+
default: return String(cause);
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2602
2652
|
function AntigravitySection({ onModelChange, loadModelDirectory }) {
|
|
2603
2653
|
const [status, setStatus] = (0, react.useState)(null);
|
|
2604
2654
|
const [loading, setLoading] = (0, react.useState)(true);
|
|
@@ -2903,7 +2953,7 @@ window.__ModuleLoader__.load({
|
|
|
2903
2953
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountPoolSection, {
|
|
2904
2954
|
accounts: status?.accounts ?? [],
|
|
2905
2955
|
activeAccountId: status?.activeAccountId,
|
|
2906
|
-
rotationStrategy: status?.rotationStrategy ?? "
|
|
2956
|
+
rotationStrategy: status?.rotationStrategy ?? "sticky",
|
|
2907
2957
|
busy,
|
|
2908
2958
|
labels: t,
|
|
2909
2959
|
loginBusyLabel: busy === "login" ? loginProgress || t.signingIn : void 0,
|
|
@@ -3027,6 +3077,76 @@ window.__ModuleLoader__.load({
|
|
|
3027
3077
|
})
|
|
3028
3078
|
]
|
|
3029
3079
|
}),
|
|
3080
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3081
|
+
className: "dsha-group",
|
|
3082
|
+
children: [
|
|
3083
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3084
|
+
className: "dsha-grouphead",
|
|
3085
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.cacheSection })
|
|
3086
|
+
}),
|
|
3087
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3088
|
+
className: "dsha-muted",
|
|
3089
|
+
children: t.cacheDesc
|
|
3090
|
+
}),
|
|
3091
|
+
status?.cache == null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3092
|
+
className: "dsha-empty",
|
|
3093
|
+
children: t.cacheEmpty
|
|
3094
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3095
|
+
className: "dsha-quota-card",
|
|
3096
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3097
|
+
className: "dsha-meter-wrap",
|
|
3098
|
+
children: [
|
|
3099
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3100
|
+
className: "dsha-meter-label",
|
|
3101
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t.cacheHitRatio }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: status.cache.hitRatio === null ? "—" : `${(status.cache.hitRatio * 100).toFixed(1)}%` })]
|
|
3102
|
+
}),
|
|
3103
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3104
|
+
className: `dsha-meter ${(status.cache.hitRatio ?? 0) >= .9 ? "dsha-meter-green" : "dsha-meter-cyan"}`,
|
|
3105
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { width: `${Math.round((status.cache.hitRatio ?? 0) * 100)}%` } })
|
|
3106
|
+
}),
|
|
3107
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3108
|
+
className: "dsha-meter-meta",
|
|
3109
|
+
children: [
|
|
3110
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
3111
|
+
t.cacheCached,
|
|
3112
|
+
": ",
|
|
3113
|
+
status.cache.cachedTokens.toLocaleString()
|
|
3114
|
+
] }),
|
|
3115
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
3116
|
+
t.cacheFresh,
|
|
3117
|
+
": ",
|
|
3118
|
+
status.cache.freshTokens.toLocaleString()
|
|
3119
|
+
] }),
|
|
3120
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
3121
|
+
t.cacheRequests,
|
|
3122
|
+
": ",
|
|
3123
|
+
status.cache.requests
|
|
3124
|
+
] })
|
|
3125
|
+
]
|
|
3126
|
+
})
|
|
3127
|
+
]
|
|
3128
|
+
}), status.cache.lastMiss !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3129
|
+
className: "dsha-row",
|
|
3130
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3131
|
+
className: "dsha-label",
|
|
3132
|
+
children: t.cacheMiss
|
|
3133
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3134
|
+
className: "dsha-value",
|
|
3135
|
+
children: missLabel(status.cache.lastMiss.cause, t)
|
|
3136
|
+
})]
|
|
3137
|
+
}), status.cache.lastMiss.idleMs !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3138
|
+
className: "dsha-row",
|
|
3139
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3140
|
+
className: "dsha-label",
|
|
3141
|
+
children: t.cacheMissIdle
|
|
3142
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3143
|
+
className: "dsha-value",
|
|
3144
|
+
children: idleLabel(status.cache.lastMiss.idleMs)
|
|
3145
|
+
})]
|
|
3146
|
+
})] })]
|
|
3147
|
+
})
|
|
3148
|
+
]
|
|
3149
|
+
}),
|
|
3030
3150
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3031
3151
|
className: "dsha-group",
|
|
3032
3152
|
children: [
|