dsh-tool-vision 0.6.4 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,23 +1,23 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Scorp1o117
4
- Bridge image preview contributed by xing666173 (dsh-bridge-preview),
5
- Copyright (c) 2026 xing666173
6
-
7
- Permission is hereby granted, free of charge, to any person obtaining a copy
8
- of this software and associated documentation files (the "Software"), to deal
9
- in the Software without restriction, including without limitation the rights
10
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
- copies of the Software, and to permit persons to whom the Software is
12
- furnished to do so, subject to the following conditions:
13
-
14
- The above copyright notice and this permission notice shall be included in all
15
- copies or substantial portions of the Software.
16
-
17
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Scorp1o117
4
+ Bridge image preview contributed by xing666173 (dsh-bridge-preview),
5
+ Copyright (c) 2026 xing666173
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
package/README.md CHANGED
@@ -10,12 +10,12 @@ Part of the [DeepSeek Harness Enhancement Suite](https://github.com/Scorp1o117/d
10
10
 
11
11
  External vision model for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness).
12
12
 
13
- DSH 0.1.1 adds native image input for DeepSeek's vision catalog. This plugin
14
- remains useful when you want a separate OpenAI-compatible vision endpoint,
15
- pixel-level image tools, screenshots, or a text-model bridge. The harness
16
- derives every model request strictly from the session log (`llm/stream`
17
- requests must equal the durable derivation — the agent-loop invariant), so the
18
- bridge keeps its conversion inside that durable path:
13
+ DSH 0.1.1 adds native image input for DeepSeek's vision catalog. This plugin
14
+ remains useful when you want a separate OpenAI-compatible vision endpoint,
15
+ pixel-level image tools, screenshots, or a text-model bridge. The harness
16
+ derives every model request strictly from the session log (`llm/stream`
17
+ requests must equal the durable derivation — the agent-loop invariant), so the
18
+ bridge keeps its conversion inside that durable path:
19
19
 
20
20
  1. **`inspect_image` tool** — sends an image (local file, or http(s) URL) to
21
21
  **any OpenAI-compatible** `/chat/completions` endpoint that supports
@@ -82,6 +82,9 @@ Or load it from a local path without npm:
82
82
  | `bridgePreviewScanIntervalMs` | `2000` | Fallback scan interval for the preview scanner (ms); `0` disables the fallback. |
83
83
  | `bridgePreviewHideHint` | `true` | Hide the bridged hint text once the preview image has loaded (kept on failure — safe degradation). |
84
84
  | `bridgeAutoImage` | `true` | While the bridge is on, report image input capability for **every** model to the host admission gate, so pasted images are accepted on text-only models without hand-editing provider configs. |
85
+ | `sendSessionHeader` | `true` | Send a stable session-id header on vision requests. OpenCode Go and similar gateways require `x-opencode-session` (one stable id per conversation); requests without it may error from 2026-09-06. |
86
+ | `sessionHeaderName` | `x-opencode-session` | Header name carrying the session id. |
87
+ | `sessionId` | `''` | Fixed session id for calls without a dsh session context; empty = auto (current dsh session id, else a stable per-process random id). |
85
88
 
86
89
  ## Image bridge setup
87
90
 
@@ -166,11 +169,11 @@ Example endpoints (`baseURL`):
166
169
  > - This plugin is a standard **profile bundle** (`dsh.bundle.patch`):
167
170
  > `dsh plugin --profile web add dsh-tool-vision` installs and mounts it in
168
171
  > one step — no manual `cordis.patch.yml` edits needed.
169
- > - Settings changes hot-apply (no restart needed).
170
- > - Version 0.6.3 and newer require DSH `0.1.0-rc.7` or newer and are tested
171
- > against `0.1.0-rc.7`, `0.1.0-rc.8`, and `0.1.1-rc.1`.
172
- > - DSH `0.1.0-rc.6` users must pin `dsh-tool-vision@0.6.1`, the last release
173
- > carrying the legacy settings-allowlist compatibility patch.
172
+ > - Settings changes hot-apply (no restart needed).
173
+ > - Version 0.6.3 and newer require DSH `0.1.0-rc.7` or newer and are tested
174
+ > against `0.1.0-rc.7`, `0.1.0-rc.8`, and `0.1.1-rc.1`.
175
+ > - DSH `0.1.0-rc.6` users must pin `dsh-tool-vision@0.6.1`, the last release
176
+ > carrying the legacy settings-allowlist compatibility patch.
174
177
 
175
178
  ## Pixel-level vision tools (v0.6.0, ported from dsh-vision-router)
176
179
 
package/README.zh.md CHANGED
@@ -1,169 +1,172 @@
1
- # dsh-tool-vision
2
-
3
- **GitHub**: [Scorp1o117/dsh-tool-vision](https://github.com/Scorp1o117/dsh-tool-vision) · **npm**: [dsh-tool-vision](https://www.npmjs.com/package/dsh-tool-vision) · [English](README.md)
4
-
5
- [![Enhancement Suite](https://img.shields.io/badge/part%20of-Enhancement%20Suite-3964fe)](https://github.com/Scorp1o117/dsh-enhancement-suite) [![npm](https://img.shields.io/npm/v/dsh-enhancement-suite)](https://www.npmjs.com/package/dsh-enhancement-suite)
6
-
7
- 属于 [DeepSeek Harness Enhancement Suite](https://github.com/Scorp1o117/dsh-enhancement-suite) —— Vision · Soul/Persona · 长期记忆 · 插件市场。
8
-
9
- 给 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 外接**视觉模型**的插件。
10
-
11
- DSH 0.1.1 已为 DeepSeek 视觉模型目录加入原生图片输入。本插件继续提供独立的 OpenAI 兼容视觉端点、像素级图片工具、截图,以及文本模型图片桥。Harness 的每次模型请求都**严格从会话日志推导**(`llm/stream` 请求必须与持久化推导一致,否则 agent-loop invariant 会报 `log-reconstruction desync`),因此图片桥仍在可持久重建的路径内完成转换:
12
-
13
- 1. **`inspect_image` 工具** —— 把图片(本地文件或 http(s) URL)发给任意支持 `image_url` 内容块的 OpenAI 兼容 `/chat/completions` 端点,把视觉模型的文字回答带回对话。
14
- 2. **图片桥(v0.2.1)** —— 粘贴的图片在**进入持久化日志之前**就被转换成 `inspect_image` 指引文本,拦截点是 `agent/pre-step` waterfall(这是 harness 唯一允许插件替换"进入某一步的消息"的缝;替换后的消息会**成为**持久化的 `user/message` 日志,所以请求重建 invariant 天然满足)。旧版本已经写进日志的图片消息,会在该会话下一次 pre-step 时用 surface `replace` 惰性修复。只有 `multimodalModels` 白名单内的模型直收图片块;**不参考模型的 `inputModalities` 声明**——因为很多配置为了通过 prompt 准入检查,会给纯文本模型声明 `input: [text, image]`(那只是声明,不代表上游真的能吃 `image_url`)。
15
-
16
- - 除 dsh SDK 外零依赖 —— 兼容任意端点:OpenAI GPT-4o、Qwen-VL(DashScope)、GLM-4V(智谱)、Moonshot、Gemini 兼容端点、本地 Ollama 等。
17
- - 注册在**全局工具层**:进程内所有 Agent 都能调用 `inspect_image`。
18
- - **Web UI 设置栏(v0.3.0)**:设置 → 视觉模型 编辑 `tool-vision` 命名空间(API 地址、只写密钥、模型、桥接选项),写入 `settings.yaml`,**改动即时生效无需重启**。API 密钥存放在 `settings.yaml` 而非 profile patch;插件按包名挂载(`name: 'dsh-tool-vision'`)以便 web 端发现客户端 bundle。
19
-
20
- ## 安装
21
-
22
- 在 profile patch(`$DSH_HOME/profiles/<name>/cordis.patch.yml`)里挂载:
23
-
24
- ```yaml
25
- - insert:
26
- - id: tool-vision
27
- name: 'dsh-tool-vision' # 前置:在 profile 里 pnpm add dsh-tool-vision
28
- config:
29
- baseURL: 'https://api.openai.com/v1'
30
- apiKeyEnv: 'VISION_API_KEY'
31
- model: 'gpt-4o-mini'
32
- ```
33
-
34
- 不装 npm 包、直接加载本地路径:
35
-
36
- ```yaml
37
- - id: tool-vision
38
- name: './plugins/dsh-tool-vision/index.js'
39
- ```
40
-
41
- ## 配置
42
-
43
- | 字段 | 默认值 | 含义 |
44
- |---|---|---|
45
- | `baseURL` | `https://api.openai.com/v1` | OpenAI 兼容 API 基地址 |
46
- | `apiKey` | `''` | API 密钥(优先于环境变量) |
47
- | `apiKeyEnv` | `VISION_API_KEY` | 存放密钥的环境变量名 |
48
- | `model` | `gpt-4o-mini` | 视觉模型 id |
49
- | `maxTokens` | `1024` | 视觉调用最大输出 token |
50
- | `timeoutMs` | `60000` | 单次请求超时 |
51
- | `maxImageBytes` | `10MB` | 本地图片大小上限 |
52
- | `description` | 默认描述 | 工具描述(模型可见) |
53
- | `bridgeTextOnly` | `true` | 把粘贴图片转成文本指引(发给看不懂图片的模型时) |
54
- | `bridgeExportDir` | 临时目录 | 桥接图片导出目录(`os.tmpdir()/dsh-vision-bridge`) |
55
- | `multimodalModels` | `[]` | 直发图片块的模型 id(如 `mimo-v2.5`) |
56
- | `bridgePreview` | `true` | 桥接图片内联预览:用户气泡内显示缩略图,点击放大 |
57
- | `bridgePreviewScanIntervalMs` | `2000` | 预览兜底扫描间隔(毫秒);`0` 关闭兜底 |
58
- | `bridgePreviewHideHint` | `true` | 图片加载成功后隐藏桥接提示文本(失败时保留,安全降级) |
59
- | `bridgeAutoImage` | `true` | 桥接开启时向宿主准入检查报告**所有**模型都支持图片输入,纯文本模型也能直接粘贴图片,无需手动改 provider 配置 |
60
-
61
- ## 图片桥配置
62
-
63
- 1. (一般不需要)只有关闭 `bridgeAutoImage` 时才需要手动给模型声明图片输入(pi-ai 风格),让 harness 放行图片消息:
64
- ```yaml
65
- llm-pi-ai:
66
- providers:
67
- your-provider:
68
- models:
69
- - id: deepseek-v4-flash
70
- input: [text, image]
71
- ```
72
- 2. 在插件配置里列出真正多模态的模型,让它们直收图片块:
73
- ```yaml
74
- - id: tool-vision
75
- name: 'dsh-tool-vision'
76
- config:
77
- multimodalModels: ['mimo-v2.5', 'grok-4.5']
78
- ```
79
-
80
- 之后在文本模型下贴图,转录里会留下一条指引:
81
- `[User sent an image, exported to: <path>. Inspect it with the inspect_image tool...]`
82
- (该消息不再以像素图形式渲染),Agent 会调用视觉端点查看并把结果带回对话。
83
-
84
- > 为什么不用 `llm/stream`?harness 会冻结每个请求,且 agent-loop invariant 会拒绝任何与会话日志推导不一致的请求;这个 cordis 版本的 waterfall `next()` 也无法替换请求参数。`agent/pre-step` 才是受支持的缝:它的决策消息**会成为**持久化日志,invariant 天然成立。
85
-
86
- 密钥解析顺序:`config.apiKey` → `process.env[apiKeyEnv]` → `process.env.OPENAI_API_KEY`。
87
-
88
- ## 桥接图片预览(v0.4.0)
89
-
90
- 纯文本模型下,被桥接的图片在对话里只显示一段 `[User sent an image...]` 文本指引。开启 `bridgePreview`(默认开)后,浏览器端会在**展示层**把指引渲染成气泡内缩略图:
91
-
92
- - **缩略图 + 灯箱**:点击缩略图全屏放大,点击任意处或按 `Esc` 关闭;
93
- - **即时 + 兜底**:新消息由 MutationObserver 即时处理,历史消息由周期兜底扫描补齐(间隔见 `bridgePreviewScanIntervalMs`);
94
- - **隐藏提示文本(P2)**:`bridgePreviewHideHint` 开启时,图片加载成功后桥接文本自动隐藏,气泡只留图片;加载失败则保留文本(安全降级,绝不出现"既无图又无字");
95
- - **识别机制**:桥接文本带不可见前缀标记(`\u200b[bridge]`),客户端据此精确识别桥接块——用户正常发言中出现"exported to:"字样不会被误伤;
96
- - **纯展示层红线**:不修改持久化消息、不修改转录、不修改模型侧文本、不碰 `inspect_image` 调用链。
97
-
98
- 预览图片由同源回环路由 `/plugins/dsh-tool-vision/image` 提供,只读桥接导出目录、仅本机 Host、仅图片扩展名、单文件 ≤ 20MB、防目录穿越。
99
-
100
- ## 工具:`inspect_image`
101
-
102
- | 参数 | 必填 | 含义 |
103
- |---|---|---|
104
- | `path` | ✅ | 图片路径(绝对路径,或相对当前工作区)或 http(s) URL |
105
- | `question` | – | 可选的具体问题 |
106
- | `detail` | – | `auto` / `low` / `high` 分辨率提示 |
107
-
108
- 示例端点(`baseURL`):
109
-
110
- - **OpenAI**:`https://api.openai.com/v1` —— `gpt-4o`、`gpt-4o-mini`
111
- - **阿里云 DashScope(Qwen-VL)**:`https://dashscope.aliyuncs.com/compatible-mode/v1` —— `qwen-vl-plus`、`qwen-vl-max`
112
- - **智谱(GLM-4V)**:`https://open.bigmodel.cn/api/paas/v4` —— `glm-4v-flash`(免费档)、`glm-4v-plus`
113
- - **Moonshot(Kimi)**:`https://api.moonshot.cn/v1` —— `moonshot-v1-8k-vision-preview`
114
- - **Ollama 本地**:`http://localhost:11434/v1` —— `llama3.2-vision`(无需密钥)
115
-
116
- ## 像素级视觉工具(v0.6.0,移植自 dsh-vision-router)
117
-
118
- 14 个 `vision_*` 工具由**同一个** `inspect_image` 配置的端点驱动
119
- (baseURL/apiKey/model)——无 provider 链、无本地模型、零新增配置:
120
-
121
- | 工具 | 用途 |
122
- |---|---|
123
- | `vision_describe` | 看图问答 / 多图对比(可选结构化 JSON) |
124
- | `vision_ground` | 定位目标,返回原图像素坐标框 |
125
- | `vision_detect` | 枚举元素(按钮/输入框/图标…),带编号框 |
126
- | `vision_crop` | 按像素区域裁剪出 PNG 产物 |
127
- | `vision_pixel_diff` | 逐像素对比:差异比例、最差区域、热图、报告 |
128
- | `vision_colors` | 主色量化,还原 UI 调色板 |
129
- | `vision_ocr` | 逐字转写文字(只读字,不做场景识别) |
130
- | `vision_long_screenshot_ocr` | 长截图分块转写为 Markdown |
131
- | `vision_trace` | potrace 矢量化输出彩色 SVG(worker 线程,安全) |
132
- | `vision_extract_foreground` | 纯色背景抠图 → 透明 PNG |
133
- | `vision_html_screenshot` | 本地 HTML 无头渲染截图(禁网) |
134
- | `vision_screenshot` | 桌面截屏(隐私门控:需在设置中开启 `desktopScreenshot`;Win: PowerShell / macOS: screencapture / Linux: import/scrot) |
135
- | `vision_present` | 通过宿主附件库把生成的图片正式展示给用户 |
136
- | `vision_materialize` | 把附件/本地图片落盘为工作区真实路径 |
137
-
138
- 质量与安全细节:
139
-
140
- - **内容哈希缓存**按 端点+模型+图片+问题 取键(切模型不吃旧答案,失败结果
141
- 不入缓存);
142
- - **统一 4MP 降采样**后再调用模型;超大输入 stat 预检直接拒绝(文件与
143
- 附件路径统一 20MB 上限);
144
- - **限流/5xx 自动重试**(感知 Retry-After 退避);端点**内容安全拒绝**明确
145
- 返回 `VISION_CONTENT_FILTERED`,不再误报后端不可用;
146
- - **长截图 OCR 边界**:120s 总预算、40 块上限、取消检查、首块失败即停;
147
- - **路径 containment**(相对输入禁止逃逸工作区);产物写入
148
- `<工作区>/.dsh-tool-vision/`。
149
-
150
- 依赖 `sharp` / `potrace` / `puppeteer-core`(声明为**可选依赖**:平台安装失败
151
- 不会阻断插件安装;缺失时懒加载降级并给出安装提示,不影响其他工具)。
152
-
153
- `vision_screenshot` 属于隐私敏感能力,**默认不注册**——在 tool-vision 设置中
154
- 开启 `desktopScreenshot: true` 后才会注册桌面截屏工具。
155
-
156
- ## 限制
157
-
158
- - 从 `0.6.3` 起最低支持 DSH `0.1.0-rc.7`,已针对 `0.1.0-rc.7`、
159
- `0.1.0-rc.8` 和 `0.1.1-rc.1` 测试。仍使用 DSH `0.1.0-rc.6` 的用户请锁定
160
- `dsh-tool-vision@0.6.1`;这是最后一个包含旧 settings 白名单兼容补丁的版本。
161
- - 被桥接的图片以文本指引进入对话(转录而非像素)——文本模型无法做像素级上下文推理;视觉模型的描述通过 `inspect_image` 回传。
162
- - 图片以 base64 传输;注意隐私与大小限制。
163
- - 独立于 dsh-llm 的路由/重试体系;失败会向 Agent 返回明确错误。
164
-
165
- ## License
166
-
167
- MIT —— 桥接预览与整合:xing666173。像素级视觉工具移植自
168
- [dsh-vision-router](https://github.com/ysr666/dsh-vision-router)(© ysr666,MIT),
169
- 在此致谢。
1
+ # dsh-tool-vision
2
+
3
+ **GitHub**: [Scorp1o117/dsh-tool-vision](https://github.com/Scorp1o117/dsh-tool-vision) · **npm**: [dsh-tool-vision](https://www.npmjs.com/package/dsh-tool-vision) · [English](README.md)
4
+
5
+ [![Enhancement Suite](https://img.shields.io/badge/part%20of-Enhancement%20Suite-3964fe)](https://github.com/Scorp1o117/dsh-enhancement-suite) [![npm](https://img.shields.io/npm/v/dsh-enhancement-suite)](https://www.npmjs.com/package/dsh-enhancement-suite)
6
+
7
+ 属于 [DeepSeek Harness Enhancement Suite](https://github.com/Scorp1o117/dsh-enhancement-suite) —— Vision · Soul/Persona · 长期记忆 · 插件市场。
8
+
9
+ 给 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 外接**视觉模型**的插件。
10
+
11
+ DSH 0.1.1 已为 DeepSeek 视觉模型目录加入原生图片输入。本插件继续提供独立的 OpenAI 兼容视觉端点、像素级图片工具、截图,以及文本模型图片桥。Harness 的每次模型请求都**严格从会话日志推导**(`llm/stream` 请求必须与持久化推导一致,否则 agent-loop invariant 会报 `log-reconstruction desync`),因此图片桥仍在可持久重建的路径内完成转换:
12
+
13
+ 1. **`inspect_image` 工具** —— 把图片(本地文件或 http(s) URL)发给任意支持 `image_url` 内容块的 OpenAI 兼容 `/chat/completions` 端点,把视觉模型的文字回答带回对话。
14
+ 2. **图片桥(v0.2.1)** —— 粘贴的图片在**进入持久化日志之前**就被转换成 `inspect_image` 指引文本,拦截点是 `agent/pre-step` waterfall(这是 harness 唯一允许插件替换"进入某一步的消息"的缝;替换后的消息会**成为**持久化的 `user/message` 日志,所以请求重建 invariant 天然满足)。旧版本已经写进日志的图片消息,会在该会话下一次 pre-step 时用 surface `replace` 惰性修复。只有 `multimodalModels` 白名单内的模型直收图片块;**不参考模型的 `inputModalities` 声明**——因为很多配置为了通过 prompt 准入检查,会给纯文本模型声明 `input: [text, image]`(那只是声明,不代表上游真的能吃 `image_url`)。
15
+
16
+ - 除 dsh SDK 外零依赖 —— 兼容任意端点:OpenAI GPT-4o、Qwen-VL(DashScope)、GLM-4V(智谱)、Moonshot、Gemini 兼容端点、本地 Ollama 等。
17
+ - 注册在**全局工具层**:进程内所有 Agent 都能调用 `inspect_image`。
18
+ - **Web UI 设置栏(v0.3.0)**:设置 → 视觉模型 编辑 `tool-vision` 命名空间(API 地址、只写密钥、模型、桥接选项),写入 `settings.yaml`,**改动即时生效无需重启**。API 密钥存放在 `settings.yaml` 而非 profile patch;插件按包名挂载(`name: 'dsh-tool-vision'`)以便 web 端发现客户端 bundle。
19
+
20
+ ## 安装
21
+
22
+ 在 profile patch(`$DSH_HOME/profiles/<name>/cordis.patch.yml`)里挂载:
23
+
24
+ ```yaml
25
+ - insert:
26
+ - id: tool-vision
27
+ name: 'dsh-tool-vision' # 前置:在 profile 里 pnpm add dsh-tool-vision
28
+ config:
29
+ baseURL: 'https://api.openai.com/v1'
30
+ apiKeyEnv: 'VISION_API_KEY'
31
+ model: 'gpt-4o-mini'
32
+ ```
33
+
34
+ 不装 npm 包、直接加载本地路径:
35
+
36
+ ```yaml
37
+ - id: tool-vision
38
+ name: './plugins/dsh-tool-vision/index.js'
39
+ ```
40
+
41
+ ## 配置
42
+
43
+ | 字段 | 默认值 | 含义 |
44
+ |---|---|---|
45
+ | `baseURL` | `https://api.openai.com/v1` | OpenAI 兼容 API 基地址 |
46
+ | `apiKey` | `''` | API 密钥(优先于环境变量) |
47
+ | `apiKeyEnv` | `VISION_API_KEY` | 存放密钥的环境变量名 |
48
+ | `model` | `gpt-4o-mini` | 视觉模型 id |
49
+ | `maxTokens` | `1024` | 视觉调用最大输出 token |
50
+ | `timeoutMs` | `60000` | 单次请求超时 |
51
+ | `maxImageBytes` | `10MB` | 本地图片大小上限 |
52
+ | `description` | 默认描述 | 工具描述(模型可见) |
53
+ | `bridgeTextOnly` | `true` | 把粘贴图片转成文本指引(发给看不懂图片的模型时) |
54
+ | `bridgeExportDir` | 临时目录 | 桥接图片导出目录(`os.tmpdir()/dsh-vision-bridge`) |
55
+ | `multimodalModels` | `[]` | 直发图片块的模型 id(如 `mimo-v2.5`) |
56
+ | `bridgePreview` | `true` | 桥接图片内联预览:用户气泡内显示缩略图,点击放大 |
57
+ | `bridgePreviewScanIntervalMs` | `2000` | 预览兜底扫描间隔(毫秒);`0` 关闭兜底 |
58
+ | `bridgePreviewHideHint` | `true` | 图片加载成功后隐藏桥接提示文本(失败时保留,安全降级) |
59
+ | `bridgeAutoImage` | `true` | 桥接开启时向宿主准入检查报告**所有**模型都支持图片输入,纯文本模型也能直接粘贴图片,无需手动改 provider 配置 |
60
+ | `sendSessionHeader` | `true` | 给视觉请求发送稳定的会话标识头。OpenCode Go 等网关要求每请求带 `x-opencode-session`(一个对话一个稳定 ID),缺失的请求在 2026-09-06 起可能报错 |
61
+ | `sessionHeaderName` | `x-opencode-session` | 承载会话 ID 的头名 |
62
+ | `sessionId` | `''` | 固定会话 ID(后台/无 dsh 会话调用时用);留空则自动:优先取当前 dsh 会话 ID,否则用进程级稳定随机 ID |
63
+
64
+ ## 图片桥配置
65
+
66
+ 1. (一般不需要)只有关闭 `bridgeAutoImage` 时才需要手动给模型声明图片输入(pi-ai 风格),让 harness 放行图片消息:
67
+ ```yaml
68
+ llm-pi-ai:
69
+ providers:
70
+ your-provider:
71
+ models:
72
+ - id: deepseek-v4-flash
73
+ input: [text, image]
74
+ ```
75
+ 2. 在插件配置里列出真正多模态的模型,让它们直收图片块:
76
+ ```yaml
77
+ - id: tool-vision
78
+ name: 'dsh-tool-vision'
79
+ config:
80
+ multimodalModels: ['mimo-v2.5', 'grok-4.5']
81
+ ```
82
+
83
+ 之后在文本模型下贴图,转录里会留下一条指引:
84
+ `[User sent an image, exported to: <path>. Inspect it with the inspect_image tool...]`
85
+ (该消息不再以像素图形式渲染),Agent 会调用视觉端点查看并把结果带回对话。
86
+
87
+ > 为什么不用 `llm/stream`?harness 会冻结每个请求,且 agent-loop invariant 会拒绝任何与会话日志推导不一致的请求;这个 cordis 版本的 waterfall `next()` 也无法替换请求参数。`agent/pre-step` 才是受支持的缝:它的决策消息**会成为**持久化日志,invariant 天然成立。
88
+
89
+ 密钥解析顺序:`config.apiKey` → `process.env[apiKeyEnv]` → `process.env.OPENAI_API_KEY`。
90
+
91
+ ## 桥接图片预览(v0.4.0)
92
+
93
+ 纯文本模型下,被桥接的图片在对话里只显示一段 `[User sent an image...]` 文本指引。开启 `bridgePreview`(默认开)后,浏览器端会在**展示层**把指引渲染成气泡内缩略图:
94
+
95
+ - **缩略图 + 灯箱**:点击缩略图全屏放大,点击任意处或按 `Esc` 关闭;
96
+ - **即时 + 兜底**:新消息由 MutationObserver 即时处理,历史消息由周期兜底扫描补齐(间隔见 `bridgePreviewScanIntervalMs`);
97
+ - **隐藏提示文本(P2)**:`bridgePreviewHideHint` 开启时,图片加载成功后桥接文本自动隐藏,气泡只留图片;加载失败则保留文本(安全降级,绝不出现"既无图又无字");
98
+ - **识别机制**:桥接文本带不可见前缀标记(`\u200b[bridge]`),客户端据此精确识别桥接块——用户正常发言中出现"exported to:"字样不会被误伤;
99
+ - **纯展示层红线**:不修改持久化消息、不修改转录、不修改模型侧文本、不碰 `inspect_image` 调用链。
100
+
101
+ 预览图片由同源回环路由 `/plugins/dsh-tool-vision/image` 提供,只读桥接导出目录、仅本机 Host、仅图片扩展名、单文件 ≤ 20MB、防目录穿越。
102
+
103
+ ## 工具:`inspect_image`
104
+
105
+ | 参数 | 必填 | 含义 |
106
+ |---|---|---|
107
+ | `path` | ✅ | 图片路径(绝对路径,或相对当前工作区)或 http(s) URL |
108
+ | `question` | – | 可选的具体问题 |
109
+ | `detail` | – | `auto` / `low` / `high` 分辨率提示 |
110
+
111
+ 示例端点(`baseURL`):
112
+
113
+ - **OpenAI**:`https://api.openai.com/v1` —— `gpt-4o`、`gpt-4o-mini`
114
+ - **阿里云 DashScope(Qwen-VL)**:`https://dashscope.aliyuncs.com/compatible-mode/v1` —— `qwen-vl-plus`、`qwen-vl-max`
115
+ - **智谱(GLM-4V)**:`https://open.bigmodel.cn/api/paas/v4` —— `glm-4v-flash`(免费档)、`glm-4v-plus`
116
+ - **Moonshot(Kimi)**:`https://api.moonshot.cn/v1` —— `moonshot-v1-8k-vision-preview`
117
+ - **Ollama 本地**:`http://localhost:11434/v1` —— `llama3.2-vision`(无需密钥)
118
+
119
+ ## 像素级视觉工具(v0.6.0,移植自 dsh-vision-router)
120
+
121
+ 14 个 `vision_*` 工具由**同一个** `inspect_image` 配置的端点驱动
122
+ (baseURL/apiKey/model)——无 provider 链、无本地模型、零新增配置:
123
+
124
+ | 工具 | 用途 |
125
+ |---|---|
126
+ | `vision_describe` | 看图问答 / 多图对比(可选结构化 JSON) |
127
+ | `vision_ground` | 定位目标,返回原图像素坐标框 |
128
+ | `vision_detect` | 枚举元素(按钮/输入框/图标…),带编号框 |
129
+ | `vision_crop` | 按像素区域裁剪出 PNG 产物 |
130
+ | `vision_pixel_diff` | 逐像素对比:差异比例、最差区域、热图、报告 |
131
+ | `vision_colors` | 主色量化,还原 UI 调色板 |
132
+ | `vision_ocr` | 逐字转写文字(只读字,不做场景识别) |
133
+ | `vision_long_screenshot_ocr` | 长截图分块转写为 Markdown |
134
+ | `vision_trace` | potrace 矢量化输出彩色 SVG(worker 线程,安全) |
135
+ | `vision_extract_foreground` | 纯色背景抠图 → 透明 PNG |
136
+ | `vision_html_screenshot` | 本地 HTML 无头渲染截图(禁网) |
137
+ | `vision_screenshot` | 桌面截屏(隐私门控:需在设置中开启 `desktopScreenshot`;Win: PowerShell / macOS: screencapture / Linux: import/scrot) |
138
+ | `vision_present` | 通过宿主附件库把生成的图片正式展示给用户 |
139
+ | `vision_materialize` | 把附件/本地图片落盘为工作区真实路径 |
140
+
141
+ 质量与安全细节:
142
+
143
+ - **内容哈希缓存**按 端点+模型+图片+问题 取键(切模型不吃旧答案,失败结果
144
+ 不入缓存);
145
+ - **统一 4MP 降采样**后再调用模型;超大输入 stat 预检直接拒绝(文件与
146
+ 附件路径统一 20MB 上限);
147
+ - **限流/5xx 自动重试**(感知 Retry-After 退避);端点**内容安全拒绝**明确
148
+ 返回 `VISION_CONTENT_FILTERED`,不再误报后端不可用;
149
+ - **长截图 OCR 边界**:120s 总预算、40 块上限、取消检查、首块失败即停;
150
+ - **路径 containment**(相对输入禁止逃逸工作区);产物写入
151
+ `<工作区>/.dsh-tool-vision/`。
152
+
153
+ 依赖 `sharp` / `potrace` / `puppeteer-core`(声明为**可选依赖**:平台安装失败
154
+ 不会阻断插件安装;缺失时懒加载降级并给出安装提示,不影响其他工具)。
155
+
156
+ `vision_screenshot` 属于隐私敏感能力,**默认不注册**——在 tool-vision 设置中
157
+ 开启 `desktopScreenshot: true` 后才会注册桌面截屏工具。
158
+
159
+ ## 限制
160
+
161
+ - 从 `0.6.3` 起最低支持 DSH `0.1.0-rc.7`,已针对 `0.1.0-rc.7`、
162
+ `0.1.0-rc.8` 和 `0.1.1-rc.1` 测试。仍使用 DSH `0.1.0-rc.6` 的用户请锁定
163
+ `dsh-tool-vision@0.6.1`;这是最后一个包含旧 settings 白名单兼容补丁的版本。
164
+ - 被桥接的图片以文本指引进入对话(转录而非像素)——文本模型无法做像素级上下文推理;视觉模型的描述通过 `inspect_image` 回传。
165
+ - 图片以 base64 传输;注意隐私与大小限制。
166
+ - 独立于 dsh-llm 的路由/重试体系;失败会向 Agent 返回明确错误。
167
+
168
+ ## License
169
+
170
+ MIT —— 桥接预览与整合:xing666173。像素级视觉工具移植自
171
+ [dsh-vision-router](https://github.com/ysr666/dsh-vision-router)(© ysr666,MIT),
172
+ 在此致谢。
package/client.js CHANGED
@@ -80,6 +80,11 @@ window.__ModuleLoader__.load({
80
80
  fieldBridgePreviewHideHint: "图片加载成功后隐藏桥接提示文本",
81
81
  fieldBridgeAutoImage: "自动声明图片能力(纯文本模型也能粘贴图片)",
82
82
  fieldDesktopScreenshot: "桌面截屏工具(vision_screenshot,默认关闭)",
83
+ fieldSendSessionHeader: "发送会话标识头(x-opencode-session)",
84
+ fieldSessionHeaderName: "会话标识头名称",
85
+ fieldSessionId: "固定会话 ID(留空 = 自动)",
86
+ hintSendSessionHeader: "OpenCode Go 等网关要求每个请求带稳定的会话标识(一个对话一个 ID);请求不带可能报错。默认发送:优先用当前 dsh 会话 ID,无会话上下文时用下方固定 ID,再否则用进程级随机 ID。",
87
+ hintSessionId: "后台/无会话调用时使用的固定会话标识(如 dsh-vision-<hostname>);留空则每进程自动生成一个稳定 ID。",
83
88
  hintBridgePreview: "纯展示层:不影响模型侧文本与 inspect_image 调用。",
84
89
  hintBridgePreviewScanIntervalMs: "默认 2000ms;越小响应越快,越大越省资源。",
85
90
  hintBridgePreviewHideHint: "加载失败时保留文本(安全降级,绝不出现既无图又无字)。",
@@ -119,6 +124,11 @@ window.__ModuleLoader__.load({
119
124
  fieldBridgePreviewHideHint: "Hide the bridged hint text once the image has loaded",
120
125
  fieldBridgeAutoImage: "Auto-declare image capability (paste images on text-only models)",
121
126
  fieldDesktopScreenshot: "Desktop screenshot tool (vision_screenshot, off by default)",
127
+ fieldSendSessionHeader: "Send session-id header (x-opencode-session)",
128
+ fieldSessionHeaderName: "Session-id header name",
129
+ fieldSessionId: "Fixed session id (empty = auto)",
130
+ hintSendSessionHeader: "OpenCode Go and similar gateways require a stable per-conversation id on every request; requests without it may error. When on: prefer the current dsh session id, else the fixed id below, else a per-process random id.",
131
+ hintSessionId: "Fixed session id used for background calls without a dsh session; leave empty for a per-process stable random id.",
122
132
  hintBridgePreview: "Pure display layer: the model-facing text and the inspect_image chain are untouched.",
123
133
  hintBridgePreviewScanIntervalMs: "Default 2000ms; lower is snappier, higher is cheaper.",
124
134
  hintBridgePreviewHideHint: "Text is kept on load failure (safe degradation, never no image AND no text).",
@@ -150,7 +160,10 @@ window.__ModuleLoader__.load({
150
160
  { key: "bridgePreviewScanIntervalMs", label: "fieldBridgePreviewScanIntervalMs", type: "number" },
151
161
  { key: "bridgePreviewHideHint", label: "fieldBridgePreviewHideHint", type: "checkbox" },
152
162
  { key: "bridgeAutoImage", label: "fieldBridgeAutoImage", type: "checkbox" },
153
- { key: "desktopScreenshot", label: "fieldDesktopScreenshot", type: "checkbox" }
163
+ { key: "desktopScreenshot", label: "fieldDesktopScreenshot", type: "checkbox" },
164
+ { key: "sendSessionHeader", label: "fieldSendSessionHeader", type: "checkbox" },
165
+ { key: "sessionHeaderName", label: "fieldSessionHeaderName", type: "text" },
166
+ { key: "sessionId", label: "fieldSessionId", type: "text" }
154
167
  ];
155
168
  var ZH_HINTS = {
156
169
  apiKey: "apiKeyHint",