@eddyskywalker/dsh-chatgpt-subscription 0.9.2 → 0.9.6

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
@@ -2,6 +2,28 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - **修复用户实测缺陷:Claude 线路缓存命中为 0(每次请求都按全新输入计费)**。根因**不是显示问题**:Anthropic 的提示缓存要求**请求携带显式的 `cache_control` 断点**,而本插件的请求构建器虽然**支持**这个能力,却**从未开启**——`mapper.ts` 里的选项默认 `false`,且 **adapter 从未传过它**(全仓库搜索该标识符只出现在 mapper 自身与其测试中)。因此没有任何一个字节的 `cache_control` 发出过,服务端无从缓存,`cache_read_input_tokens` 自然永远是 0。
6
+ - **对照本地参照实现,问题更清楚**:`pi-ai` 的 `resolveCacheRetention()` **默认返回 `"short"`**,即**默认缓存**,只有显式设为 `"none"` 才关;本插件恰好相反。这个"默认值反转"就是缺陷本身。
7
+ - **修法(四件事)**:
8
+ 1. **默认开启缓存**,且选项语义改为**opt-out**(`cacheControl !== false`)。这一点很重要:旧设计的失败模式正是「选项存在、调用方忘了传」,所以**默认值必须是缓存行为**——测试证明,即使把 adapter 的参数删掉,缓存**依然生效**,从机制上杜绝同一类缺陷复现。
9
+ 2. **补上真正省钱的断点**:原先**只**能标 system 块(而且其自身注释就写着"只标它会缓存不到任何变化的东西");现在标**三处**——system 末块、**最后一条 user 消息的最后一个块**(跨轮复用历史的关键)、最后一个 tool。断点数量受 `MAX_CACHE_BREAKPOINTS = 4`(服务端硬上限)约束,并有测试用 13 条消息 + 图片 + 多工具的压力场景验证不超限。
10
+ 3. **标记时机正确**:在 `mergeClaudeMessages` **之后**、对**最终**请求体做标记,避免断点落在合并后会被移动的块上。
11
+ 4. **前缀稳定性已核查**:断点之上若存在逐轮变化的值,缓存会整体失效、断点等于白标。已确认 Claude 请求路径中**没有** `Date.now` / `Math.random` / UUID 等易变值(`cooldownMsFor` 的 `Date.now` 是重试时钟、不进请求字节),且 `leadingSystemText` 是**有序纯折叠**、不重排。
12
+ - **为什么"看不到"不是显示问题**:`tokenUsage()` 确实会省略值为 0 的计数器,但 harness 的累加器会把缺失**归一化为 0**(`usage.cacheReadTokens ?? 0`),所以"省略 0"与"字面 0"在 UI 上表现一致。用户看到的是**真实的 0**——因为服务端确实无缓存可命中。
13
+ - **验证**:`tsc` 三个项目 **0 错误**;按 CI 配方本地跑 `npm run typecheck` + `npm run build` + `npm test` + `npm pack --dry-run` **全部通过**;全量测试 **1595 通过 / 0 失败**,且在**隐藏参照快照**(CI 的真实条件)下同样绿(1593 通过 / 9 跳过)。**独立复核**:我另写脚本 dump 真实请求体,确认三处断点出现在正确位置、**不传任何选项时即为 3 个**(证明默认开启)、显式 opt-out 时为 0。
14
+ - **未验证项(如实说明)**:服务端**最小可缓存提示长度**(约 1024 token,随模型而异)不在任何本地来源中,无法离线核实;且**本次未向真实端点发过请求**。因此结论是「**请求现在携带了使服务端缓存成为可能的断点**」,**不是**「已观测到服务端返回 `cache_read_input_tokens`」。
15
+
16
+
17
+ - **修复用户实测缺陷:选择 Claude Opus 5.5 必然失败(`claude_code_version_too_old`)**。上游原文:`Claude Code 2.1.251 does not support this model; version 2.1.280 or newer is required.` 那个 **2.1.251 是本插件自己申报的版本号**被服务端回显——本插件发布 `claude-opus-5-5`(该模型要求客户端 ≥ 2.1.280),却仍申报 2.1.251,两个数字自相矛盾,因此对该模型的每次请求都白花一个往返后被拒。**这是本插件引入的缺陷,与用户环境无关。**
18
+ - **根因不只是"数字写旧了"**:加模型与抬版本之间没有任何强制关系,所以同类缺陷会反复出现。修法分两层。
19
+ - **① 修正值与记录门槛**:`CLAUDE_CLI_VERSION` 由 `2.1.251` 改为 `2.1.283`(npm `latest` 的真实已发布版本,且 ≥ 2.1.280)。能力表新增可选字段 `minCliVersion`,**仅**给 `claude-opus-5-5` 填 `2.1.280`;其余 14 行**一律不填**,接口注释写明「缺省 = 未知门槛,不等于没有门槛」——不臆造无依据的数字。注:上游 npm `stable` tag 本身是 `2.1.274`,**低于该模型要求**,这正是 [anthropics/claude-code#96130](https://github.com/anthropics/claude-code/issues/96130) 报的坑。
20
+ - **② 本地预检,不再让上游报错**:新增 `assertClaudeCliVersionMeetsFloor()`,在 `requestStream()` **解析凭据之前、发起 fetch 之前**比较「生效版本 vs 该模型门槛」,低于则抛 `PROVIDER_ERROR`(**明确不是凭据错误**——错误文案直接写明「你的登录态仍然有效,重新登录不会改变它」,因为上游原文会误导用户去重新登录)。未记录门槛的模型行为不变;上游 400 分支保留,用于应对「本表尚未知的门槛」。
21
+ - **③ 加了会咬人的不变量锁**:测试断言「**申报的默认版本 ≥ 每一个模型的 `minCliVersion`**」,对**全表**遍历、且针对**申报常量本身**(不受 pin/环境变量影响),失败信息会同时点出两个版本号与修法。**我独立做了变异验证**:把 Opus 5.5 的门槛临时改成 `2.1.300`,测试立刻失败并输出 `CLAUDE_CLI_VERSION is 2.1.283 but claude-opus-5-5 requires 2.1.300 or newer…expected -17 to be greater than or equal to 0`;恢复后 22 项全绿。这条锁从此让「加了高门槛模型却忘抬版本」无法通过 CI。
22
+ - **排查过程中发现的另一件事**:npm 上 `0.9.0` 与 `0.9.2` **均已发布**,而**已发布的 0.9.2 仍然带着 `2.1.251` 且不含预检**(我拉了 tarball 核对)。同时 DSH web profile 通过软链接指向本仓库,而仓库的 `lib/` **是旧的**——所以运行中的插件确实跑的是有缺陷的构建。本次已重新 `npm run build`,构建产物现为 `2.1.283` + 预检(`lib` 由 .gitignore 覆盖,不入库)。
23
+ - **验证**:三个 tsconfig 均 **0 错误**;`npm test` **110 文件 / 1587 项通过、0 失败**;`npm run build` 成功。
24
+ - **临时可用的旁路**(无需等新版本):启动 DSH 前设 `DSH_CLAUDE_CLI_VERSION=2.1.283`。优先级为 pin > 环境变量 > 申报默认值。
25
+
26
+
5
27
  - **修复 Kimi Code 设置卡片「过一段时间就报 `rejected the stored credential (401)`」**:这不是登录态过期,而是**卡片拿着一个已经过期的 access token 去查用量**,并把服务端的 401 读成了「凭据被拒绝」。
6
28
  - **根因一:卡片从不刷新号池凭据**。`/status`、`/quota`、`/connection/test` 都把号池里的 credentials 原样交给 `fetchAccountQuota`,而它只在**没有**传入凭据时才会刷新——号池的刷新只发生在模型请求路径上(`getEffectiveAccount`)。Kimi 的 access token 只有 **900 秒**(实测本机凭据 `exp - iat = 900`),于是「15 分钟没发过 Kimi 消息」=「打开设置页必现 401」。实测本机两份存储:镜像里的 token 有效,号池里的 access token 已过期——正是这一条。
7
29
  - **根因二:同一枚轮换 refresh token 被两条路径各自刷新**。单凭据文件是号池主账号的**镜像**,但目录加载会绕过号池去刷它(`loadProviderModels` → `ensureAccessToken(store)`,且 stale-while-revalidate 会在后台再刷一次)。Kimi 的 refresh token 轮换后旧的即作废,于是镜像先刷成功就把号池那份变成死令牌;适配器下次刷新拿到 `invalid_grant`,账号被标 `authStatus=expired` 移出轮换,而进程内的「已拒绝」墓碑只在**重新登录**时清除——必须重新登录才能恢复。官方 CLI 正是为同一问题加了跨进程文件锁。
package/README.md CHANGED
@@ -424,6 +424,12 @@ DSH 设置页的「Subagent」卡片会把勾选的模型写成会话级的允
424
424
  「强制 effort」分支,若把 Opus 5.5 一并归入,就会**静默把每次请求抬高一档、花更多钱**,因此它走的是普通 adaptive
425
425
  分支。
426
426
 
427
+ 它还有一条**版本门槛**:上游要求申报的客户端版本 **≥ 2.1.280** 才为该模型提供服务。能力表用 `minCliVersion`
428
+ 记录这一点(**只有这一行有门槛**,其余 14 行缺省——缺省表示「未知」而不是「无门槛」,不臆造数字)。插件申报的
429
+ 默认版本为 **2.1.283**,并且**在发出请求之前就本地校验**「申报版本 vs 该模型门槛」,不满足时直接给出模型、当前
430
+ 版本与要求版本,**而不是白花一个往返让上游返回 400**。测试另有一条不变量:**申报的默认版本必须 ≥ 表中每个模型的
431
+ 门槛**——这条锁让「加了高门槛模型却忘了抬版本」无法通过 CI。
432
+
427
433
  ### 收编本机已有的 Claude Code 登录(可选,默认关闭)
428
434
 
429
435
  如果你本机已经登录过 Claude Code,可以点「收编本机登录」把它作为一条账号导入,省去再次登录。
@@ -559,7 +565,8 @@ npm pack --dry-run
559
565
  | Claude 相关接口返回 403 | 403 现在是**同源校验**的结果(修改状态的路由只接受同源 JSON POST)。若出现在浏览器里,检查是否从其它来源发起了请求;插件已不再有任何"确认后才能用"的门禁 |
560
566
  | 授权后粘贴授权码报「请把 # 后面的部分一起复制」 | 手动流程需要 `<授权码>#<state>` **整段**。只粘贴授权码是不被接受的:用流程自己的 state 顶上会架空 state 参数、把粘贴框变成登录 CSRF 的入口,因此插件宁可报错也不猜 |
561
567
  | loopback 回调收不到 / 端口绑定失败 | 端口探测失败会自动**降级为手动粘贴**(卡片会说明原因),照提示粘贴授权码即可。另:若浏览器把 `localhost` 解析到 `::1`,依赖浏览器的跨地址族回退可达本机 v4 监听,这是**假定**而非实测 |
562
- | 模型请求报持续 400 / 「You're out of extra usage」 | 可能是身份或版本门槛:订阅令牌要求 Claude Code 身份头与 `system` 首块,且服务端会校验你申报的客户端版本(过旧会答 `claude_code_version_too_old`)。可用 `DSH_CLAUDE_CLI_VERSION` 抬高申报版本;该错误被归类为**请求问题而非凭据问题**,不会把你登出 |
568
+ | 选择某个模型报 `claude_code_version_too_old`(例如 Opus 5.5 要求 2.1.280+) | 上游会校验本插件**申报的**客户端版本,新模型有各自的最低版本要求。插件现在会**在发出请求之前**本地拦下并告知模型、当前版本与要求版本(归类为**请求问题而非凭据问题**,**不会把你登出**——重新登录也不会有帮助)。用 `DSH_CLAUDE_CLI_VERSION` 抬高申报版本即可;已发布版本 ≥2.1.283 时 Opus 5.5 可直接使用 |
569
+ | 模型请求报持续 400 / 「You're out of extra usage」 | 可能是身份或版本门槛:订阅令牌要求 Claude Code 身份头与 `system` 首块,且服务端会校验你申报的客户端版本。此类错误被归类为**请求问题而非凭据问题**,不会把你登出 |
563
570
  | 额度显示为空 / 某项显示「—」 | 额度接口的键集随账号类型变化,未提供的窗口会显示为未知而**不会伪造 0**。注意 **`utilization` 是已用百分比,0% 表示尚未使用(正常态)**,不是额度耗尽;真正的耗尽会以 429 与响应头状态呈现。接口被限流时卡片保留上次成功快照并标注时间 |
564
571
  | 收编后提示「本机 Claude Code 登录已过期」 | 这是设计如此:收编的是**快照**,插件永不刷新它(Claude Code 刷新同一枚轮换令牌,两个进程各自刷新会互相作废)。回 Claude Code 重新登录后**再收编一次**即可 |
565
572
  | 账号被标为「无法识别账号身份」 | 服务端这个账号既没返回 uuid 也没返回 email,插件无法在再次登录时自动认出它。用卡片上的**手动合并**把它并入既有账号;这是已知限制,插件不会用假 id 掩盖它 |
package/lib/index.js CHANGED
@@ -21954,20 +21954,38 @@ const INTERLEAVED_THINKING_BETA = "interleaved-thinking-2025-05-14";
21954
21954
  /** Anthropic protocol revision sent as `anthropic-version`. */
21955
21955
  const ANTHROPIC_VERSION = "2023-06-01";
21956
21956
  /**
21957
- * Baseline client version this route reports to the subscription gateway.
21958
- *
21959
- * Upstream enforces a *minimum reported client version* on the subscription
21960
- * path: a value below the floor is refused with HTTP 400 and an error body whose
21961
- * code is `claude_code_version_too_old`. That is a hard stop for every request
21962
- * rather than a per-request failure, so it cannot be a compile-time literal that
21963
- * only moves when this package is republished — a user whose installed baseline
21964
- * has aged past the server's floor has to be able to raise it without waiting
21965
- * for a release.
21966
- *
21967
- * Read the effective value with {@link claudeCliVersion}, pin one with
21968
- * {@link setClaudeCliVersion}.
21969
- */
21970
- const CLAUDE_CLI_VERSION = "2.1.251";
21957
+ * Client version this route CLAIMS to be on the subscription gateway.
21958
+ *
21959
+ * Upstream enforces a *minimum reported client version* per model on the
21960
+ * subscription path: the version travels in the `user-agent` header (built by
21961
+ * `claudeUserAgent` in client.ts) and a claim below the floor is refused with
21962
+ * HTTP 400 and an error body whose code is `claude_code_version_too_old`. That
21963
+ * is a hard stop for the whole request rather than a per-request failure, and the
21964
+ * number in the refusal is OUR OWN claim echoed back — so the user is told their
21965
+ * client is too old without being told which model demanded more, which is
21966
+ * exactly how it misleads people into signing in again.
21967
+ *
21968
+ * THE INVARIANT THIS CONSTANT CARRIES. It must be at or above the highest
21969
+ * `minCliVersion` that any shipped model in `model-catalog.ts` declares. A
21970
+ * default below a shipped model's floor is a model this package advertises in
21971
+ * its own picker and then cannot call at all, which is the bug this comment now
21972
+ * records: `2.1.251` shipped while `claude-opus-5-5` — also shipped, and the
21973
+ * row the default picker leads with — requires `2.1.280`. The matching
21974
+ * regression lock lives in `test/claude-model-catalog.test.ts` and fails
21975
+ * loudly, naming both versions, when a future model's floor is added without
21976
+ * raising this value.
21977
+ *
21978
+ * The value is a REAL published `@anthropic-ai/claude-code` release, not a
21979
+ * number chosen merely to clear a floor: claiming a version that does not exist
21980
+ * trades a clear refusal for an unpredictable one. Note that npm's `stable`
21981
+ * dist-tag can itself sit below a model's floor, so `latest` is the tag to read.
21982
+ *
21983
+ * It is still not a literal that only moves when this package is republished: a
21984
+ * user whose installed baseline has aged past the server's floor has to be able
21985
+ * to raise it without waiting for a release. Read the effective value with
21986
+ * {@link claudeCliVersion}, pin one with {@link setClaudeCliVersion}.
21987
+ */
21988
+ const CLAUDE_CLI_VERSION = "2.1.283";
21971
21989
  /**
21972
21990
  * Loopback callback port the subscription flow defaults to.
21973
21991
  *
@@ -22069,6 +22087,67 @@ function setClaudeCliVersion(value) {
22069
22087
  cliVersionOverride = normalized === "" ? null : normalized;
22070
22088
  }
22071
22089
  /**
22090
+ * Compare two dotted numeric versions, e.g. `'2.1.283'` against `'2.1.280'`.
22091
+ *
22092
+ * This exists so a version FLOOR can be checked before a request is sent. The
22093
+ * comparison is deliberately small and TOTAL rather than a SemVer parser: the
22094
+ * only strings it ever sees are this line's own reported version (a pin, the
22095
+ * `DSH_CLAUDE_CLI_VERSION` override, or the shipped default) and the floors
22096
+ * recorded in the model catalog, and it must not throw on any of them — a
22097
+ * comparison that can throw would turn a misconfigured version into a crash
22098
+ * instead of the precise pre-flight refusal it was added to produce.
22099
+ *
22100
+ * Total means: a missing segment compares as 0 (`'2.1'` equals `'2.1.0'`), and a
22101
+ * segment that is not a run of decimal digits compares as 0 rather than as NaN
22102
+ * (`'abc'` equals `'0.0.0'`, and so does `''`). Leading zeros are exact rather
22103
+ * than octal, and an optional leading `v` is tolerated because that is how a
22104
+ * release is often written down.
22105
+ *
22106
+ * @returns a negative number, 0, or a positive number — the sign is the whole
22107
+ * contract, so callers compare against 0 rather than against ±1.
22108
+ */
22109
+ function compareDottedVersions(left, right) {
22110
+ const a = versionSegments(left);
22111
+ const b = versionSegments(right);
22112
+ const length = Math.max(a.length, b.length);
22113
+ for (let index = 0; index < length; index += 1) {
22114
+ const difference = (a[index] ?? 0) - (b[index] ?? 0);
22115
+ if (difference !== 0) return difference;
22116
+ }
22117
+ return 0;
22118
+ }
22119
+ /**
22120
+ * Whether one reported version clears a floor.
22121
+ *
22122
+ * An absent floor (`undefined`) means "no known floor" rather than "no floor",
22123
+ * so it never blocks a request: refusing a model because nobody recorded a
22124
+ * number would be this line inventing a restriction upstream never stated.
22125
+ * A MALFORMED version on either side, on the other hand, is refused — that is
22126
+ * the conservative direction, since the check exists to stop an unknown claim
22127
+ * from reaching a server that has already refused one.
22128
+ */
22129
+ function meetsDottedVersionFloor(version, floor) {
22130
+ if (floor === void 0 || floor.trim() === "") return true;
22131
+ if (!isDottedVersion(version)) return false;
22132
+ return compareDottedVersions(version, floor) >= 0;
22133
+ }
22134
+ /** Whether a string is a run of dot-separated decimal segments (an optional leading `v`). */
22135
+ function isDottedVersion(value) {
22136
+ const trimmed = value.trim().replace(/^v/i, "");
22137
+ return /^\d+(\.\d+)*$/.test(trimmed);
22138
+ }
22139
+ /** Numeric segments of a dotted version; a non-numeric or absent segment is 0. */
22140
+ function versionSegments(value) {
22141
+ const trimmed = value.trim().replace(/^v/i, "");
22142
+ if (trimmed === "") return [0];
22143
+ return trimmed.split(".").map((segment) => {
22144
+ const digits = /^\d+$/.test(segment.trim()) ? segment.trim() : "";
22145
+ if (digits === "") return 0;
22146
+ const parsed = Number.parseInt(digits, 10);
22147
+ return Number.isFinite(parsed) ? parsed : 0;
22148
+ });
22149
+ }
22150
+ /**
22072
22151
  * Resolve the browser authorization endpoint.
22073
22152
  *
22074
22153
  * The subscription flow has answered on more than one host, while its Console
@@ -22307,6 +22386,14 @@ function loopbackRedirectUri(port = DEFAULT_CALLBACK_PORT) {
22307
22386
  *
22308
22387
  * A future reader who notices the shared `supportsTemperature: false` and
22309
22388
  * "fixes" item 2 to 'mid-convo' has reintroduced exactly that bug.
22389
+ *
22390
+ * One more field on that row comes from neither source: its `minCliVersion`
22391
+ * floor is the number upstream itself states when it refuses the model
22392
+ * (`claude_code_version_too_old`, "version 2.1.280 or newer is required"). That
22393
+ * is a stronger witness than a documentation page — the server said it — and it
22394
+ * is recorded here so a claim below it is refused locally, before the request is
22395
+ * sent, instead of as an upstream 400 that names neither the model nor the number
22396
+ * the user has to reach.
22310
22397
  */
22311
22398
  /**
22312
22399
  * Fifteen rows: the snapshot's own 14, in the snapshot's own declaration order,
@@ -22577,7 +22664,8 @@ const CLAUDE_MODELS = Object.freeze([
22577
22664
  "xhigh",
22578
22665
  "max"
22579
22666
  ],
22580
- canDisableThinking: false
22667
+ canDisableThinking: false,
22668
+ minCliVersion: "2.1.280"
22581
22669
  }
22582
22670
  ]);
22583
22671
  /** Every id the fallback table knows, in declaration order. */
@@ -22681,6 +22769,24 @@ function claudeModelSupportsTemperature(modelId, catalog) {
22681
22769
  function claudeModelCanDisableThinking(modelId, catalog) {
22682
22770
  return resolveClaudeModel(modelId, catalog ?? CLAUDE_MODELS).canDisableThinking;
22683
22771
  }
22772
+ /**
22773
+ * Lowest reported client version upstream serves one model to, when known.
22774
+ *
22775
+ * `undefined` means "no known floor" — NOT "no floor" — so a caller must treat it
22776
+ * as "nothing to check" rather than as "any version is fine". The distinction
22777
+ * matters because the caller is a pre-flight refusal: an absent floor must let
22778
+ * the request through (refusing it would invent a restriction nobody recorded),
22779
+ * while a present floor must block a claim below it.
22780
+ *
22781
+ * Unlike the other per-model lookups this one does NOT resolve through the
22782
+ * conservative stub for an unknown id. The stub describes capabilities this line
22783
+ * assumes; a version floor is a fact about the server that this line either has
22784
+ * or has not observed, and manufacturing one for an unknown id would refuse a
22785
+ * model nobody has evidence against.
22786
+ */
22787
+ function claudeMinCliVersionFor(modelId, catalog) {
22788
+ return (catalog ?? CLAUDE_MODELS).find((model) => model.id === modelId)?.minCliVersion;
22789
+ }
22684
22790
  //#endregion
22685
22791
  //#region src/host/claude/token-store.ts
22686
22792
  /**
@@ -24354,6 +24460,36 @@ function classifyFailure(status, bodyText, headers = {}) {
24354
24460
  * block may be replayed at all arrives at the END of the block, so a signed
24355
24461
  * block cannot be recognized before then, and a reasoning block that cannot be
24356
24462
  * replayed is not content the caller should be told it received.
24463
+ *
24464
+ * ---------------------------------------------------------------------------
24465
+ * 7. PROMPT CACHING IS ON BY DEFAULT, AND IT IS THE REQUEST THAT TURNS IT ON
24466
+ * ---------------------------------------------------------------------------
24467
+ *
24468
+ * Anthropic's prompt cache is REQUEST-DRIVEN: the server caches a prefix only
24469
+ * where the request puts an explicit cache_control breakpoint, and a request
24470
+ * with no breakpoint gets no cache_read_input_tokens back no matter how
24471
+ * identical its bytes are turn over turn. Emitting the breakpoints is therefore
24472
+ * this module's job, and it is the DEFAULT: ClaudeRequestOptions.cacheControl
24473
+ * is true unless a caller explicitly states false. The reference implementation
24474
+ * this line is transcribed from resolves the same way (its resolveCacheRetention
24475
+ * defaults to 'short'; only an explicit 'none' skips caching), and the
24476
+ * asymmetry is deliberate - a caller that says NOTHING must still get caching,
24477
+ * because an opt-in flag that one call site forgets to pass is exactly how this
24478
+ * request stopped carrying breakpoints in the first place.
24479
+ *
24480
+ * Three sites are marked, which is three of the four breakpoints the wire
24481
+ * accepts (see MAX_CACHE_BREAKPOINTS): the LAST system block, the last block of
24482
+ * the LAST user message, and the last tool. The first covers the identity block
24483
+ * and the caller's prompt; the second is what actually caches CONVERSATION
24484
+ * HISTORY across turns, since it sits at the end of everything the next turn
24485
+ * will resend; the third keeps the tool table inside the cached prefix instead
24486
+ * of behind the first message breakpoint.
24487
+ *
24488
+ * Where the marking happens matters as much as that it happens: the message
24489
+ * breakpoint goes on the FINAL merged list, after mergeClaudeMessages has
24490
+ * folded consecutive same-role turns together. Marking the pre-merge list can
24491
+ * land the breakpoint on a block the merge then moves back, which leaves a
24492
+ * marker in the request that caches nothing the next turn can reuse.
24357
24493
  */
24358
24494
  function isRecord$3(value) {
24359
24495
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -24398,6 +24534,20 @@ const CLAUDE_CODE_IDENTITY_TEXT = "You are Claude Code, Anthropic's official CLI
24398
24534
  /** Answer room the budget clamp always leaves below the response ceiling. */
24399
24535
  const MIN_ANSWER_TOKENS = 1024;
24400
24536
  /**
24537
+ * Block types the wire accepts a cache_control marker on.
24538
+ *
24539
+ * A marker on any other block is a request the server rejects, so the last
24540
+ * block of the last user message is marked only when its type is one of these
24541
+ * (TRANSCRIBED from the reference, which guards on exactly this set). The
24542
+ * absence of thinking blocks is not an oversight: they only ever appear in
24543
+ * ASSISTANT turns, and this module never marks an assistant block.
24544
+ */
24545
+ const CACHE_MARKABLE_BLOCK_TYPES = /* @__PURE__ */ new Set([
24546
+ "text",
24547
+ "image",
24548
+ "tool_result"
24549
+ ]);
24550
+ /**
24401
24551
  * Thinking budget one level asks for, before any clamp.
24402
24552
  *
24403
24553
  * TRANSCRIBED from simple-options.js lines 38-43. The values are the
@@ -24959,13 +25109,15 @@ function leadingSystemText(options) {
24959
25109
  * The system array for one request: identity first, always.
24960
25110
  *
24961
25111
  * The identity block is not conditional on anything (see the module doc). The
24962
- * optional ephemeral cache marker is opt-in and goes on the LAST block, which
24963
- * is the Anthropic-recommended shape: a breakpoint at the end of the prompt
24964
- * caches everything above it, so marking the user block also covers the
24965
- * identity block, while marking the identity block alone would cache nothing
24966
- * that changes.
25112
+ * ephemeral cache marker goes on the LAST block and is ON BY DEFAULT, which is
25113
+ * the Anthropic-recommended shape: a breakpoint at the end of the prompt caches
25114
+ * everything above it, so marking the user block also covers the identity
25115
+ * block, while marking the identity block alone would cache nothing that
25116
+ * changes.
25117
+ *
25118
+ * @param cacheControl - false is the opt-OUT; the default requests caching.
24967
25119
  */
24968
- function buildClaudeSystemBlocks(options, systemCacheControl = false) {
25120
+ function buildClaudeSystemBlocks(options, cacheControl = true) {
24969
25121
  const blocks = [{
24970
25122
  type: "text",
24971
25123
  text: CLAUDE_CODE_IDENTITY_TEXT
@@ -24975,12 +25127,74 @@ function buildClaudeSystemBlocks(options, systemCacheControl = false) {
24975
25127
  type: "text",
24976
25128
  text: userText
24977
25129
  });
24978
- if (systemCacheControl) {
25130
+ if (cacheControl) {
24979
25131
  const last = blocks[blocks.length - 1];
24980
25132
  last.cache_control = { type: "ephemeral" };
24981
25133
  }
24982
25134
  return blocks;
24983
25135
  }
25136
+ /**
25137
+ * The last block worth a cache breakpoint on the message list, if any.
25138
+ *
25139
+ * TRANSCRIBED from the reference, which marks the last block of the last USER
25140
+ * message and nothing else. Two guards come with it, and both are load-bearing:
25141
+ * the last message must be a user turn (a history ending on an assistant turn -
25142
+ * a prefill-shaped request - gets no message breakpoint rather than a marker
25143
+ * somewhere it was not asked for), and the block must be one the wire accepts a
25144
+ * marker on. An unmarkable block yields NO breakpoint rather than one moved
25145
+ * onto an earlier turn: an unmasked request is merely uncached, while a marker
25146
+ * on a block type the wire rejects fails the whole turn.
25147
+ */
25148
+ function lastCacheableUserBlock(messages) {
25149
+ const lastMessage = messages[messages.length - 1];
25150
+ if (lastMessage === void 0 || lastMessage.role !== "user") return void 0;
25151
+ const block = lastMessage.content[lastMessage.content.length - 1];
25152
+ if (block === void 0) return void 0;
25153
+ return CACHE_MARKABLE_BLOCK_TYPES.has(String(block.type)) ? block : void 0;
25154
+ }
25155
+ /**
25156
+ * Put the cache breakpoints on one built body, and report how many landed.
25157
+ *
25158
+ * Split out from the builder so the marking runs against the FINAL shape - the
25159
+ * merged message list and the full tool array - rather than against the
25160
+ * intermediate one (see the module doc's section 7). An assistant turn after
25161
+ * the last user turn (a truncated history, or a request that appends an
25162
+ * assistant preamble) leaves that turn's tail unmarked; marking it would put
25163
+ * the marker below the assistant content and cache a prefix the next request
25164
+ * does not resend.
25165
+ *
25166
+ * @param body - the built body, mutated in place: it was created by this module
25167
+ * and has not been handed to a caller yet.
25168
+ * @returns the number of breakpoints written.
25169
+ */
25170
+ function markClaudeCacheBreakpoints(body) {
25171
+ let marked = 0;
25172
+ const system = body.system;
25173
+ if (Array.isArray(system)) {
25174
+ const last = system[system.length - 1];
25175
+ if (last !== void 0) {
25176
+ last.cache_control = { type: "ephemeral" };
25177
+ marked += 1;
25178
+ }
25179
+ }
25180
+ const messages = body.messages;
25181
+ if (Array.isArray(messages)) {
25182
+ const target = lastCacheableUserBlock(messages);
25183
+ if (target !== void 0) {
25184
+ target.cache_control = { type: "ephemeral" };
25185
+ marked += 1;
25186
+ }
25187
+ }
25188
+ const tools = body.tools;
25189
+ if (Array.isArray(tools) && tools.length > 0) {
25190
+ const last = tools[tools.length - 1];
25191
+ if (last !== void 0) {
25192
+ last.cache_control = { type: "ephemeral" };
25193
+ marked += 1;
25194
+ }
25195
+ }
25196
+ return marked;
25197
+ }
24984
25198
  /** One tool definition in the wire shape. */
24985
25199
  function wireTool(tool, names) {
24986
25200
  const schema = isRecord$3(tool.parameters) ? { ...tool.parameters } : {
@@ -25180,7 +25394,8 @@ function mergeClaudeMessages(entries) {
25180
25394
  *
25181
25395
  * @param options - request options carrying the normalized conversation.
25182
25396
  * @param images - images read for this request by resolveRequestImages.
25183
- * @param request - cache and translation options.
25397
+ * @param request - cache and translation options. Caching is the DEFAULT and
25398
+ * is skipped only when request.cacheControl is explicitly false.
25184
25399
  */
25185
25400
  function buildClaudeRequestBody(options, images = NO_RESOLVED_IMAGES, request = {}) {
25186
25401
  const names = request.toolNames ?? claudeToolNames(options.tools);
@@ -25206,11 +25421,12 @@ function buildClaudeRequestBody(options, images = NO_RESOLVED_IMAGES, request =
25206
25421
  }
25207
25422
  const thinkingEnabled = thinking !== void 0 && thinking.type !== "disabled";
25208
25423
  const temperatureAllowed = claudeModelSupportsTemperature(options.model) && !thinkingEnabled;
25209
- return {
25424
+ const caching = request.cacheControl !== false;
25425
+ const body = {
25210
25426
  model: options.model,
25211
25427
  max_tokens: maxTokens,
25212
25428
  stream: true,
25213
- system: buildClaudeSystemBlocks(options, request.systemCacheControl === true),
25429
+ system: buildClaudeSystemBlocks(options, caching),
25214
25430
  messages: mergeClaudeMessages(entries),
25215
25431
  ...options.tools !== void 0 && options.tools.length > 0 ? { tools: options.tools.map((tool) => wireTool(tool, names)) } : {},
25216
25432
  ...thinking === void 0 ? {} : { thinking },
@@ -25218,6 +25434,8 @@ function buildClaudeRequestBody(options, images = NO_RESOLVED_IMAGES, request =
25218
25434
  ...temperatureAllowed && options.temperature !== void 0 ? { temperature: options.temperature } : {},
25219
25435
  ...options.stop !== void 0 && options.stop.length > 0 ? { stop_sequences: [...options.stop] } : {}
25220
25436
  };
25437
+ if (caching) markClaudeCacheBreakpoints(body);
25438
+ return body;
25221
25439
  }
25222
25440
  /**
25223
25441
  * Fresh stream state.
@@ -26804,7 +27022,11 @@ function cancelLogin() {
26804
27022
  * A reported-client-version rejection is a REQUEST problem even though it
26805
27023
  * arrives as a 400 that mentions the client: it must NOT sign the user out, and
26806
27024
  * it has a real remedy (raise the reported version), so it is handled
26807
- * explicitly with that remedy in the message.
27025
+ * explicitly with that remedy in the message. Where the catalog records a
27026
+ * model's floor, the same verdict is produced LOCALLY before the request is sent
27027
+ * ({@link assertClaudeCliVersionMeetsFloor}), so the ordinary case never spends
27028
+ * a request to be told the same thing less precisely; the upstream branch above
27029
+ * stays for a claim that is too low for a floor this table has not learned yet.
26808
27030
  *
26809
27031
  * ---------------------------------------------------------------------------
26810
27032
  * 5. ASSUMPTIONS AND JUDGEMENT CALLS
@@ -26891,6 +27113,46 @@ const MAX_COOLDOWN_MS = 300 * 60 * 1e3;
26891
27113
  /** What the user is told when this line has no credential to call with. */
26892
27114
  const MISSING_CREDENTIAL_MESSAGE = "Not signed in to Claude(订阅). Sign in from Settings > Claude.";
26893
27115
  /**
27116
+ * Refuse one model locally, before the request is sent, when the version this
27117
+ * route reports is below the floor that model declares.
27118
+ *
27119
+ * WHY THIS EXISTS. Upstream validates the reported client version — the
27120
+ * `user-agent` built by client.ts's `claudeUserAgent` — against a per-model
27121
+ * minimum and answers a claim below it with an HTTP 400 whose code is
27122
+ * `claude_code_version_too_old`. That refusal is correct but nearly unusable as
27123
+ * a diagnosis: the version in its text is OUR OWN claim echoed back, the model
27124
+ * is only identifiable from the request that was just consumed, and the number
27125
+ * the user has to reach is buried in prose. The predictable consequence was the
27126
+ * reported bug — a user reading "your client is too old" about a model this
27127
+ * package advertises, whose remedy the text never names, and whose wording reads
27128
+ * like a credential problem often enough that people sign in again for nothing.
27129
+ *
27130
+ * The catalog now records the floor per model (`minCliVersion`), so the same
27131
+ * verdict can be produced HERE, at zero cost, naming the model, the version in
27132
+ * force, the version required, and the remedy. The upstream refusal is left in
27133
+ * place exactly as it was — this is an addition to the path, not a replacement,
27134
+ * because a claim that clears every floor this table knows can still be too low
27135
+ * for something the table has not learned yet, and `toLlmError` still handles
27136
+ * that case with the same remedy.
27137
+ *
27138
+ * WHAT IT MUST NOT DO. It must not disable a model for anyone who never picks it:
27139
+ * the check runs on the REQUEST path, against the model actually being called, so
27140
+ * a floor for one row cannot remove another row from the picker. It must not be a
27141
+ * credential error either — nothing is wrong with the stored sign-in, and the
27142
+ * message says so outright, because signing in again is the wrong move and the
27143
+ * upstream text invites it.
27144
+ *
27145
+ * Called with the model the request is about to name, and with the catalog that
27146
+ * request resolved against, so a floor added for a live-only id is honoured too.
27147
+ */
27148
+ function assertClaudeCliVersionMeetsFloor(modelId, catalog = FALLBACK_MODELS) {
27149
+ const floor = claudeMinCliVersionFor(modelId, catalog);
27150
+ if (floor === void 0) return;
27151
+ const effective = claudeCliVersion();
27152
+ if (meetsDottedVersionFloor(effective, floor)) return;
27153
+ throw new LlmError("Claude(订阅) will not serve " + modelId + " to a client reporting version " + effective + ": upstream requires " + floor + " or newer for this model, and a lower claim is refused with claude_code_version_too_old. This was caught locally, before the request was sent. Raise the reported version with the DSH_CLAUDE_CLI_VERSION environment variable, or with a setClaudeCliVersion pin, then retry. The stored sign-in is still valid — this is not a credential problem and signing in again will not change it.", "PROVIDER_ERROR");
27154
+ }
27155
+ /**
26894
27156
  * The immutable id of the account a token came from, presented as the two-method
26895
27157
  * store oauth.ts wants. See module note 3.
26896
27158
  */
@@ -26975,6 +27237,23 @@ var ClaudeAdapter = class extends LlmAdapter {
26975
27237
  const live = await (this.options.loadCatalog ?? ((current) => loadCatalog(current, { fetchFn: this.options.fetchFn })))(credentials).catch(() => []);
26976
27238
  return live.length > 0 ? live : FALLBACK_MODELS;
26977
27239
  }
27240
+ /**
27241
+ * The catalog a REQUEST should be validated against.
27242
+ *
27243
+ * The same two branches {@link resolveModel} takes, in the same order, because
27244
+ * the live listing is authoritative for which ids exist and a floor recorded
27245
+ * for a live-only id would otherwise be skipped. An absent credential is not an
27246
+ * error here: with no account there is nothing to list and the shipped table is
27247
+ * the only answer available, which is exactly what the preview path already
27248
+ * assumes. A credential that cannot be read degrades to the shipped table for
27249
+ * the same reason — the floor check must not be the thing that turns a listing
27250
+ * failure into a failed request.
27251
+ */
27252
+ async catalogForRequest() {
27253
+ const record = await this.primaryAccount(await this.settings()).catch(() => null);
27254
+ if (record === null) return FALLBACK_MODELS;
27255
+ return this.catalog(record.credentials);
27256
+ }
26978
27257
  async listModels(provider) {
26979
27258
  const prov = provider || "claude-subscription";
26980
27259
  const settings = await this.settings();
@@ -27074,7 +27353,10 @@ var ClaudeAdapter = class extends LlmAdapter {
27074
27353
  * no credentials were resolved for it.
27075
27354
  */
27076
27355
  async attemptRequest(credentials, requestOptions, images, toolNames, thinking, signal, fetchFn) {
27077
- const body = JSON.stringify(buildClaudeRequestBody(requestOptions, images, { toolNames }));
27356
+ const body = JSON.stringify(buildClaudeRequestBody(requestOptions, images, {
27357
+ toolNames,
27358
+ cacheControl: true
27359
+ }));
27078
27360
  try {
27079
27361
  return await fetchFn("https://api.anthropic.com/v1/messages?beta=true", {
27080
27362
  method: "POST",
@@ -27096,6 +27378,7 @@ var ClaudeAdapter = class extends LlmAdapter {
27096
27378
  const requestOptions = offloadOldestRequestImages(normalizeGenerateOptions(options));
27097
27379
  const images = await resolveRequestImages(requestOptions, this.options.attachments, signal);
27098
27380
  const toolNames = claudeToolNames(requestOptions.tools);
27381
+ assertClaudeCliVersionMeetsFloor(requestOptions.model, await this.catalogForRequest());
27099
27382
  const effort = requestOptions.reasoningEffort === void 0 || requestOptions.reasoningEffort === null ? void 0 : String(requestOptions.reasoningEffort);
27100
27383
  const thinking = claudeRequestThinks(requestOptions.model, effort);
27101
27384
  const pool = this.accountPool;
@@ -81,7 +81,11 @@
81
81
  * A reported-client-version rejection is a REQUEST problem even though it
82
82
  * arrives as a 400 that mentions the client: it must NOT sign the user out, and
83
83
  * it has a real remedy (raise the reported version), so it is handled
84
- * explicitly with that remedy in the message.
84
+ * explicitly with that remedy in the message. Where the catalog records a
85
+ * model's floor, the same verdict is produced LOCALLY before the request is sent
86
+ * ({@link assertClaudeCliVersionMeetsFloor}), so the ordinary case never spends
87
+ * a request to be told the same thing less precisely; the upstream branch above
88
+ * stays for a claim that is too low for a floor this table has not learned yet.
85
89
  *
86
90
  * ---------------------------------------------------------------------------
87
91
  * 5. ASSUMPTIONS AND JUDGEMENT CALLS
@@ -125,6 +129,40 @@ import { type AttachmentImageReader } from './mapper.ts';
125
129
  * message, and this is only the fallback for a response that states neither.
126
130
  */
127
131
  export declare const POOL_COOLDOWN_MS: number;
132
+ /**
133
+ * Refuse one model locally, before the request is sent, when the version this
134
+ * route reports is below the floor that model declares.
135
+ *
136
+ * WHY THIS EXISTS. Upstream validates the reported client version — the
137
+ * `user-agent` built by client.ts's `claudeUserAgent` — against a per-model
138
+ * minimum and answers a claim below it with an HTTP 400 whose code is
139
+ * `claude_code_version_too_old`. That refusal is correct but nearly unusable as
140
+ * a diagnosis: the version in its text is OUR OWN claim echoed back, the model
141
+ * is only identifiable from the request that was just consumed, and the number
142
+ * the user has to reach is buried in prose. The predictable consequence was the
143
+ * reported bug — a user reading "your client is too old" about a model this
144
+ * package advertises, whose remedy the text never names, and whose wording reads
145
+ * like a credential problem often enough that people sign in again for nothing.
146
+ *
147
+ * The catalog now records the floor per model (`minCliVersion`), so the same
148
+ * verdict can be produced HERE, at zero cost, naming the model, the version in
149
+ * force, the version required, and the remedy. The upstream refusal is left in
150
+ * place exactly as it was — this is an addition to the path, not a replacement,
151
+ * because a claim that clears every floor this table knows can still be too low
152
+ * for something the table has not learned yet, and `toLlmError` still handles
153
+ * that case with the same remedy.
154
+ *
155
+ * WHAT IT MUST NOT DO. It must not disable a model for anyone who never picks it:
156
+ * the check runs on the REQUEST path, against the model actually being called, so
157
+ * a floor for one row cannot remove another row from the picker. It must not be a
158
+ * credential error either — nothing is wrong with the stored sign-in, and the
159
+ * message says so outright, because signing in again is the wrong move and the
160
+ * upstream text invites it.
161
+ *
162
+ * Called with the model the request is about to name, and with the catalog that
163
+ * request resolved against, so a floor added for a live-only id is honoured too.
164
+ */
165
+ export declare function assertClaudeCliVersionMeetsFloor(modelId: string, catalog?: readonly ClaudeModelEntry[]): void;
128
166
  /**
129
167
  * The pool surface this adapter uses, stated structurally.
130
168
  *
@@ -196,6 +234,19 @@ export declare class ClaudeAdapter extends LlmAdapter {
196
234
  * caller-supplied seam that does neither.
197
235
  */
198
236
  private catalog;
237
+ /**
238
+ * The catalog a REQUEST should be validated against.
239
+ *
240
+ * The same two branches {@link resolveModel} takes, in the same order, because
241
+ * the live listing is authoritative for which ids exist and a floor recorded
242
+ * for a live-only id would otherwise be skipped. An absent credential is not an
243
+ * error here: with no account there is nothing to list and the shipped table is
244
+ * the only answer available, which is exactly what the preview path already
245
+ * assumes. A credential that cannot be read degrades to the shipped table for
246
+ * the same reason — the floor check must not be the thing that turns a listing
247
+ * failure into a failed request.
248
+ */
249
+ private catalogForRequest;
199
250
  listModels(provider?: string): Promise<readonly LlmModelInfo[]>;
200
251
  resolveModel(provider: string, modelId: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
201
252
  prepareCall(provider: string, model: string, signal?: AbortSignal): Promise<PreparedAdapterCall>;
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../../src/host/claude/adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../../src/host/claude/adapter.ts"],"names":[],"mappings":"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"}
@@ -144,6 +144,36 @@
144
144
  * block may be replayed at all arrives at the END of the block, so a signed
145
145
  * block cannot be recognized before then, and a reasoning block that cannot be
146
146
  * replayed is not content the caller should be told it received.
147
+ *
148
+ * ---------------------------------------------------------------------------
149
+ * 7. PROMPT CACHING IS ON BY DEFAULT, AND IT IS THE REQUEST THAT TURNS IT ON
150
+ * ---------------------------------------------------------------------------
151
+ *
152
+ * Anthropic's prompt cache is REQUEST-DRIVEN: the server caches a prefix only
153
+ * where the request puts an explicit cache_control breakpoint, and a request
154
+ * with no breakpoint gets no cache_read_input_tokens back no matter how
155
+ * identical its bytes are turn over turn. Emitting the breakpoints is therefore
156
+ * this module's job, and it is the DEFAULT: ClaudeRequestOptions.cacheControl
157
+ * is true unless a caller explicitly states false. The reference implementation
158
+ * this line is transcribed from resolves the same way (its resolveCacheRetention
159
+ * defaults to 'short'; only an explicit 'none' skips caching), and the
160
+ * asymmetry is deliberate - a caller that says NOTHING must still get caching,
161
+ * because an opt-in flag that one call site forgets to pass is exactly how this
162
+ * request stopped carrying breakpoints in the first place.
163
+ *
164
+ * Three sites are marked, which is three of the four breakpoints the wire
165
+ * accepts (see MAX_CACHE_BREAKPOINTS): the LAST system block, the last block of
166
+ * the LAST user message, and the last tool. The first covers the identity block
167
+ * and the caller's prompt; the second is what actually caches CONVERSATION
168
+ * HISTORY across turns, since it sits at the end of everything the next turn
169
+ * will resend; the third keeps the tool table inside the cached prefix instead
170
+ * of behind the first message breakpoint.
171
+ *
172
+ * Where the marking happens matters as much as that it happens: the message
173
+ * breakpoint goes on the FINAL merged list, after mergeClaudeMessages has
174
+ * folded consecutive same-role turns together. Marking the pre-merge list can
175
+ * land the breakpoint on a block the merge then moves back, which leaves a
176
+ * marker in the request that caches nothing the next turn can reuse.
147
177
  */
148
178
  import { type ContentBlock, type GenerateOptions, type StreamChunk } from '../common/llm-compat.ts';
149
179
  import type { AttachmentStore } from '@deepseek-ai/dsh-attachment';
@@ -156,6 +186,15 @@ import type { AttachmentStore } from '@deepseek-ai/dsh-attachment';
156
186
  export declare const CLAUDE_CODE_IDENTITY_TEXT = "You are Claude Code, Anthropic's official CLI for Claude.";
157
187
  /** Answer room the budget clamp always leaves below the response ceiling. */
158
188
  export declare const MIN_ANSWER_TOKENS = 1024;
189
+ /**
190
+ * Cache breakpoints one Messages request may carry.
191
+ *
192
+ * TRANSCRIBED from the wire's own limit and the reference implementation's
193
+ * reading of it. This module marks at most three (see the module doc), so the
194
+ * budget is never the binding constraint - the constant exists so a test can
195
+ * assert that against the REAL number rather than a magic three.
196
+ */
197
+ export declare const MAX_CACHE_BREAKPOINTS = 4;
159
198
  /**
160
199
  * Thinking budget one level asks for, before any clamp.
161
200
  *
@@ -375,20 +414,35 @@ export declare function leadingSystemText(options: GenerateOptions): string | un
375
414
  * The system array for one request: identity first, always.
376
415
  *
377
416
  * The identity block is not conditional on anything (see the module doc). The
378
- * optional ephemeral cache marker is opt-in and goes on the LAST block, which
379
- * is the Anthropic-recommended shape: a breakpoint at the end of the prompt
380
- * caches everything above it, so marking the user block also covers the
381
- * identity block, while marking the identity block alone would cache nothing
382
- * that changes.
417
+ * ephemeral cache marker goes on the LAST block and is ON BY DEFAULT, which is
418
+ * the Anthropic-recommended shape: a breakpoint at the end of the prompt caches
419
+ * everything above it, so marking the user block also covers the identity
420
+ * block, while marking the identity block alone would cache nothing that
421
+ * changes.
422
+ *
423
+ * @param cacheControl - false is the opt-OUT; the default requests caching.
424
+ */
425
+ export declare function buildClaudeSystemBlocks(options: GenerateOptions, cacheControl?: boolean): AnthropicBlock[];
426
+ /**
427
+ * How many cache_control markers one built body carries.
428
+ *
429
+ * Walks the finished body rather than counting calls to the marker: the number
430
+ * the wire enforces is the number of markers IN THE BYTES, so a test that
431
+ * counts its own marking calls could pass while the body carried a fourth
432
+ * breakpoint from somewhere else. Used by the tests only.
383
433
  */
384
- export declare function buildClaudeSystemBlocks(options: GenerateOptions, systemCacheControl?: boolean): AnthropicBlock[];
434
+ export declare function countClaudeCacheBreakpoints(body: unknown): number;
385
435
  /** Options this builder reads beyond GenerateOptions. */
386
436
  export interface ClaudeRequestOptions {
387
437
  /**
388
- * Put an ephemeral cache breakpoint on the last system block. Default false,
389
- * so the marker is opt-in.
438
+ * Emit prompt-cache breakpoints. Default TRUE: caching is what a caller that
439
+ * states nothing gets (see the module doc). Pass false only to opt out.
440
+ *
441
+ * When on, breakpoints go on the last system block, the last block of the
442
+ * last user message, and the last tool - at most
443
+ * {@link MAX_CACHE_BREAKPOINTS} in total.
390
444
  */
391
- systemCacheControl?: boolean;
445
+ cacheControl?: boolean;
392
446
  /**
393
447
  * Tool-name translation to use. Pass the same value to createStreamState so
394
448
  * the response side maps back through the identical table.
@@ -405,7 +459,8 @@ export interface ClaudeRequestOptions {
405
459
  *
406
460
  * @param options - request options carrying the normalized conversation.
407
461
  * @param images - images read for this request by resolveRequestImages.
408
- * @param request - cache and translation options.
462
+ * @param request - cache and translation options. Caching is the DEFAULT and
463
+ * is skipped only when request.cacheControl is explicitly false.
409
464
  */
410
465
  export declare function buildClaudeRequestBody(options: GenerateOptions, images?: ResolvedRequestImages, request?: ClaudeRequestOptions): Record<string, unknown>;
411
466
  /** One tool_use block accumulating across input_json_delta fragments. */
@@ -1 +1 @@
1
- {"version":3,"file":"mapper.d.ts","sourceRoot":"","sources":["../../../../src/host/claude/mapper.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mapper.d.ts","sourceRoot":"","sources":["../../../../src/host/claude/mapper.ts"],"names":[],"mappings":"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"}
@@ -219,6 +219,14 @@
219
219
  *
220
220
  * A future reader who notices the shared `supportsTemperature: false` and
221
221
  * "fixes" item 2 to 'mid-convo' has reintroduced exactly that bug.
222
+ *
223
+ * One more field on that row comes from neither source: its `minCliVersion`
224
+ * floor is the number upstream itself states when it refuses the model
225
+ * (`claude_code_version_too_old`, "version 2.1.280 or newer is required"). That
226
+ * is a stronger witness than a documentation page — the server said it — and it
227
+ * is recorded here so a claim below it is refused locally, before the request is
228
+ * sent, instead of as an upstream 400 that names neither the model nor the number
229
+ * the user has to reach.
222
230
  */
223
231
  /** One model's shipped capability entry. */
224
232
  export interface ClaudeModelEntry {
@@ -265,6 +273,25 @@ export interface ClaudeModelEntry {
265
273
  * at all): sending `{ type: 'disabled' }` is not a supported request.
266
274
  */
267
275
  canDisableThinking: boolean;
276
+ /**
277
+ * Lowest reported client version upstream serves this model to, when known.
278
+ *
279
+ * ABSENT MEANS "NO KNOWN FLOOR", WHICH IS NOT THE SAME AS "NO FLOOR". Upstream
280
+ * enforces a minimum reported client version per model and answers a claim
281
+ * below it with HTTP 400 / `claude_code_version_too_old`
282
+ * ({@link ERROR_CODE_CLIENT_VERSION_TOO_OLD} in types.ts, whose doc comment
283
+ * states the invariant that ties this field to `CLAUDE_CLI_VERSION`). Most
284
+ * rows here predate any reason to record a number, and a floor that is guessed
285
+ * is worse than one that is absent in both directions: too low and the request
286
+ * still reaches the server and is refused there, too high and this line
287
+ * refuses a model the user could actually call. So the field is set ONLY where
288
+ * a floor has been observed, and a reader must never read its absence as
289
+ * evidence that a model accepts any client version.
290
+ *
291
+ * The value is a dotted numeric version compared by
292
+ * `compareDottedVersions` / `meetsDottedVersionFloor` in types.ts.
293
+ */
294
+ minCliVersion?: string;
268
295
  }
269
296
  /**
270
297
  * Fifteen rows: the snapshot's own 14, in the snapshot's own declaration order,
@@ -362,4 +389,20 @@ export declare function claudeThinkingMode(modelId: string, catalog?: readonly C
362
389
  export declare function claudeModelSupportsTemperature(modelId: string, catalog?: readonly ClaudeModelEntry[]): boolean;
363
390
  /** Whether thinking can be turned off for one model. */
364
391
  export declare function claudeModelCanDisableThinking(modelId: string, catalog?: readonly ClaudeModelEntry[]): boolean;
392
+ /**
393
+ * Lowest reported client version upstream serves one model to, when known.
394
+ *
395
+ * `undefined` means "no known floor" — NOT "no floor" — so a caller must treat it
396
+ * as "nothing to check" rather than as "any version is fine". The distinction
397
+ * matters because the caller is a pre-flight refusal: an absent floor must let
398
+ * the request through (refusing it would invent a restriction nobody recorded),
399
+ * while a present floor must block a claim below it.
400
+ *
401
+ * Unlike the other per-model lookups this one does NOT resolve through the
402
+ * conservative stub for an unknown id. The stub describes capabilities this line
403
+ * assumes; a version floor is a fact about the server that this line either has
404
+ * or has not observed, and manufacturing one for an unknown id would refuse a
405
+ * model nobody has evidence against.
406
+ */
407
+ export declare function claudeMinCliVersionFor(modelId: string, catalog?: readonly ClaudeModelEntry[]): string | undefined;
365
408
  //# sourceMappingURL=model-catalog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"model-catalog.d.ts","sourceRoot":"","sources":["../../../../src/host/claude/model-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6NG;AAIH,4CAA4C;AAC5C,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAA;IACV,qFAAqF;IACrF,IAAI,EAAE,MAAM,CAAA;IACZ,iFAAiF;IACjF,aAAa,EAAE,MAAM,CAAA;IACrB,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAA;IACjB,6CAA6C;IAC7C,aAAa,EAAE,OAAO,CAAA;IACtB,kDAAkD;IAClD,mBAAmB,EAAE,OAAO,CAAA;IAC5B;;;;;;;;;;;;;;OAcG;IACH,YAAY,EAAE,WAAW,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAA;IAC1D;;;;;;OAMG;IACH,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC;;;;;OAKG;IACH,kBAAkB,EAAE,OAAO,CAAA;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,gBAAgB,EAiMX,CAAA;AAE1C,+DAA+D;AAC/D,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAA2C,CAAA;AAEzF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,gBAAgB,EAAkB,CAAA;AAEzE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,yBAAyB,EAAE,SAAS,MAAM,EAMtD,CAAA;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,gBAAgB,EAAkB,GAAG,gBAAgB,CAkB1H;AAED,4EAA4E;AAC5E,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,CAEtG;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,CAEjG;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,EAAE,CAEvG;AAED,6CAA6C;AAC7C,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAAG,OAAO,CAExG;AAED,2CAA2C;AAC3C,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAAG,gBAAgB,CAAC,cAAc,CAAC,CAE3H;AAED,+CAA+C;AAC/C,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAAG,OAAO,CAE9G;AAED,wDAAwD;AACxD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAAG,OAAO,CAE7G"}
1
+ {"version":3,"file":"model-catalog.d.ts","sourceRoot":"","sources":["../../../../src/host/claude/model-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqOG;AAIH,4CAA4C;AAC5C,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAA;IACV,qFAAqF;IACrF,IAAI,EAAE,MAAM,CAAA;IACZ,iFAAiF;IACjF,aAAa,EAAE,MAAM,CAAA;IACrB,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAA;IACjB,6CAA6C;IAC7C,aAAa,EAAE,OAAO,CAAA;IACtB,kDAAkD;IAClD,mBAAmB,EAAE,OAAO,CAAA;IAC5B;;;;;;;;;;;;;;OAcG;IACH,YAAY,EAAE,WAAW,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAA;IAC1D;;;;;;OAMG;IACH,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC;;;;;OAKG;IACH,kBAAkB,EAAE,OAAO,CAAA;IAC3B;;;;;;;;;;;;;;;;;OAiBG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,gBAAgB,EA8MX,CAAA;AAE1C,+DAA+D;AAC/D,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAA2C,CAAA;AAEzF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,gBAAgB,EAAkB,CAAA;AAEzE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,yBAAyB,EAAE,SAAS,MAAM,EAMtD,CAAA;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,gBAAgB,EAAkB,GAAG,gBAAgB,CAkB1H;AAED,4EAA4E;AAC5E,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,CAEtG;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,CAEjG;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,EAAE,CAEvG;AAED,6CAA6C;AAC7C,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAAG,OAAO,CAExG;AAED,2CAA2C;AAC3C,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAAG,gBAAgB,CAAC,cAAc,CAAC,CAE3H;AAED,+CAA+C;AAC/C,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAAG,OAAO,CAE9G;AAED,wDAAwD;AACxD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAAG,OAAO,CAE7G;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,GAAG,SAAS,CAGjH"}
@@ -92,20 +92,38 @@ export declare const INTERLEAVED_THINKING_BETA = "interleaved-thinking-2025-05-1
92
92
  /** Anthropic protocol revision sent as `anthropic-version`. */
93
93
  export declare const ANTHROPIC_VERSION = "2023-06-01";
94
94
  /**
95
- * Baseline client version this route reports to the subscription gateway.
95
+ * Client version this route CLAIMS to be on the subscription gateway.
96
96
  *
97
- * Upstream enforces a *minimum reported client version* on the subscription
98
- * path: a value below the floor is refused with HTTP 400 and an error body whose
99
- * code is `claude_code_version_too_old`. That is a hard stop for every request
100
- * rather than a per-request failure, so it cannot be a compile-time literal that
101
- * only moves when this package is republished — a user whose installed baseline
102
- * has aged past the server's floor has to be able to raise it without waiting
103
- * for a release.
97
+ * Upstream enforces a *minimum reported client version* per model on the
98
+ * subscription path: the version travels in the `user-agent` header (built by
99
+ * `claudeUserAgent` in client.ts) and a claim below the floor is refused with
100
+ * HTTP 400 and an error body whose code is `claude_code_version_too_old`. That
101
+ * is a hard stop for the whole request rather than a per-request failure, and the
102
+ * number in the refusal is OUR OWN claim echoed back — so the user is told their
103
+ * client is too old without being told which model demanded more, which is
104
+ * exactly how it misleads people into signing in again.
104
105
  *
105
- * Read the effective value with {@link claudeCliVersion}, pin one with
106
- * {@link setClaudeCliVersion}.
106
+ * THE INVARIANT THIS CONSTANT CARRIES. It must be at or above the highest
107
+ * `minCliVersion` that any shipped model in `model-catalog.ts` declares. A
108
+ * default below a shipped model's floor is a model this package advertises in
109
+ * its own picker and then cannot call at all, which is the bug this comment now
110
+ * records: `2.1.251` shipped while `claude-opus-5-5` — also shipped, and the
111
+ * row the default picker leads with — requires `2.1.280`. The matching
112
+ * regression lock lives in `test/claude-model-catalog.test.ts` and fails
113
+ * loudly, naming both versions, when a future model's floor is added without
114
+ * raising this value.
115
+ *
116
+ * The value is a REAL published `@anthropic-ai/claude-code` release, not a
117
+ * number chosen merely to clear a floor: claiming a version that does not exist
118
+ * trades a clear refusal for an unpredictable one. Note that npm's `stable`
119
+ * dist-tag can itself sit below a model's floor, so `latest` is the tag to read.
120
+ *
121
+ * It is still not a literal that only moves when this package is republished: a
122
+ * user whose installed baseline has aged past the server's floor has to be able
123
+ * to raise it without waiting for a release. Read the effective value with
124
+ * {@link claudeCliVersion}, pin one with {@link setClaudeCliVersion}.
107
125
  */
108
- export declare const CLAUDE_CLI_VERSION = "2.1.251";
126
+ export declare const CLAUDE_CLI_VERSION = "2.1.283";
109
127
  /**
110
128
  * Loopback callback port the subscription flow defaults to.
111
129
  *
@@ -223,6 +241,38 @@ export declare function claudeCliVersion(): string;
223
241
  * environment and then the shipped baseline.
224
242
  */
225
243
  export declare function setClaudeCliVersion(value: string | null): void;
244
+ /**
245
+ * Compare two dotted numeric versions, e.g. `'2.1.283'` against `'2.1.280'`.
246
+ *
247
+ * This exists so a version FLOOR can be checked before a request is sent. The
248
+ * comparison is deliberately small and TOTAL rather than a SemVer parser: the
249
+ * only strings it ever sees are this line's own reported version (a pin, the
250
+ * `DSH_CLAUDE_CLI_VERSION` override, or the shipped default) and the floors
251
+ * recorded in the model catalog, and it must not throw on any of them — a
252
+ * comparison that can throw would turn a misconfigured version into a crash
253
+ * instead of the precise pre-flight refusal it was added to produce.
254
+ *
255
+ * Total means: a missing segment compares as 0 (`'2.1'` equals `'2.1.0'`), and a
256
+ * segment that is not a run of decimal digits compares as 0 rather than as NaN
257
+ * (`'abc'` equals `'0.0.0'`, and so does `''`). Leading zeros are exact rather
258
+ * than octal, and an optional leading `v` is tolerated because that is how a
259
+ * release is often written down.
260
+ *
261
+ * @returns a negative number, 0, or a positive number — the sign is the whole
262
+ * contract, so callers compare against 0 rather than against ±1.
263
+ */
264
+ export declare function compareDottedVersions(left: string, right: string): number;
265
+ /**
266
+ * Whether one reported version clears a floor.
267
+ *
268
+ * An absent floor (`undefined`) means "no known floor" rather than "no floor",
269
+ * so it never blocks a request: refusing a model because nobody recorded a
270
+ * number would be this line inventing a restriction upstream never stated.
271
+ * A MALFORMED version on either side, on the other hand, is refused — that is
272
+ * the conservative direction, since the check exists to stop an unknown claim
273
+ * from reaching a server that has already refused one.
274
+ */
275
+ export declare function meetsDottedVersionFloor(version: string, floor?: string): boolean;
226
276
  /**
227
277
  * Resolve the browser authorization endpoint.
228
278
  *
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/host/claude/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/host/claude/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eddyskywalker/dsh-chatgpt-subscription",
3
- "version": "0.9.2",
3
+ "version": "0.9.6",
4
4
  "description": "DSH provider plugin for Codex access through a ChatGPT subscription.",
5
5
  "author": {
6
6
  "name": "eddyskywalker",