Przeglądaj źródła

docs: 同步请求图片投影文档与 Agent Note

更新 attachment、attachment-local、llm-deepseek 的 README,附件与 LLM 流式子系统文档,配置目录,type-equiv 清单,以及双语配对记录;新增请求图片投影的 Agent Note,并修正 v41 计价 Note 里关于 640,000 像素预算的说明。
creatixchu 1 tydzień temu
rodzic
commit
e17a736cc3
25 zmienionych plików z 202 dodań i 41 usunięć
  1. 2 2
      .agents/notes/implemented/bug-fix/2026-09-10-deepseek-image-token-calculator-v41.i18n.yaml
  2. 1 1
      .agents/notes/implemented/bug-fix/2026-09-10-deepseek-image-token-calculator-v41.md
  3. 1 1
      .agents/notes/implemented/bug-fix/2026-09-10-deepseek-image-token-calculator-v41.zh.md
  4. 6 0
      .agents/notes/implemented/bug-fix/2026-09-10-deepseek-v41-request-image-projection.i18n.yaml
  5. 31 0
      .agents/notes/implemented/bug-fix/2026-09-10-deepseek-v41-request-image-projection.md
  6. 31 0
      .agents/notes/implemented/bug-fix/2026-09-10-deepseek-v41-request-image-projection.zh.md
  7. 2 2
      docs/config-catalog.i18n.yaml
  8. 6 2
      docs/config-catalog.md
  9. 6 2
      docs/config-catalog.zh.md
  10. 2 2
      docs/subsystems/attachment.i18n.yaml
  11. 42 3
      docs/subsystems/attachment.md
  12. 44 4
      docs/subsystems/attachment.zh.md
  13. 2 2
      docs/subsystems/llm-streaming.i18n.yaml
  14. 1 1
      docs/subsystems/llm-streaming.md
  15. 1 1
      docs/subsystems/llm-streaming.zh.md
  16. 2 2
      packages/attachment/attachment-local/README.i18n.yaml
  17. 1 1
      packages/attachment/attachment-local/README.md
  18. 1 1
      packages/attachment/attachment-local/README.zh.md
  19. 2 2
      packages/attachment/attachment/README.i18n.yaml
  20. 2 2
      packages/attachment/attachment/README.md
  21. 2 2
      packages/attachment/attachment/README.zh.md
  22. 2 2
      packages/llm/llm-deepseek/README.i18n.yaml
  23. 1 3
      packages/llm/llm-deepseek/README.md
  24. 1 3
      packages/llm/llm-deepseek/README.zh.md
  25. 10 0
      scripts/type-equiv.manifest.json

+ 2 - 2
.agents/notes/implemented/bug-fix/2026-09-10-deepseek-image-token-calculator-v41.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-09-10-deepseek-image-token-calculator-v41.md
-2026-09-10-deepseek-image-token-calculator-v41.md: d8042b04c1ed7824720485aa3ccf584f913d0726
-2026-09-10-deepseek-image-token-calculator-v41.zh.md: 6be386da1c66c469329d03e7b86c8c0e2c22ce3a
+2026-09-10-deepseek-image-token-calculator-v41.md: 4e57118987ddb7c2e2574d0dc40b02d8acf49e1a
+2026-09-10-deepseek-image-token-calculator-v41.zh.md: 209ddb99616d5d94fd4f390706304a722137bd15

+ 1 - 1
.agents/notes/implemented/bug-fix/2026-09-10-deepseek-image-token-calculator-v41.md

@@ -24,4 +24,4 @@ The test vectors are re-pinned from the published calculator. The request-pricin
 
 ## Consequences
 
-An 800×800 request image costs 422 tokens instead of 349, while a 640×480 image costs 206 instead of 209 and a low-budget 512×512 image costs 184 instead of 201. Compaction pressure changes with the retained image dimensions. The 640,000-pixel budget does not imply a 422-token ceiling: an 8192×1 image stays within that pixel budget and costs 1024 tokens. The estimate no longer carries a three-token conservative margin; provider usage remains the authoritative anchor once a request completes. Sessions replayed through `llm-replay` use their fixture's `imageRequestTokens` and are unaffected.
+An 800×800 request image costs 422 tokens instead of 349, while a 640×480 image costs 206 instead of 209 and a low-budget 512×512 image costs 184 instead of 201. Compaction pressure changes with the retained image dimensions. The request projection later moved onto the same grid ([successor](2026-09-10-deepseek-v41-request-image-projection.md)), so the 640,000-pixel budget that made a square request image cost 422 tokens no longer applies. The estimate no longer carries a three-token conservative margin; provider usage remains the authoritative anchor once a request completes. Sessions replayed through `llm-replay` use their fixture's `imageRequestTokens` and are unaffected.

+ 1 - 1
.agents/notes/implemented/bug-fix/2026-09-10-deepseek-image-token-calculator-v41.zh.md

@@ -24,4 +24,4 @@ Status: implemented
 
 ## 后果
 
-800×800 请求图片的计价从 349 变为 422 token,640×480 图片从 209 变为 206,低预算下的 512×512 图片从 201 变为 184。压缩压力随保留图片的尺寸变化。640,000 像素预算不意味着 422 token 上限:8192×1 图片在该像素预算内,仍计 1024 token。估算值不再带 3 token 的保守余量;请求完成后,提供方 usage 仍是权威锚点。经 `llm-replay` 回放的会话使用各自 fixture 的 `imageRequestTokens`,不受影响。
+800×800 请求图片的计价从 349 变为 422 token,640×480 图片从 209 变为 206,低预算下的 512×512 图片从 201 变为 184。压缩压力随保留图片的尺寸变化。请求投影后来改为同一套网格([后续决策](2026-09-10-deepseek-v41-request-image-projection.zh.md)),让正方形请求图片计 422 token 的 640,000 像素预算已不再适用。估算值不再带 3 token 的保守余量;请求完成后,提供方 usage 仍是权威锚点。经 `llm-replay` 回放的会话使用各自 fixture 的 `imageRequestTokens`,不受影响。

+ 6 - 0
.agents/notes/implemented/bug-fix/2026-09-10-deepseek-v41-request-image-projection.i18n.yaml

@@ -0,0 +1,6 @@
+# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
+# side as of the last confirmed-consistent state. Both languages carry equal authority;
+# after editing either side, bring the other along and re-record with:
+#   pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-09-10-deepseek-v41-request-image-projection.md
+2026-09-10-deepseek-v41-request-image-projection.md: 32b1eb4251ec12005879551bfad363a98034291b
+2026-09-10-deepseek-v41-request-image-projection.zh.md: 0360dcc655e1b366ca7169784b63e2650ee3dd09

+ 31 - 0
.agents/notes/implemented/bug-fix/2026-09-10-deepseek-v41-request-image-projection.md

@@ -0,0 +1,31 @@
+# Agent Note: DeepSeek request images on the published token grid
+
+Status: implemented
+
+English | [中文](2026-09-10-deepseek-v41-request-image-projection.zh.md)
+
+## Problem
+
+The harness projected every DeepSeek request image under a 640,000 total-pixel budget, a value chosen for the retired V4 vision model and kept unchanged when the [token estimator moved to the `v41` calculator](2026-09-10-deepseek-image-token-calculator-v41.md). The current Flash model retains far more: it pads each edge to whole 14px patches, groups 3×3 patches into one token cell, and keeps the largest aspect-preserving grid whose token count `rows × (columns + 1) + 2` fits 1024. A square image keeps 1302×1302 pixels, a 16:9 image keeps 1708×966, and extreme aspect ratios keep up to about 1.8 million pixels. The 640,000-pixel projection therefore sent a 2000×2000 screenshot as 800×800, roughly 38% of the pixels the model would have used, and the estimator priced that reduced version at 422 tokens instead of the 994 the model charges for the full grid. The Vision guide's "about 1300×1300 total pixels" describes only the square case; the exact rule is the token grid.
+
+Two smaller gaps sat beside it. The request version had no per-side cap: the provider rejects any image over 4096 pixels per side once a request carries 15 or more images, while normalization admits an 8192-pixel long edge, so a many-image session could fail on one thin image. The 1 MiB encoded-byte target was sized for 640,000-pixel outputs and would push a 1302×1302 photograph down the JPEG quality ladder.
+
+## Decision
+
+`ImageRequestPolicy` in `dsh-attachment` becomes a projection rule plus an optional per-side cap plus the byte target. The projection is a closed union: `pixel-budget` keeps the previous total-pixel geometry, and `token-grid` carries the patch size, downsampling ratio, and token cap of the provider grid. `request-projection.ts` owns the grid solver as `tokenGridProjection`, and `deepSeekImageTokens` in `llm-deepseek` reuses it for pricing, so the projection that sizes the request and the estimator that prices it are one solver. `requestImageDimensions` applies the projection, then the per-side cap, and keeps the source long edge exact while rounding the short edge; the local store resizes by the long edge only, so the encoded dimensions equal the predicted ones and the pricing handle text names what is actually sent. The provider pads the short edge to whole patches on its side.
+
+DeepSeek routes resolve the grid when `imagePixelBudget` is omitted, a `pixel-budget` projection for a positive integer or the 512×512 `low` preset, a 4096-pixel per-side cap on every request image so the image count never changes a projection, and a 2 MiB byte target. The pi-ai route keeps its 2048×2048 pixel budget. The request-image transform version moves to `request-image-v6`, and the cache descriptor records the projection fields in a fixed order and the per-side cap, so no earlier cache entry or upload mapping is reused. Sources within the grid are sent at their own dimensions; small images are never enlarged because the provider scales up below 544×544 pixels itself.
+
+## Alternatives considered
+
+**Raise the pixel budget to 1302×1302.** A total-pixel budget is right only for squares: a 16:9 source would be sent at 1.69 million pixels when the grid keeps 1.65 million, and a 4:1 source when it keeps 1.59 million, while extreme ratios would lose detail the grid keeps. One rule that reproduces the provider removes the guesswork.
+
+**Send the solver's exact dimensions with a fill resize.** The solved grid edges are whole patches and differ from the source aspect ratio by under one patch. Filling that box would distort the image slightly even though the provider does the same on its side; the issue requires the aspect ratio preserved, the padding on the provider side does not change the token count, and pricing reproduces the provider from the sent dimensions either way.
+
+**A function-valued projection on the policy.** The cache and upload identity is a digest over serialized policy fields, so the projection has to be data.
+
+**Keep the 1 MiB target.** The target is not a cap: an output over it is still sent at the smallest ladder quality. At 1302×1302 a JPEG photograph at quality 85 lands between 400 KB and 1.2 MB, so 2 MiB keeps most images at the top quality and lets more PNG screenshots pass through losslessly, while the inline base64 fallback still holds about seven such images under its 20 MiB bound.
+
+## Consequences
+
+A square source now reaches the model at up to 1302×1302 pixels and 994 tokens instead of 800×800 and 422, so image-heavy sessions reach compaction pressure sooner and the estimator matches provider usage for the sent version. Every existing request-image cache entry and DeepSeek Files API mapping is regenerated on the next request. Thin images keep their full grid until the per-side cap applies: an 8192×78 source costs 396 tokens under the grid but is sent as 4096×39. `llm-replay` does not project images, so keyless snapshots are unchanged, and the `llm-deepseek` adapter tests with mocked attachments pin the resolved policy and the projected handle text.

+ 31 - 0
.agents/notes/implemented/bug-fix/2026-09-10-deepseek-v41-request-image-projection.zh.md

@@ -0,0 +1,31 @@
+# Agent Note: DeepSeek 请求图片按官方 token 网格投影
+
+Status: implemented
+
+[English](2026-09-10-deepseek-v41-request-image-projection.md) | 中文
+
+## 问题
+
+harness 此前把每张 DeepSeek 请求图片投影到 640,000 总像素预算内。这个值是为已下线的 V4 视觉模型选的,[token 预估器改用 `v41` 计算器](2026-09-10-deepseek-image-token-calculator-v41.zh.md)时没有改动它。当前 Flash 模型保留的远多于此:它把每条边补齐到整数个 14 px patch,把 3×3 个 patch 归为一个 token 格,再保留 token 数 `rows × (columns + 1) + 2` 不超过 1024 的最大等比网格。正方形图片保留 1302×1302 像素,16:9 图片保留 1708×966,极端宽高比最多保留约 180 万像素。因此 640,000 像素投影把一张 2000×2000 的截图缩成 800×800 发出,只有模型本可使用像素的约 38%,预估器为这个缩小版计 422 token,而模型对完整网格收 994 token。图像理解指南里的「约 1300×1300 总像素」只描述正方形的情况,确切规则是 token 网格。
+
+旁边还有两个较小的缺口。请求版本没有单边上限:请求包含 15 张及以上图片时,提供方拒绝任何单边超过 4096 像素的图片,而规范化允许 8192 像素长边,多图会话可能因一张细长图失败。1 MiB 编码字节目标是按 640,000 像素输出定的,会把 1302×1302 的照片压到 JPEG 质量阶梯的低档。
+
+## 决策
+
+`dsh-attachment` 里的 `ImageRequestPolicy` 改为投影规则加可选单边上限加字节目标。投影是封闭联合类型:`pixel-budget` 保留原来的总像素几何,`token-grid` 携带提供方网格的 patch 大小、降采样比和 token 上限。`request-projection.ts` 以 `tokenGridProjection` 承载网格求解器,`llm-deepseek` 的 `deepSeekImageTokens` 复用它计价,于是决定请求尺寸的投影和为它计价的预估器是同一个求解器。`requestImageDimensions` 先应用投影,再应用单边上限,保持源图长边精确、短边四舍五入;本地存储只按长边缩放,因此编码后的尺寸等于预测尺寸,计价用的句柄文本写的就是实际发送的尺寸。提供方在自己那侧把短边补齐到整数个 patch。
+
+DeepSeek 路由在省略 `imagePixelBudget` 时解析为网格,正整数或 512×512 的 `low` 预设解析为 `pixel-budget` 投影,每张请求图片都加 4096 像素单边上限,使图片数量不会改变投影,字节目标为 2 MiB。pi-ai 路由保留 2048×2048 像素预算。请求图片变换版本升到 `request-image-v6`,缓存描述符按固定顺序记录投影字段和单边上限,因此之前的缓存条目和上传映射都不会被复用。网格内的源图按自身尺寸发送;小图不放大,因为提供方自己会放大 544×544 像素以下的图片。
+
+## 备选方案
+
+**把像素预算提高到 1302×1302。** 总像素预算只对正方形正确:16:9 的源图会按 169 万像素发送而网格只保留 165 万,4:1 的源图网格只保留 159 万,极端比例又会丢掉网格本会保留的细节。一条复现提供方的规则消除了猜测。
+
+**用填充缩放发送求解器的精确尺寸。** 求解出的网格边长是整数个 patch,与源图宽高比相差不到一个 patch。填充到这个框会轻微变形,尽管提供方那侧也会这样做;issue 要求保持宽高比,提供方那侧的补齐不改变 token 数,而计价无论如何都从发送尺寸复现提供方。
+
+**策略里放函数形式的投影。** 缓存和上传身份是序列化策略字段的摘要,投影必须是数据。
+
+**保留 1 MiB 目标。** 目标不是上限:超过它的输出仍会以阶梯最小质量发送。1302×1302 的 JPEG 照片在质量 85 时约 400 KB 到 1.2 MB,2 MiB 让多数图片停在最高质量,也让更多 PNG 截图无损直发,而内联 base64 回退在 20 MiB 上界内仍能容纳约七张这样的图片。
+
+## 后果
+
+正方形源图现在最多以 1302×1302 像素、994 token 到达模型,而不是 800×800 和 422,因此图片密集的会话更早触及 compaction 压力,预估器对发送版本的计价与提供方 usage 一致。所有已有的请求图片缓存条目和 DeepSeek Files API 映射在下次请求时重新生成。细长图在单边上限生效前保留完整网格:8192×78 的源图在网格下计 396 token,但以 4096×39 发送。`llm-replay` 不投影图片,keyless 快照不变;`llm-deepseek` 适配器用 mock 附件的测试固定了解析出的策略和投影后的句柄文本。

+ 2 - 2
docs/config-catalog.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write docs/config-catalog.md
-config-catalog.md: 7c975892a0a5f04a80dd13186ef2b3732b419400
-config-catalog.zh.md: 57dac94311814e87a2bf7385c404811a910d3d4b
+config-catalog.md: 259cbe8afe509cf448a54fa9fcddf297d4f2f588
+config-catalog.zh.md: 49e1064239f20fc6236c3dde6f2698791a9a1183

+ 6 - 2
docs/config-catalog.md

@@ -1079,7 +1079,11 @@ export interface DeepSeekCatalogModel {
   maxTokens?: number
   /** Accepted request modalities; omission is text-only. */
   inputModalities?: ModelModality[]
-  /** Total-pixel budget for one deterministic request preview, or the 512-by-512 `low` preset. */
+  /**
+   * Total-pixel budget replacing the published token-grid projection for one
+   * deterministic request preview, or the 512-by-512 `low` preset; omission
+   * projects onto the token grid.
+   */
   imagePixelBudget?: number | 'low'
   /** Encoded-byte target for one deterministic request preview; the smallest quality-ladder output is used when no quality fits. */
   imageMaxBytes?: number
@@ -1094,7 +1098,7 @@ export interface DeepSeekCatalogModel {
 
 Depends on: [`ModelModality`](../packages/llm/llm/src/index.ts) · [`RetryPolicyConfig`](../packages/llm/llm/src/index.ts) · [`SystemPromptUpdate`](../packages/llm/llm/src/index.ts)
 
-Source: [`packages/llm/llm-deepseek/src/index.ts:134`](../packages/llm/llm-deepseek/src/index.ts)
+Source: [`packages/llm/llm-deepseek/src/index.ts:128`](../packages/llm/llm-deepseek/src/index.ts)
 
 <a id="deepseek-aidsh-llm-pi-ai"></a>
 

+ 6 - 2
docs/config-catalog.zh.md

@@ -1081,7 +1081,11 @@ export interface DeepSeekCatalogModel {
   maxTokens?: number
   /** Accepted request modalities; omission is text-only. */
   inputModalities?: ModelModality[]
-  /** Total-pixel budget for one deterministic request preview, or the 512-by-512 `low` preset. */
+  /**
+   * Total-pixel budget replacing the published token-grid projection for one
+   * deterministic request preview, or the 512-by-512 `low` preset; omission
+   * projects onto the token grid.
+   */
   imagePixelBudget?: number | 'low'
   /** Encoded-byte target for one deterministic request preview; the smallest quality-ladder output is used when no quality fits. */
   imageMaxBytes?: number
@@ -1096,7 +1100,7 @@ export interface DeepSeekCatalogModel {
 
 依赖:[`ModelModality`](../packages/llm/llm/src/index.ts) · [`RetryPolicyConfig`](../packages/llm/llm/src/index.ts) · [`SystemPromptUpdate`](../packages/llm/llm/src/index.ts)
 
-来源:[`packages/llm/llm-deepseek/src/index.ts:134`](../packages/llm/llm-deepseek/src/index.ts)
+来源:[`packages/llm/llm-deepseek/src/index.ts:128`](../packages/llm/llm-deepseek/src/index.ts)
 
 <a id="deepseek-aidsh-llm-pi-ai"></a>
 

+ 2 - 2
docs/subsystems/attachment.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write docs/subsystems/attachment.md
-attachment.md: 53b3e51e5b1c87625256178287061386342bc055
-attachment.zh.md: 549c06cc5abe542e5d6feeba10b24ebf6ba8dfd1
+attachment.md: bbcf438646eb4cae78ef5b86ccbf9e04b927fa55
+attachment.zh.md: 0e3c58fc59cb319bde681bdfa7124c852cb6da19

Plik diff jest za duży
+ 42 - 3
docs/subsystems/attachment.md


+ 44 - 4
docs/subsystems/attachment.zh.md

@@ -125,11 +125,51 @@ interface StoredImageAttachment {
 }
 ```
 
+```ts type-equiv
+/** Aspect-preserving downscale rule of one request image; small images are never enlarged. */
+type ImageRequestProjection =
+  | {
+    /** Hard cap on width multiplied by height. */
+    kind: 'pixel-budget'
+    /** Maximum width multiplied by height after projection. */
+    maxPixels: number
+  }
+  | {
+    /**
+     * Largest aspect-preserving patch grid whose token count
+     * `rows × (columns + 1) + 2` fits `maxTokens`; the DeepSeek published vision layout.
+     */
+    kind: 'token-grid'
+    /** Patch edge in pixels; a downscaled edge is a whole number of patches. */
+    patchSize: number
+    /** Patches per token cell along each axis. */
+    downsampleRatio: number
+    /** Token cap for one image. */
+    maxTokens: number
+  }
+```
+
+```ts type-equiv
+/** Dimensions a `token-grid` projection retains for one image and the tokens it charges. */
+interface TokenGridProjection {
+  /** Retained width: the patch-padded source when it fits, otherwise the solved width. */
+  width: number
+  /** Retained height: the patch-padded source when it fits, otherwise the solved height. */
+  height: number
+  /** Tokens charged for the retained grid. */
+  tokens: number
+  /** Whether the patch-padded source already fits `maxTokens` without downscaling. */
+  unscaled: boolean
+}
+```
+
 ```ts type-equiv
 /** Deterministic request-image policy selected by one exact model route. */
 interface ImageRequestPolicy {
-  /** Maximum width multiplied by height after aspect-preserving projection. */
-  maxPixels: number
+  /** Downscale rule applied before the per-side cap. */
+  projection: ImageRequestProjection
+  /** Maximum width and maximum height after projection; omission bounds the long edge by the projection alone. */
+  maxDimension?: number
   /** Encoded-byte target before base64 expansion or Files API upload; the smallest quality-ladder output is kept when no quality fits. */
   maxBytes: number
 }
@@ -157,7 +197,7 @@ interface RequestImageAttachment {
 }
 ```
 
-`saveImage()` 准备并原子提交提供方无关的规范化附件,然后直接返回 `ImageAttachmentRef`。`saveImages()` 在发布批次前为每个成员各准备一次经过验证的附件,因此校验拒绝不会留下部分对象,发布也不会重复解码或选择质量。`admitPromptContent()` 在文件凭证解析后接收完整且有序的 Host prompt,把 base64 图片上传替换为持久引用,并让持久文件引用原样通过。`admitEncodedImages()` 支持其他 wire 入口,把张数、聚合字节和有序批量准入交给 `saveImages()`。`admitEncodedFile()` 让编码协议适配器使用服务拥有的规范 base64 准入,`isAttachmentError()` 让这些适配器无需导入实现辅助函数即可识别稳定的附件错误。`readImage()` 校验来自已授权会话路径的规范化附件。`imageHostPath()` 只公开提供方所持对象的宿主位置,不判断当前工具执行环境能否读取它。`readImageRequest()` 按确切路由的像素和字节预算派生并缓存确定性请求版本。该版本包含编码字节和元数据,不包含执行环境路径。新条目在发布前完整解码,缓存命中只做有界元数据探测。调用方需要有序批次时,对单数方法使用 `Promise.all`。本地实现按需编码首选候选、合并相同请求身份的并发任务、允许每个等待方单独取消、没有等待方时停止共享任务,并通过实例级限流器限制全部变换,默认同时执行两项。该服务不规定保留策略:恢复和 fork 后的会话可能共享对象,因此基于引用的垃圾回收会延期实现,不与单个会话的删除绑定。
+`saveImage()` 准备并原子提交提供方无关的规范化附件,然后直接返回 `ImageAttachmentRef`。`saveImages()` 在发布批次前为每个成员各准备一次经过验证的附件,因此校验拒绝不会留下部分对象,发布也不会重复解码或选择质量。`admitPromptContent()` 在文件凭证解析后接收完整且有序的 Host prompt,把 base64 图片上传替换为持久引用,并让持久文件引用原样通过。`admitEncodedImages()` 支持其他 wire 入口,把张数、聚合字节和有序批量准入交给 `saveImages()`。`admitEncodedFile()` 让编码协议适配器使用服务拥有的规范 base64 准入,`isAttachmentError()` 让这些适配器无需导入实现辅助函数即可识别稳定的附件错误。`readImage()` 校验来自已授权会话路径的规范化附件。`imageHostPath()` 只公开提供方所持对象的宿主位置,不判断当前工具执行环境能否读取它。`readImageRequest()` 按确切路由的投影规则、可选单边上限和编码字节目标派生并缓存确定性请求版本。该版本包含编码字节和元数据,不包含执行环境路径。新条目在发布前完整解码,缓存命中只做有界元数据探测。调用方需要有序批次时,对单数方法使用 `Promise.all`。本地实现按需编码首选候选、合并相同请求身份的并发任务、允许每个等待方单独取消、没有等待方时停止共享任务,并通过实例级限流器限制全部变换,默认同时执行两项。该服务不规定保留策略:恢复和 fork 后的会话可能共享对象,因此基于引用的垃圾回收会延期实现,不与单个会话的删除绑定。
 
 <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
 
@@ -280,7 +320,7 @@ fileHostPath(ref: FileAttachmentRef): string | undefined
 /**
  * Generate or read one deterministic model-request version from the stored normalized image.
  * @param ref - durable provider-independent normalized attachment reference.
- * @param policy - exact route pixel budget and encoded-byte target; a target no ladder quality meets yields the smallest ladder output.
+ * @param policy - route projection, optional per-side cap, and byte target; an unmet target yields the smallest ladder output.
  * @param signal - optional cancellation.
  * @returns request bytes and the cache/upload identity covering every transform input.
  */

+ 2 - 2
docs/subsystems/llm-streaming.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write docs/subsystems/llm-streaming.md
-llm-streaming.md: 77d3313b3c1a025f756e984453bd041abd7b0534
-llm-streaming.zh.md: cac7897ab412f45bd5b5ab20c6460644f18f4ff2
+llm-streaming.md: b3d2468a41833139e3c9c9ded69cbb863e5fd8d3
+llm-streaming.zh.md: 2d849206746ee44736ff402a364f913301ed87a8

+ 1 - 1
docs/subsystems/llm-streaming.md

@@ -249,7 +249,7 @@ interface LlmFailure {
 
 ## Request-image pricing
 
-An adapter whose provider charges visual tokens for request images declares per-route pricing by overriding `LlmAdapter.imageRequestPricing`, and `ctx.llm.imageRequestPricing(provider, model)` resolves it synchronously for consumers. The token meter resolves the routed model's pricing on every measurement so compaction pressure, retention, and range selection price image history as the routed request actually sends it; the DeepSeek adapter reproduces its own request projection (per-model pixel budget, oldest-first offload) and prices retained images with the published vision accounting, while provider usage remains the authoritative anchor for completed requests.
+An adapter whose provider charges visual tokens for request images declares per-route pricing by overriding `LlmAdapter.imageRequestPricing`, and `ctx.llm.imageRequestPricing(provider, model)` resolves it synchronously for consumers. The token meter resolves the routed model's pricing on every measurement so compaction pressure, retention, and range selection price image history as the routed request actually sends it; the DeepSeek adapter reproduces its own request projection (per-model projection and per-side cap, oldest-first offload) and prices retained images with the published vision accounting, while provider usage remains the authoritative anchor for completed requests.
 
 ```ts type-equiv
 /**

+ 1 - 1
docs/subsystems/llm-streaming.zh.md

@@ -251,7 +251,7 @@ interface LlmFailure {
 
 ## 请求图片定价
 
-提供方对请求图片收取视觉 token 的适配器通过覆写 `LlmAdapter.imageRequestPricing` 声明按路由的定价,消费方经 `ctx.llm.imageRequestPricing(provider, model)` 同步解析。token 计量服务在每次计量时解析路由模型的定价,使 compaction 的压力、保留与选段都按路由请求实际发送的形式为图片历史计价;DeepSeek 适配器复现自身的请求投影(按模型的像素预算、最旧优先 offload),并用官方公布的视觉计量为保留图片定价,已完成请求仍以 provider usage 为权威锚点。
+提供方对请求图片收取视觉 token 的适配器通过覆写 `LlmAdapter.imageRequestPricing` 声明按路由的定价,消费方经 `ctx.llm.imageRequestPricing(provider, model)` 同步解析。token 计量服务在每次计量时解析路由模型的定价,使 compaction 的压力、保留与选段都按路由请求实际发送的形式为图片历史计价;DeepSeek 适配器复现自身的请求投影(按模型的投影与单边上限、最旧优先 offload),并用官方公布的视觉计量为保留图片定价,已完成请求仍以 provider usage 为权威锚点。
 
 ```ts type-equiv
 /**

+ 2 - 2
packages/attachment/attachment-local/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/attachment/attachment-local/README.md
-README.md: a14490f855268cdaa65bc0d3d8968ece56d25bf3
-README.zh.md: 44e8205dfdc7debd625c1ad4819f56bcfbe94728
+README.md: 3689ee4d497cf2e6dbf74450a9ec71c5138170c3
+README.zh.md: 3c208d1626daa1d93c6e1cc6bd483ded21338f75

+ 1 - 1
packages/attachment/attachment-local/README.md

@@ -85,7 +85,7 @@ Objects land at `<DSH_HOME>/attachments/v1/objects/<sha256-prefix>/<sha256>`; eq
 
 Admission accepts up to 20 images and 200 MiB of source bytes per message; one source may use up to 20 MiB, 64 million pixels, and 8192 pixels per side. It applies orientation, removes metadata and color profiles, and normalizes under a 2048×2048 total-pixel budget, an 8192-pixel long edge, and a 4 MiB encoded-byte target. Extreme aspect ratios therefore retain their short-edge resolution. Clean single-frame 8-bit sRGB/sRGBA PNG, JPEG, or WebP input already within those limits passes through byte-identically; GIF, animation, metadata, orientation, 16-bit PNG, and incompatible color spaces force conversion.
 
-Request versions live below `<DSH_HOME>/attachments/v1/request-images/`. `readImageRequest` scales without enlargement to a route pixel budget, then applies a separate encoded-byte target through the same alpha routing and quality ladder. Its cache identity includes the attachment id, transform version, budgets, and fixed encoder settings; cached bytes are header-probed for format, 8-bit sRGB/sRGBA, dimensions, and alpha facts, and a mismatch regenerates the entry. Concurrent callers share one transform and cache write, while cancellation stops shared work only when no waiter remains. `imageHostPath` derives the normalized object's host path, and the mounted filesystem may map that path into its execution world without writing it to durable history.
+Request versions live below `<DSH_HOME>/attachments/v1/request-images/`. `readImageRequest` scales without enlargement to the route projection, a total-pixel budget or a provider token grid, then to the route per-side cap; it resizes by the long edge only, so the encoder rounds the short edge exactly as the projection predicts, and then applies a separate encoded-byte target through the same alpha routing and quality ladder. Its cache identity includes the attachment id, transform version, projection, per-side cap, byte target, and fixed encoder settings; cached bytes are header-probed for format, 8-bit sRGB/sRGBA, dimensions, and alpha facts, and a mismatch regenerates the entry. Concurrent callers share one transform and cache write, while cancellation stops shared work only when no waiter remains. `imageHostPath` derives the normalized object's host path, and the mounted filesystem may map that path into its execution world without writing it to durable history.
 
 Generic-file bytes have one canonical object at `<DSH_HOME>/attachments/v1/file-objects/<digest-prefix>/<digest>`. Each reference path at `<DSH_HOME>/attachments/v1/files/<digest-prefix>/<digest>/<name>` is a read-only hard link, so different names for equal bytes do not duplicate disk content. `readFileStream` reads the reference path in bounded chunks and verifies the complete digest and recorded byte count before a consumer can finish successfully. A missing, changed, or truncated object fails its consumer instead of producing a complete export with different bytes.
 

+ 1 - 1
packages/attachment/attachment-local/README.zh.md

@@ -85,7 +85,7 @@ kind: "package-reference"
 
 准入允许每条消息最多 20 张图片与 200 MiB 源字节;单个源图最多 20 MiB、6400 万像素与单边 8192 像素。系统应用方向、移除元数据与色彩配置,并把规范化结果限制在 2048×2048 总像素预算、8192 像素长边和 4 MiB 编码字节目标内,因此,即使宽高比极端,图片也会保留短边分辨率。已经满足限制的干净、单帧、8-bit sRGB/sRGBA PNG、JPEG 或 WebP 会逐字节直通;GIF、动画、元数据、方向、16-bit PNG 与不兼容色彩空间会触发转换。
 
-请求版本位于 `<DSH_HOME>/attachments/v1/request-images/`。`readImageRequest` 在不放大的前提下缩放到路由像素预算,再通过相同的 alpha 路由与质量阶梯应用独立编码字节目标。缓存身份包含附件 id、变换版本、预算与固定编码参数;缓存字节会先通过文件头探测格式、8-bit sRGB/sRGBA、尺寸与 alpha 信息,不匹配时重新生成。并发调用方共享一次变换与缓存写入,且只在没有等待方时由取消停止共享工作。`imageHostPath` 派生规范化对象的宿主路径,挂载的文件系统可以把该路径映射进执行世界,而不会写入持久历史。
+请求版本位于 `<DSH_HOME>/attachments/v1/request-images/`。`readImageRequest` 在不放大的前提下缩放到路由投影,即总像素预算或提供方 token 网格,再缩放到路由单边上限。缩放只按长边给定,短边由编码器四舍五入,与投影预测的尺寸一致。随后通过相同的 alpha 路由与质量阶梯应用独立编码字节目标。缓存身份包含附件 id、变换版本、投影、单边上限、字节目标与固定编码参数;缓存字节会先通过文件头探测格式、8-bit sRGB/sRGBA、尺寸与 alpha 信息,不匹配时重新生成。并发调用方共享一次变换与缓存写入,且只在没有等待方时由取消停止共享工作。`imageHostPath` 派生规范化对象的宿主路径,挂载的文件系统可以把该路径映射进执行世界,而不会写入持久历史。
 
 通用文件字节的唯一规范对象位于 `<DSH_HOME>/attachments/v1/file-objects/<digest-prefix>/<digest>`。每条引用路径 `<DSH_HOME>/attachments/v1/files/<digest-prefix>/<digest>/<name>` 都是只读硬链接,所以名称不同但字节相同的文件不会重复占用磁盘。`readFileStream` 以有界分块读取引用路径,并在消费方成功结束前校验完整摘要与记录的字节数。对象缺失、被改写或截断时,消费方会失败,不会得到字节已经变化的完整导出。
 

+ 2 - 2
packages/attachment/attachment/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/attachment/attachment/README.md
-README.md: 35fde71833fa13b26662244cfa68cec17b90b663
-README.zh.md: b0e98d1e4a0c35082a8eef23f69659f47916d3f7
+README.md: e17d6bea53127d54babad919bdd546388b742d5f
+README.zh.md: 5bf061304684cbdd8be00709ba9d1224b6c68ffa

+ 2 - 2
packages/attachment/attachment/README.md

@@ -66,13 +66,13 @@ This section explains the design decisions behind the seam and the service opera
 - **Normalize and persist before event.** Every source is prepared and verified before the batch publishes in order, so the session log never references a partial or failed normalization.
 - **Immutable and retention-neutral.** Objects are immutable once published; resumed and forked sessions may share them, so reference-aware garbage collection is deferred rather than tied to any one session's deletion.
 - **Verify on read.** Reads check bytes and metadata against the logged reference before returning them, and request projections fully decode cached bytes, so a missing, corrupted, or swapped object fails closed.
-- **Role-neutral image blocks.** The `ImageBlock` content block in `dsh-llm` carries an `ImageAttachmentRef`; provider adapters resolve it into deterministic request versions with explicit pixel and byte budgets, while execution filesystems may map the immutable host object to a model-readable process path.
+- **Role-neutral image blocks.** The `ImageBlock` content block in `dsh-llm` carries an `ImageAttachmentRef`; provider adapters resolve it into deterministic request versions under an explicit projection rule, per-side cap, and byte target, while execution filesystems may map the immutable host object to a model-readable process path.
 - **Error routing by code.** `AttachmentError` re-implements the `HarnessError` shape instead of extending it because the base lives in `dsh-llm`, which depends on this package; consumers use `isAttachmentError` and route on `code`, never on the prototype chain.
 - **Files are verbatim, images are normalized.** `saveFile` commits an existing byte array, `saveFileStream` commits bounded chunks with backpressure and cancellation, `readFileStream` verifies and returns bounded chunks, and `fileHostPath` locates the stored object for read-on-demand projection; neither file write path applies admission limits. The image path keeps its separate normalization, limits, and request-version pipeline. The `FileBlock` content block in `dsh-llm` carries a `FileAttachmentRef`, and request assembly projects it to deterministic handle text for every route.
 
 ### Service operations
 
-The service family runs one admission-and-storage flow: every entry point enforces source batch limits and canonical base64, prepares provider-independent normalized attachments before publishing any member, and commits them durably in input order without partial results. Host prompt consumers pass ordered text, encoded images, and already resolved file references to `ctx.attachments.admitPromptContent()`; the method persists images and passes file references unchanged. Encoded protocol adapters call `ctx.attachments.admitEncodedFile()`, which checks canonical base64 before delegating to `saveFile`; adapters recognize attachment failures through `ctx.attachments.isAttachmentError()`. Generic-file callers choose `saveFile` for existing bytes or `saveFileStream` for a bounded asynchronous byte source; both return the same durable reference, while `readFileStream` verifies its digest and length during a bounded read. `readImageRequest` derives deterministic route-sized variants whose identity includes the attachment id, transform version, pixel and byte budgets, and encoder settings. The pure `requestImageDimensions` export computes each projection's aspect-preserving dimensions from a total-pixel budget, so providers and request pricing share one geometry. `imageHostPath` exposes an implementation-owned host location only to trusted same-process consumers that need execution-world mapping. Callers compose ordered batches while the implementation owns compression concurrency, caching, and singleflight. Reads, streamed writes, and projections preserve caller cancellation. Failures carry stable machine-readable codes, and the caller-correctable admission subset is recognizable at runtime so each protocol adapter maps its own vocabulary; the exact per-operation contracts live in [`src/index.ts`](src/index.ts) and [`src/error.ts`](src/error.ts).
+The service family runs one admission-and-storage flow: every entry point enforces source batch limits and canonical base64, prepares provider-independent normalized attachments before publishing any member, and commits them durably in input order without partial results. Host prompt consumers pass ordered text, encoded images, and already resolved file references to `ctx.attachments.admitPromptContent()`; the method persists images and passes file references unchanged. Encoded protocol adapters call `ctx.attachments.admitEncodedFile()`, which checks canonical base64 before delegating to `saveFile`; adapters recognize attachment failures through `ctx.attachments.isAttachmentError()`. Generic-file callers choose `saveFile` for existing bytes or `saveFileStream` for a bounded asynchronous byte source; both return the same durable reference, while `readFileStream` verifies its digest and length during a bounded read. `readImageRequest` derives deterministic route-sized variants whose identity includes the attachment id, transform version, projection rule, per-side cap, byte target, and encoder settings. The pure `requestImageDimensions` and `tokenGridProjection` exports compute each projection's aspect-preserving dimensions from a total-pixel budget or a provider token grid, so providers and request pricing share one geometry. `imageHostPath` exposes an implementation-owned host location only to trusted same-process consumers that need execution-world mapping. Callers compose ordered batches while the implementation owns compression concurrency, caching, and singleflight. Reads, streamed writes, and projections preserve caller cancellation. Failures carry stable machine-readable codes, and the caller-correctable admission subset is recognizable at runtime so each protocol adapter maps its own vocabulary; the exact per-operation contracts live in [`src/index.ts`](src/index.ts) and [`src/error.ts`](src/error.ts).
 
 ### Source map
 

+ 2 - 2
packages/attachment/attachment/README.zh.md

@@ -66,13 +66,13 @@ kind: "package-reference"
 - **事件前完成规范化与持久化。** 每个源图都会在批次按序发布前完成准备与校验,因此会话日志绝不会引用部分完成或规范化失败的对象。
 - **不可变且保留策略中立。** 对象一经发布即不可变;恢复和 fork 后的会话可能共享它们,因此引用感知的垃圾回收被推迟,而不是与任何单个会话的删除绑定。
 - **读取时校验。** 读取在返回前把字节和元数据与记录的引用比对,请求投影还会完整解码缓存字节,因此缺失、损坏或被替换的对象不会通过校验。
-- **角色无关的图片块。** `dsh-llm` 中的 `ImageBlock` 内容块携带 `ImageAttachmentRef`;提供方适配器以显式像素与字节预算把引用解析为确定性请求版本,执行文件系统则可以把不可变宿主对象映射为模型可读的进程路径。
+- **角色无关的图片块。** `dsh-llm` 中的 `ImageBlock` 内容块携带 `ImageAttachmentRef`;提供方适配器按显式的投影规则、单边上限与字节目标把引用解析为确定性请求版本,执行文件系统则可以把不可变宿主对象映射为模型可读的进程路径。
 - **按错误码路由。** `AttachmentError` 重新实现 `HarnessError` 的结构而不是继承它,因为基类位于 `dsh-llm`,而后者依赖本包;消费方用 `isAttachmentError` 识别错误并按 `code` 路由,绝不依赖原型链。
 - **文件原样,图片规范化。**`saveFile` 提交已有字节数组,`saveFileStream` 以背压和取消语义提交有界分块,`readFileStream` 校验并返回有界分块,`fileHostPath` 定位存储对象供按需读取投影;两种文件写入路径都不设准入限制。图片路径保留其独立的规范化、限额与请求版本流水线。`dsh-llm` 中的 `FileBlock` 内容块承载 `FileAttachmentRef`,请求组装会为每条路由将其投影为确定性的句柄文本。
 
 ### 服务操作
 
-服务族运行同一条准入与存储流程:每个入口都强制执行源批次限制与规范 base64,在发布任何成员前准备提供方无关的规范化附件,再按输入顺序持久提交而不产生部分结果。Host 提示词消费方把有序文本、编码图片和已经解析的文件引用交给 `ctx.attachments.admitPromptContent()`;该方法持久化图片,并让文件引用原样通过。编码协议适配器调用 `ctx.attachments.admitEncodedFile()`,由该方法检查规范 base64 后委托给 `saveFile`;适配器通过 `ctx.attachments.isAttachmentError()` 识别附件错误。通用文件调用方可以用 `saveFile` 提交已有字节,或用 `saveFileStream` 提交有界异步字节源;两者返回相同的持久引用,`readFileStream` 则在有界读取过程中校验摘要与长度。`readImageRequest` 派生确定性的路由尺寸变体,其身份包含附件 id、变换版本、像素与字节预算及编码参数。纯函数导出 `requestImageDimensions` 会按总像素预算计算每个投影保持宽高比的尺寸,使提供方与请求定价共享同一套几何计算。`imageHostPath` 只向需要把该位置映射到执行环境的受信任同进程消费方暴露实现拥有的宿主位置。调用方组合有序批次,而实现负责管理压缩并发、缓存与 singleflight。读取、流式写入和投影保留调用方的取消语义。失败带有稳定且机器可读的错误码,运行时即可识别可由调用方修正的准入子集,让每个协议适配器映射自己的词汇;各操作的确切约定见 [`src/index.ts`](src/index.ts) 与 [`src/error.ts`](src/error.ts)。
+服务族运行同一条准入与存储流程:每个入口都强制执行源批次限制与规范 base64,在发布任何成员前准备提供方无关的规范化附件,再按输入顺序持久提交而不产生部分结果。Host 提示词消费方把有序文本、编码图片和已经解析的文件引用交给 `ctx.attachments.admitPromptContent()`;该方法持久化图片,并让文件引用原样通过。编码协议适配器调用 `ctx.attachments.admitEncodedFile()`,由该方法检查规范 base64 后委托给 `saveFile`;适配器通过 `ctx.attachments.isAttachmentError()` 识别附件错误。通用文件调用方可以用 `saveFile` 提交已有字节,或用 `saveFileStream` 提交有界异步字节源;两者返回相同的持久引用,`readFileStream` 则在有界读取过程中校验摘要与长度。`readImageRequest` 派生确定性的路由尺寸变体,其身份包含附件 id、变换版本、投影规则、单边上限、字节目标及编码参数。纯函数导出 `requestImageDimensions` 与 `tokenGridProjection` 按总像素预算或提供方 token 网格计算每个投影保持宽高比的尺寸,使提供方与请求定价共享同一套几何计算。`imageHostPath` 只向需要把该位置映射到执行环境的受信任同进程消费方暴露实现拥有的宿主位置。调用方组合有序批次,而实现负责管理压缩并发、缓存与 singleflight。读取、流式写入和投影保留调用方的取消语义。失败带有稳定且机器可读的错误码,运行时即可识别可由调用方修正的准入子集,让每个协议适配器映射自己的词汇;各操作的确切约定见 [`src/index.ts`](src/index.ts) 与 [`src/error.ts`](src/error.ts)。
 
 ### 源码地图
 

+ 2 - 2
packages/llm/llm-deepseek/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/llm/llm-deepseek/README.md
-README.md: 41646097f80dfad9cc523595b9d2820c28fde66b
-README.zh.md: c7a1e40952dff7a804880dcd266114cecb20918f
+README.md: c99431b0d415ef82d4833d7ca38b531b3cd38937
+README.zh.md: 1d515c5ee8a855981e529b7fff03cbf18fa9dde7

+ 1 - 3
packages/llm/llm-deepseek/README.md

@@ -74,7 +74,7 @@ The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-a
 
 ### Streaming with thinking and images
 
-An image-capable route resolves each durable reference into a deterministic request version under its pixel and byte budgets. `imagePixelBudget` accepts a positive integer or `low`; omission uses 640,000 total pixels, `low` uses 512×512 total pixels, and `imageMaxBytes` defaults to 1 MiB. Alpha images use WebP effort 0 and opaque images use JPEG on the 85/75/60 quality ladder, keeping the smallest output when every candidate exceeds the target. Every retained image is preceded by text naming its complete attachment id and actual request dimensions. When the current filesystem maps the attachment provider's host object, that text also carries a read-only execution-world path and the extension for a writable copy. Text-only and unlisted routes receive stable attachment placeholders while durable history keeps the image references.
+An image-capable route resolves each durable reference into a deterministic request version under its projection, per-side cap, and byte target. Omitting `imagePixelBudget` projects onto the published vision token grid of 14px patches, 3:1 downsampling, and at most 1024 tokens per image, so a square image keeps up to 1302×1302 pixels and other aspect ratios keep the largest grid inside the cap; a positive integer replaces the grid with a total-pixel budget, and `low` uses 512×512 total pixels. Every request image is capped at 4096 pixels per side, the provider limit for requests carrying 15 or more images, and `imageMaxBytes` defaults to 2 MiB. Alpha images use WebP effort 0 and opaque images use JPEG on the 85/75/60 quality ladder, keeping the smallest output when every candidate exceeds the target. Every retained image is preceded by text naming its complete attachment id and actual request dimensions. When the current filesystem maps the attachment provider's host object, that text also carries a read-only execution-world path and the extension for a writable copy. Text-only and unlisted routes receive stable attachment placeholders while durable history keeps the image references.
 
 The adapter normally uploads those exact request bytes through the DeepSeek Files API and sends file-id blocks. A failed or timed-out file resolution rebuilds the whole chat request with the same request versions as base64 data URLs; one request never mixes file ids and inline images. Cached ids are scoped by endpoint and API key, refreshed before expiry, invalidated from provider stale-file errors, and resolved through singleflight with waiter-local cancellation. Quota failure deletes one configured batch of the oldest harness-owned files before one upload retry.
 
@@ -190,8 +190,6 @@ These limits define where the adapter stops and future work begins. They are cur
 - **Images are input-only durable attachments** — direct external URLs and assistant image output are not supported; DeepSeek input normally uses the Files API and uses inline base64 only for per-request recovery.
 - The default catalog pre-registers `deepseek-flash` and its text/image and in-history capabilities without probing gateway availability. Requests can fail with `INVALID_REQUEST` until the gateway enables the id. With `DEEPSEEK_API_KEY` and a supporting gateway configured, `DEEPSEEK_FLASH_E2E=1` enables the Chat Completions check in [this package's e2e suite](tests/adapter.e2e.ts).
 
-- The default request-image projection caps total pixels at 640,000, below the provider's roughly 1300×1300 processing budget, so it can discard usable detail. Each model's `imagePixelBudget` can override this default; changing the default affects request content and needs separate snapshot verification ([decision](../../../.agents/notes/implemented/bug-fix/2026-09-10-deepseek-image-token-calculator-v41.md)).
-
 <a id="dev-note"></a>
 ### Dev Note
 

+ 1 - 3
packages/llm/llm-deepseek/README.zh.md

@@ -74,7 +74,7 @@ kind: "package-reference"
 
 ### 带 thinking 与图片的流式调用
 
-支持图片的路由会在自身像素与字节预算内把每个持久引用解析为确定性请求版本。`imagePixelBudget` 接受正整数或 `low`;省略时使用总计 640,000 像素,`low` 使用总计 512×512 像素,`imageMaxBytes` 默认为 1 MiB。带 alpha 的图片使用 effort 0 的 WebP,不透明图片使用 JPEG,并采用 85/75/60 质量阶梯;全部候选都超过目标时保留最小输出。每张保留图片前都有文本,注明完整附件 id 与实际请求尺寸。当前文件系统可以映射附件提供方的宿主对象时,该文本还携带只读执行世界路径与可写副本使用的扩展名。纯文本与未列出路由接收稳定附件占位符,而持久历史继续保留图片引用。
+支持图片的路由会在自身投影、单边上限与字节目标内把每个持久引用解析为确定性请求版本。省略 `imagePixelBudget` 时按官方公布的视觉 token 网格投影,即 14 px patch、3:1 降采样、单图最多 1024 token,因此正方形图片最多保留 1302×1302 像素,其他宽高比保留上限内最大的网格;正整数会用总像素预算取代网格,`low` 使用总计 512×512 像素。每张请求图片单边最多 4096 像素,这是提供方对包含 15 张及以上图片的请求的限制;`imageMaxBytes` 默认为 2 MiB。带 alpha 的图片使用 effort 0 的 WebP,不透明图片使用 JPEG,并采用 85/75/60 质量阶梯;全部候选都超过目标时保留最小输出。每张保留图片前都有文本,注明完整附件 id 与实际请求尺寸。当前文件系统可以映射附件提供方的宿主对象时,该文本还携带只读执行世界路径与可写副本使用的扩展名。纯文本与未列出路由接收稳定附件占位符,而持久历史继续保留图片引用。
 
 适配器通常通过 DeepSeek Files API 上传这些确切请求字节,并发送 file-id 块。文件解析失败或超时会用相同请求版本的 base64 data URL 重建整份 chat 请求;一次请求绝不混用 file id 与内联图片。缓存 id 按端点与 API key 限定作用域,在到期前刷新,根据提供方的陈旧文件错误失效,并通过带等待方局部取消的 singleflight 解析。配额失败会先删除一批配置数量的最旧 harness 文件,再重试一次上传。
 
@@ -190,8 +190,6 @@ loop 保留的响应块会追加到下一个请求,并保留其更早的可复
 - **图片是仅用于输入的持久附件**——不支持直接外部 URL 与 assistant 图片输出;DeepSeek 输入通常使用 Files API,仅在单次请求恢复时使用内联 base64。
 - 默认目录预注册 `deepseek-flash` 及其文本、图片和历史内更新能力,不探测网关可用性。网关开放该 ID 前,请求可能以 `INVALID_REQUEST` 失败。配置 `DEEPSEEK_API_KEY` 和支持该 ID 的网关后,设置 `DEEPSEEK_FLASH_E2E=1` 可启用[本包 e2e 测试文件](tests/adapter.e2e.ts)中的 Chat Completions 协议验证。
 
-- 默认请求图片投影限制为 640,000 总像素,低于提供方约 1300×1300 的处理预算,可能丢弃可用细节。每个模型的 `imagePixelBudget` 可以覆盖默认值。更改默认值会改变请求内容,需要单独验证快照([决策](../../../.agents/notes/implemented/bug-fix/2026-09-10-deepseek-image-token-calculator-v41.zh.md))。
-
 <a id="dev-note"></a>
 ### 开发备注
 

+ 10 - 0
scripts/type-equiv.manifest.json

@@ -1001,6 +1001,16 @@
       "symbol": "StoredImageAttachment",
       "source": "packages/attachment/attachment/src/types.ts"
     },
+    {
+      "doc": "docs/subsystems/attachment.md",
+      "symbol": "ImageRequestProjection",
+      "source": "packages/attachment/attachment/src/types.ts"
+    },
+    {
+      "doc": "docs/subsystems/attachment.md",
+      "symbol": "TokenGridProjection",
+      "source": "packages/attachment/attachment/src/types.ts"
+    },
     {
       "doc": "docs/subsystems/attachment.md",
       "symbol": "ImageRequestPolicy",

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików