@anionex/dsh-vision-toolkit 0.1.45 → 0.1.46
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 +25 -1
- package/README.i18n.yaml +2 -2
- package/README.md +27 -4
- package/README.zh.md +27 -4
- package/docs/requirements-traceability/README.i18n.yaml +2 -2
- package/docs/requirements-traceability/README.md +4 -4
- package/docs/requirements-traceability/README.zh.md +4 -4
- package/lib/client.js +20 -1
- package/lib/client.js.map +1 -1
- package/lib/config.js +147 -5
- package/lib/config.js.map +1 -1
- package/lib/evidence-cache.js +3 -0
- package/lib/evidence-cache.js.map +1 -1
- package/lib/image-input-variants.js +12 -3
- package/lib/image-input-variants.js.map +1 -1
- package/lib/index.js +4 -7
- package/lib/index.js.map +1 -1
- package/lib/runtime.js +40 -5
- package/lib/runtime.js.map +1 -1
- package/lib/settings-compat.js +38 -0
- package/lib/settings-compat.js.map +1 -0
- package/lib/types/client/index.d.ts +7 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/config.d.ts +19 -4
- package/lib/types/config.d.ts.map +1 -1
- package/lib/types/evidence-cache.d.ts.map +1 -1
- package/lib/types/image-input-variants.d.ts.map +1 -1
- package/lib/types/index.d.ts +2 -2
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/runtime.d.ts +4 -0
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/settings-compat.d.ts +12 -0
- package/lib/types/settings-compat.d.ts.map +1 -0
- package/lib/types/upstream.d.ts +4 -1
- package/lib/types/upstream.d.ts.map +1 -1
- package/lib/upstream.js +88 -29
- package/lib/upstream.js.map +1 -1
- package/package.json +23 -22
- package/src/client/index.tsx +29 -3
- package/src/config.ts +171 -9
- package/src/evidence-cache.ts +3 -0
- package/src/image-input-variants.ts +12 -3
- package/src/index.ts +4 -8
- package/src/runtime.ts +45 -5
- package/src/settings-compat.ts +56 -0
- package/src/upstream.ts +88 -25
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,29 @@ All notable user-facing changes to DSH Vision Toolkit are documented in this fil
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.1.46] - 2026-10-01
|
|
8
|
+
|
|
9
|
+
### Compatibility
|
|
10
|
+
|
|
11
|
+
- Verified DSH `0.2.0-rc.2` using an isolated, hoisted tarball Profile, actual visual-tool calls, and Web Settings. Add an exact peer/compatibility declaration and a required CI Profile lane for this release. The fixture explicitly selects its OpenAI-compatible protocol after the host's default provider protocol changed.
|
|
12
|
+
- 已通过 DSH `0.2.0-rc.2` 隔离压缩包 Profile、真实视觉工具调用及 Web 设置验收;增加确切兼容声明及 CI 验收矩阵。官方默认协议变更后,测试 fixture 显式选择原有 OpenAI 协议。核实时官方尚无正式稳定版。
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Added an explicit OpenAI Responses protocol for custom vision providers. An optional, constrained `reasoningEffort` is forwarded only for Responses requests, exposed conditionally in bilingual Settings, and included in both live-Session and durable evidence cache identities. Existing OpenAI configurations remain on Chat Completions and omit effort by default.
|
|
17
|
+
|
|
18
|
+
- Added hardened `provider.headers` for non-secret deployment metadata and `provider.sessionHeaders` for gateways such as OpenCode Zen that require per-session routing. Session values are process-keyed HMAC identifiers that never expose raw Session ids or workspace paths; configured headers are bounded, conflict-checked, error-redacted, cache-isolated, restricted to the provider origin/base path, and shared by both requests in a connection-and-model health operation. The manifest-verified vendored snapshot remains unchanged ([#144](https://github.com/Anionex/dsh-vision-toolkit/issues/144)).
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Restored startup and live Settings on DSH 0.1.7, whose Config projection replaced the old `settings.register/get/watch` API. The plugin now uses the host's matching Schema form, unwraps live configuration values for runtime use, and follows Settings document updates. Earlier DSH versions continue using their registration API.
|
|
23
|
+
- Vision API subprocesses now try IPv4 addresses before IPv6 when both are returned by DNS. This avoids exhausting the connection timeout on unusable fake IPv6 addresses in TUN proxies while preserving IPv6-only endpoints.
|
|
24
|
+
- Stopped the image-input variant route from crashing on request messages that carry no `source`. The variant read `message.source.kind` while assembling the evidence pass, but the official `createUserMessage()` helper writes no `source` at runtime (its published type still declares one as required), so a plugin that built a programmatic `ctx.llm.stream()` call on a `vision-toolkit-*` route — the usual summarize, title, or classify pattern (`@modusensus/dsh-mneme` ≤ 0.8.1 did exactly this) — failed with `TypeError: Cannot read properties of undefined (reading 'kind')`, which the host surfaced only as a generic `UNKNOWN` failure chunk. A user message without provenance is now treated as a user turn, and assistant history without provenance is skipped instead of throwing; messages that do carry a source behave exactly as before.
|
|
25
|
+
|
|
26
|
+
### Documentation
|
|
27
|
+
|
|
28
|
+
- Documented `/responses` endpoint composition, common and provider-specific effort values, possible token/latency/cost effects, and the need to review provider data-retention policy even though requests send `store: false`.
|
|
29
|
+
|
|
7
30
|
## [0.1.45] - 2026-09-14
|
|
8
31
|
|
|
9
32
|
### Fixed
|
|
@@ -455,7 +478,8 @@ All notable user-facing changes to DSH Vision Toolkit are documented in this fil
|
|
|
455
478
|
- Runtime teardown cancels in-flight operations before removing Agent-scoped tools, the activation bootstrap, and the Skill.
|
|
456
479
|
- The Web client is published through the current nested `dsh.client` manifest and loader-compatible built artifact required by DSH snapshot0810.
|
|
457
480
|
|
|
458
|
-
[Unreleased]: https://github.com/Anionex/dsh-vision-toolkit/compare/v0.1.
|
|
481
|
+
[Unreleased]: https://github.com/Anionex/dsh-vision-toolkit/compare/v0.1.46...HEAD
|
|
482
|
+
[0.1.46]: https://github.com/Anionex/dsh-vision-toolkit/compare/v0.1.45...v0.1.46
|
|
459
483
|
[0.1.45]: https://github.com/Anionex/dsh-vision-toolkit/compare/v0.1.44...v0.1.45
|
|
460
484
|
[0.1.44]: https://github.com/Anionex/dsh-vision-toolkit/compare/v0.1.43...v0.1.44
|
|
461
485
|
[0.1.43]: https://github.com/Anionex/dsh-vision-toolkit/compare/v0.1.42...v0.1.43
|
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write dsh-vision-toolkit/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 4fbd8f3ed25c4e810601067365c07aeaacac14da
|
|
6
|
+
README.zh.md: 6c1b977fa3f142f70f97dd03aa3d6f80bebf3ffc
|
package/README.md
CHANGED
|
@@ -274,7 +274,29 @@ You can also configure a Profile patch:
|
|
|
274
274
|
protocol: openai
|
|
275
275
|
```
|
|
276
276
|
|
|
277
|
-
OpenAI Chat Completions-compatible endpoints and Anthropic Messages are supported.
|
|
277
|
+
OpenAI Chat Completions-compatible endpoints, OpenAI Responses, and Anthropic Messages are supported. Existing configurations continue to use Chat Completions. To use a Responses-compatible endpoint, set `protocol: responses`; the plugin appends `/responses` to the configured base URL. An optional `reasoningEffort` is sent only for Responses requests:
|
|
278
|
+
|
|
279
|
+
```yaml
|
|
280
|
+
protocol: responses
|
|
281
|
+
reasoningEffort: medium
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Leaving `reasoningEffort` blank uses the model or proxy default. Common values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`, but the field intentionally accepts provider-specific values made from letters, digits, `.`, `_`, and `-` (up to 64 characters). Support and billing vary by model or proxy; higher effort may increase reasoning tokens, latency, and cost. The Responses request sets `store: false`, but that flag does not replace reviewing the provider's own data-retention policy. The Web Settings panel exposes the full provider, runtime, timeout, image-limit, and image-input-variant configuration.
|
|
285
|
+
Profile patches can also supply non-secret deployment metadata in `provider.headers`. A gateway that routes by conversation can name one or more generated headers in `provider.sessionHeaders`; OpenCode Zen, for example, requires `x-opencode-session`:
|
|
286
|
+
|
|
287
|
+
```yaml
|
|
288
|
+
- id: vision-toolkit
|
|
289
|
+
config:
|
|
290
|
+
provider:
|
|
291
|
+
headers:
|
|
292
|
+
x-tenant: acme
|
|
293
|
+
sessionHeaders:
|
|
294
|
+
- x-opencode-session
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Static header values are ordinary plaintext Settings, not Credentials. Do not put API keys, bearer tokens, cookies, or other secrets in `provider.headers`; continue to use `provider.credential` for the vision API key. The plugin rejects client-owned authentication headers and HTTP routing/framing headers, case-insensitive duplicates, conflicts between the two fields, invalid names or values, more than 32 combined entries, names over 128 bytes, values over 4096 bytes, or more than 16384 bytes in total.
|
|
298
|
+
|
|
299
|
+
Every session header receives the first 32 hexadecimal characters of an HMAC-SHA256 value keyed with fresh process randomness. The value is stable for the same operation identity while the DSH process is running and rotates after restart. Calls with a Session use its id as the private HMAC input; only calls without one fall back to `workspace:<absolute path>`, and neither the raw Session id nor workspace path is sent. One Settings health operation uses the same derived value for `GET /models` and its real model request. Headers are injected only into URLs on the configured provider origin and beneath its base path. With both fields empty, no extra-header environment value is created and provider wire-header behavior remains unchanged.
|
|
278
300
|
|
|
279
301
|
The advanced **Default save directory** setting can place artifacts, pasted images, and caches below an absolute POSIX shared root such as `/tmp/dsh-vision-toolkit`; the plugin creates a private mode-0700 child for the current user and workspace. Leaving it blank keeps the existing workspace-local `.dsh-vision-toolkit` directory. Configured shared roots are currently rejected on Windows because their ownership and access-control lists cannot yet be verified safely.
|
|
280
302
|
|
|
@@ -292,11 +314,12 @@ For advanced setups — overriding `runtime.python`, using `runtime.mode: extern
|
|
|
292
314
|
|
|
293
315
|
| Problem | What to do |
|
|
294
316
|
| --- | --- |
|
|
295
|
-
| The vision-model test fails with `Vision API returned an incompatible response structure` | The base URL usually needs a path prefix. Local OpenAI-compatible services such as LM Studio and Ollama should be entered as `http://127.0.0.1:1234/v1` (include `/v1`); the plugin appends `/chat/completions
|
|
317
|
+
| The vision-model test fails with `Vision API returned an incompatible response structure` | The base URL usually needs a path prefix. Local OpenAI-compatible services such as LM Studio and Ollama should be entered as `http://127.0.0.1:1234/v1` (include `/v1`); the plugin appends `/chat/completions` for OpenAI Chat Completions or `/responses` for OpenAI Responses, and a port-only address may hit an unknown endpoint |
|
|
296
318
|
| Pasting an image still says the model does not support image input | Restart the Web Profile, refresh the page, and confirm the selected route has the `(Vision Toolkit)` suffix. You can also place the image in the session workspace and invoke `/vision-skills` |
|
|
297
319
|
| The vision service returns 429 | Wait for the `Retry-After` interval, or switch to your own endpoint when you need stable higher volume |
|
|
298
320
|
| The image exceeds a size or pixel limit | Crop or resize it first; the error identifies whether bytes or decoded pixels caused the rejection |
|
|
299
321
|
| A custom Credential is missing | Enter the API key in **Settings → Vision Toolkit** and confirm the Credential name matches the provider configuration |
|
|
322
|
+
| OpenCode Zen returns `400 MissingSessionID` | Add `x-opencode-session` to `provider.sessionHeaders` in the Profile patch, then restart the Profile; do not put a Session id or API key in `provider.headers` |
|
|
300
323
|
| First-time runtime setup fails | The standalone-Python download needs network and disk access (domestic mirror first, GitHub fallback). Check connectivity or package-cache access, or install Python 3.11+ / configure `runtime.python` in Settings, then retry the model test |
|
|
301
324
|
| Chrome is not found | Install Chrome, Chromium, or Edge. Only HTML screenshot rendering is unavailable; the other tools still work |
|
|
302
325
|
| DSH Desktop says `dsh` is not recognized, or its built-in marketplace install fails | Open **DSH Terminal** from the tray, run `dsh plugin --profile desktop add @anionex/dsh-vision-toolkit`, then restart DSH Desktop. The Desktop 2.0.1 marketplace has known install issues, so the terminal command is the reliable path for now |
|
|
@@ -304,9 +327,9 @@ For advanced setups — overriding `runtime.python`, using `runtime.mode: extern
|
|
|
304
327
|
|
|
305
328
|
## FAQ
|
|
306
329
|
|
|
307
|
-
**
|
|
330
|
+
**How does a vision model affect costs?**
|
|
308
331
|
|
|
309
|
-
|
|
332
|
+
Each inspection is a separate multimodal request containing the necessary intent and image; context does not accumulate across calls. Actual cost depends on the provider, image, model, and — for Responses — `reasoningEffort`. Higher effort may consume more reasoning tokens and take longer. Leave effort blank to use the provider default, or use a locally deployed small multimodal side model (for example the Gemma 4 or Qwen 3.5/3.6 series) when predictable local cost matters.
|
|
310
333
|
|
|
311
334
|
## Donation
|
|
312
335
|
|
package/README.zh.md
CHANGED
|
@@ -272,7 +272,29 @@ flowchart LR
|
|
|
272
272
|
protocol: openai
|
|
273
273
|
```
|
|
274
274
|
|
|
275
|
-
支持 OpenAI Chat Completions
|
|
275
|
+
支持 OpenAI Chat Completions 兼容端点、OpenAI Responses 和 Anthropic Messages。已有配置仍默认使用 Chat Completions。若要使用兼容 Responses 的端点,请设置 `protocol: responses`;插件会在 API 地址后拼接 `/responses`。可选的 `reasoningEffort` 仅随 Responses 请求发送:
|
|
276
|
+
|
|
277
|
+
```yaml
|
|
278
|
+
protocol: responses
|
|
279
|
+
reasoningEffort: medium
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
`reasoningEffort` 留空时使用模型或代理的默认值。常见值包括 `none`、`minimal`、`low`、`medium`、`high` 和 `xhigh`;为兼容第三方代理和未来扩展,该字段也接受由字母、数字、`.`、`_`、`-` 组成且不超过 64 位的提供方自定义值。实际支持范围与计费由模型或代理决定;较高强度可能增加推理 token、延迟和费用。Responses 请求会设置 `store: false`,但该标记不能替代核查服务商自己的数据保留政策。Web Settings 页面还可以调整超时、图片限制、并发、运行时和图片输入变体。
|
|
283
|
+
Profile patch 还可以通过 `provider.headers` 配置非秘密的部署元数据。需要按会话路由的网关可在 `provider.sessionHeaders` 中列出一个或多个运行时生成的请求头;例如 OpenCode Zen 要求 `x-opencode-session`:
|
|
284
|
+
|
|
285
|
+
```yaml
|
|
286
|
+
- id: vision-toolkit
|
|
287
|
+
config:
|
|
288
|
+
provider:
|
|
289
|
+
headers:
|
|
290
|
+
x-tenant: acme
|
|
291
|
+
sessionHeaders:
|
|
292
|
+
- x-opencode-session
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
静态请求头是保存在 Settings 中的普通明文,不是 Credential。不要在 `provider.headers` 中填写 API Key、Bearer Token、Cookie 或其他秘密;视觉 API Key 仍应使用 `provider.credential`。插件会拒绝客户端自有的鉴权头和 HTTP 路由/请求帧控制头、大小写不敏感的重复项、两个字段间的冲突、非法名称或值、合计超过 32 项、名称超过 128 字节、值超过 4096 字节,或总量超过 16384 字节的配置。
|
|
296
|
+
|
|
297
|
+
每个会话请求头都会获得 HMAC-SHA256(带密钥的不可逆摘要)的前 32 位十六进制值,密钥在 DSH 进程启动时随机生成。同一进程内,相同 operation identity(一次调用采用的会话身份)得到稳定值;重启后会轮换。有 Session 的调用只把 Session id 作为内部 HMAC 输入;确实没有 Session 时才回退到 `workspace:<绝对路径>`,原始 Session id 和 workspace 路径都不会发送。同一次 Settings 健康检查的 `GET /models` 与真实模型请求共用派生值。请求头只注入配置 provider 的同源 URL 且路径必须位于其 base path 下。两个字段均为空时,不创建额外请求头环境变量,provider 的网络请求头语义保持原样。
|
|
276
298
|
|
|
277
299
|
高级设置中的 **默认保存目录** 可以把产物、粘贴图片和缓存放到 `/tmp/dsh-vision-toolkit` 等 POSIX 绝对共享根目录下;插件会为当前用户和工作区创建权限为 0700 的私有子目录。留空时继续使用工作区内原有的 `.dsh-vision-toolkit` 目录。Windows 目前会拒绝配置共享根目录,因为插件尚不能安全校验其所有权和访问控制列表。
|
|
278
300
|
|
|
@@ -290,11 +312,12 @@ flowchart LR
|
|
|
290
312
|
|
|
291
313
|
| 问题 | 处理方式 |
|
|
292
314
|
| --- | --- |
|
|
293
|
-
| 视觉模型测试失败:`Vision API returned an incompatible response structure` | 通常是 API 地址少了路径前缀。LM Studio、Ollama 等本地 OpenAI 兼容服务需填写 `http://127.0.0.1:1234/v1`(带 `/v1
|
|
315
|
+
| 视觉模型测试失败:`Vision API returned an incompatible response structure` | 通常是 API 地址少了路径前缀。LM Studio、Ollama 等本地 OpenAI 兼容服务需填写 `http://127.0.0.1:1234/v1`(带 `/v1`);OpenAI Chat Completions 会拼接 `/chat/completions`,OpenAI Responses 会拼接 `/responses`,只填端口号可能命中未知端点 |
|
|
294
316
|
| 粘贴图片后仍提示模型不支持图片 | 重启 Web Profile 并刷新页面,确认当前模型已切换到带 `(Vision Toolkit)` 的变体;也可以把图片先放进会话工作区,再调用 `/vision-skills` |
|
|
295
317
|
| 视觉服务提示 429 | 按错误中的 `Retry-After` 等待后重试;如果需要稳定高额度,切换到自己的视觉端点 |
|
|
296
318
|
| 图片过大或像素超限 | 先裁剪或缩放图片;错误会明确显示是字节还是像素限制 |
|
|
297
319
|
| 自定义 Credential 缺失 | 在 **设置 → 视觉工具** 填写 API Key,并确认 Credential 名称与配置一致 |
|
|
320
|
+
| OpenCode Zen 返回 `400 MissingSessionID` | 在 Profile patch 的 `provider.sessionHeaders` 中加入 `x-opencode-session`,然后重启 Profile;不要把 Session id 或 API Key 写进 `provider.headers` |
|
|
298
321
|
| 首次运行时准备失败 | 自动下载托管 Python 需要网络和磁盘权限(默认先走国内镜像,失败时回退 GitHub);失败时检查网络或包缓存,也可以安装 Python 3.11+ 或在 Settings 中配置 `runtime.python`,然后重新测试 |
|
|
299
322
|
| 找不到 Chrome | 安装 Chrome、Chromium 或 Edge;只有 HTML 截图不可用,其他工具不受影响 |
|
|
300
323
|
| DSH Desktop 提示找不到 `dsh` 命令,或内置插件市场安装失败 | 从托盘打开 **DSH 终端**,运行 `dsh plugin --profile desktop add @anionex/dsh-vision-toolkit`,再重启 DSH Desktop。桌面版 2.0.1 的内置市场存在已知安装问题,当前请优先使用终端安装 |
|
|
@@ -302,9 +325,9 @@ flowchart LR
|
|
|
302
325
|
|
|
303
326
|
## FAQ
|
|
304
327
|
|
|
305
|
-
|
|
328
|
+
**视觉模型会怎样影响成本?**
|
|
306
329
|
|
|
307
|
-
|
|
330
|
+
每次检查都是一条独立的多模态请求,只携带必要意图和图片,调用之间不会累积上下文。实际费用取决于服务商、图片、模型,以及 Responses 的 `reasoningEffort`;较高强度可能消耗更多推理 token 并增加延迟。留空可使用提供方默认值;若更看重可预测的本地成本,也可以使用本地部署的小型多模态侧模型(例如 Gemma 4 或 Qwen 3.5/3.6 系列)。
|
|
308
331
|
|
|
309
332
|
## 赞赏
|
|
310
333
|
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write dsh-vision-toolkit/docs/requirements-traceability/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: d54a924435446194cd92e4449177d1fdb95f0ade
|
|
6
|
+
README.zh.md: 03541d3d2c7ca6fd43cdff852c9fda5f1e7ede53
|
|
@@ -11,7 +11,7 @@ This reference maps the DSH Vision Toolkit product brief's committed P0/P1 requi
|
|
|
11
11
|
| P0-1 Standard Profile Bundle | **Delivered** | [`package.json`](../../package.json), [`cordis.patch.yml`](../../cordis.patch.yml), [`src/index.ts`](../../src/index.ts), committed `lib/` | [`tests/package-layout.spec.ts`](../../tests/package-layout.spec.ts), [`tests/profile-install.e2e.spec.ts`](../../tests/profile-install.e2e.spec.ts), clean `--dump-config`, Web/Headless boot, disable, and removal checks |
|
|
12
12
|
| P0-2 Upstream reuse | **Delivered** | [`src/version.ts`](../../src/version.ts), [`src/runtime-install.ts`](../../src/runtime-install.ts), [`scripts/upstream-manifest.mjs`](../../scripts/upstream-manifest.mjs), [`scripts/sync-upstream.mjs`](../../scripts/sync-upstream.mjs), [`vendor/agent-vision-toolkit/UPSTREAM_MANIFEST.json`](../../vendor/agent-vision-toolkit/UPSTREAM_MANIFEST.json) | [`tests/upstream.spec.ts`](../../tests/upstream.spec.ts), [`tests/runtime-install.spec.ts`](../../tests/runtime-install.spec.ts), build-time manifest verification |
|
|
13
13
|
| P0-3 Native tools | **Delivered** | Independent definitions in [`src/tools.ts`](../../src/tools.ts), Agent-scoped publication in [`src/exposure.ts`](../../src/exposure.ts), shared execution in [`src/runtime.ts`](../../src/runtime.ts), upstream adapter in [`src/upstream.ts`](../../src/upstream.ts) | [`tests/tools.spec.ts`](../../tests/tools.spec.ts), [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts), [`tests/upstream.spec.ts`](../../tests/upstream.spec.ts), [`tests/vision-prompt-guard.spec.ts`](../../tests/vision-prompt-guard.spec.ts), real-profile progressive calls for all five P0 tools |
|
|
14
|
-
| P0-4 Configuration and Credentials | **Delivered** | [`src/config.ts`](../../src/config.ts), durable configured-root history in [`src/storage-history.ts`](../../src/storage-history.ts), per-operation resolution in [`src/runtime.ts`](../../src/runtime.ts), isolated environment construction in [`src/upstream.ts`](../../src/upstream.ts) | [`tests/config.spec.ts`](../../tests/config.spec.ts), [`tests/storage-history.spec.ts`](../../tests/storage-history.spec.ts), Credential/error/redaction cases in [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts) and [`tests/errors.spec.ts`](../../tests/errors.spec.ts) |
|
|
14
|
+
| P0-4 Configuration and Credentials | **Delivered** | [`src/config.ts`](../../src/config.ts), durable configured-root history in [`src/storage-history.ts`](../../src/storage-history.ts), per-operation protocol/credential resolution in [`src/runtime.ts`](../../src/runtime.ts), isolated environment construction in [`src/upstream.ts`](../../src/upstream.ts) | [`tests/config.spec.ts`](../../tests/config.spec.ts), [`tests/storage-history.spec.ts`](../../tests/storage-history.spec.ts), OpenAI Responses/optional reasoning-effort transport plus Credential/error/redaction cases in [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts) and [`tests/errors.spec.ts`](../../tests/errors.spec.ts) |
|
|
15
15
|
| P0-5 Skill lifecycle | **Delivered** | Readiness ordering, lifecycle abort, and global disposer in [`src/index.ts`](../../src/index.ts); per-Agent activation, restoration, and teardown in [`src/exposure.ts`](../../src/exposure.ts); bundled content in [`src/skill.ts`](../../src/skill.ts) | Agent isolation, native/direct/Code Mode activation, Session restoration, in-flight cancellation, and disposal cases in [`tests/tools.spec.ts`](../../tests/tools.spec.ts); progressive exposure plus disable/uninstall paths in [`tests/profile-install.e2e.spec.ts`](../../tests/profile-install.e2e.spec.ts) |
|
|
16
16
|
| P0-6 Text-only model results | **Delivered** | JSON output schemas and pure renderers in [`src/tools.ts`](../../src/tools.ts); canonical Artifact descriptors in [`src/artifacts.ts`](../../src/artifacts.ts); schema visibility derived from durable Skill-load evidence in [`src/exposure.ts`](../../src/exposure.ts) | Tool schema/presentation assertions in [`tests/tools.spec.ts`](../../tests/tools.spec.ts), request-by-request schema and model-facing transcript assertions in [`tests/profile-install.e2e.spec.ts`](../../tests/profile-install.e2e.spec.ts) |
|
|
17
17
|
| P0-7 Stable errors | **Delivered** | [`src/errors.ts`](../../src/errors.ts), boundary validation in [`src/paths.ts`](../../src/paths.ts), [`src/runtime.ts`](../../src/runtime.ts), and [`src/upstream.ts`](../../src/upstream.ts) | [`tests/errors.spec.ts`](../../tests/errors.spec.ts), [`tests/paths.spec.ts`](../../tests/paths.spec.ts), parser, timeout, cancellation, credential, and capacity cases in runtime/upstream tests |
|
|
@@ -25,7 +25,7 @@ This reference maps the DSH Vision Toolkit product brief's committed P0/P1 requi
|
|
|
25
25
|
| P1-2 Extended tools | **Delivered** | `vision_pixel_diff`, `vision_long_screenshot_ocr`, `vision_extract_foreground`, `vision_dominant_colors`, and `vision_html_screenshot` in [`src/tools.ts`](../../src/tools.ts) and [`src/runtime.ts`](../../src/runtime.ts) | P1 parser and runtime cases in [`tests/upstream.spec.ts`](../../tests/upstream.spec.ts) and [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts); pixel-diff and long-OCR real-profile calls |
|
|
26
26
|
| P1-3 Dedicated Web presentation | **Delivered** | Browser plugin and dedicated cards in [`src/client/index.tsx`](../../src/client/index.tsx); presentation-only capability metadata in [`src/artifact-access.ts`](../../src/artifact-access.ts) | [`tests/client.spec.ts`](../../tests/client.spec.ts), safe preview tests in [`tests/artifact-access.spec.ts`](../../tests/artifact-access.spec.ts), Web visual/console QA |
|
|
27
27
|
| P1-4 Health checks | **Delivered** | Health/version runtime contracts in [`src/runtime.ts`](../../src/runtime.ts), same-origin Web actions in [`src/web.ts`](../../src/web.ts), and the Settings surface in [`src/client/index.tsx`](../../src/client/index.tsx); Settings distinguishes the lightweight authenticated `GET /models` probe from an explicit real multimodal request using the bundled diagnostic image, and these administrative diagnostics are deliberately absent from the model tool registry | Health and real-model success/failure cases in [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts), explicit API/model-test behavior in [`tests/web.spec.ts`](../../tests/web.spec.ts) and [`tests/client.spec.ts`](../../tests/client.spec.ts), plus model-tool absence in [`tests/tools.spec.ts`](../../tests/tools.spec.ts) |
|
|
28
|
-
| P1-5 Settings | **Delivered** | Namespace/config in [`src/config.ts`](../../src/config.ts), prepare-before-swap manager in [`src/runtime-manager.ts`](../../src/runtime-manager.ts), private same-origin route in [`src/web.ts`](../../src/web.ts), section in [`src/client/index.tsx`](../../src/client/index.tsx) | [`tests/runtime-manager.spec.ts`](../../tests/runtime-manager.spec.ts), [`tests/web.spec.ts`](../../tests/web.spec.ts), [`tests/client.spec.ts`](../../tests/client.spec.ts), clean Web-profile save/restart QA |
|
|
28
|
+
| P1-5 Settings | **Delivered** | Namespace/config in [`src/config.ts`](../../src/config.ts), prepare-before-swap manager in [`src/runtime-manager.ts`](../../src/runtime-manager.ts), private same-origin route in [`src/web.ts`](../../src/web.ts), section in [`src/client/index.tsx`](../../src/client/index.tsx), including conditional OpenAI Responses reasoning effort | [`tests/runtime-manager.spec.ts`](../../tests/runtime-manager.spec.ts), [`tests/web.spec.ts`](../../tests/web.spec.ts), conditional display/validation/save/reload cases in [`tests/client.spec.ts`](../../tests/client.spec.ts), clean Web-profile save/restart QA |
|
|
29
29
|
| P1-6 Install and upgrade experience | **Delivered** | Bundle lifecycle in [`src/index.ts`](../../src/index.ts), content-addressed managed runtime in [`src/runtime-install.ts`](../../src/runtime-install.ts), generation manager in [`src/runtime-manager.ts`](../../src/runtime-manager.ts), and the registry/Profile-constrained Settings updater with DSH Web restart handoff in [`src/plugin-update.ts`](../../src/plugin-update.ts) | Runtime interruption/concurrency tests, frozen-lockfile rollback and runtime-readiness checks in [`tests/plugin-update.spec.ts`](../../tests/plugin-update.spec.ts), package-layout test, clean-profile lifecycle, persisted Settings and failed-candidate retention checks |
|
|
30
30
|
|
|
31
31
|
## Cross-cutting requirements
|
|
@@ -34,7 +34,7 @@ This reference maps the DSH Vision Toolkit product brief's committed P0/P1 requi
|
|
|
34
34
|
|---|---|
|
|
35
35
|
| Security | [`src/paths.ts`](../../src/paths.ts) confines reads and writes by realpath; [`src/runtime.ts`](../../src/runtime.ts) decodes and limits images before upload and structurally parses trace SVG before commit; [`src/upstream.ts`](../../src/upstream.ts) marks image text/instructions as untrusted in every direct and long-OCR visual-model prompt; [`src/tools.ts`](../../src/tools.ts) and [`src/skill.ts`](../../src/skill.ts) tell the text agent to treat derived output as evidence, not commands; [`src/artifact-access.ts`](../../src/artifact-access.ts) revalidates signed files on every read and sandboxes SVG; [`src/errors.ts`](../../src/errors.ts) redacts secrets. Path, symlink, format, size, XML/doctype, prompt guard, token-forgery, file-replacement, and CSP cases are automated. |
|
|
36
36
|
| Portability | Runtime and browser calls use argv vectors and Node filesystem/process APIs rather than POSIX shell composition. Managed preparation has `uv` and `venv`/pip paths; Python and Chrome discovery include platform-specific candidates. Package tests avoid machine-local dependency specifiers, and the checked-in fixture/profile flow is keyless. |
|
|
37
|
-
| Performance and cancellation | [`src/runtime.ts`](../../src/runtime.ts) applies one hard deadline, propagates `AbortSignal`, bounds per-session concurrency, rejects oversized decoded images before remote I/O, deduplicates repeated inputs within one glance operation, and keeps a one-entry content/configuration-keyed cache for the last successful identical glance in each live Session. [`src/index.ts`](../../src/index.ts) aborts plugin-owned calls before unregistering tools. Timeout, caller/plugin cancellation, cache-hit/miss, semaphore, and independent-session behavior are automated. |
|
|
37
|
+
| Performance and cancellation | [`src/runtime.ts`](../../src/runtime.ts) applies one hard deadline, propagates `AbortSignal`, bounds per-session concurrency, rejects oversized decoded images before remote I/O, deduplicates repeated inputs within one glance operation, and keeps a one-entry content/configuration-keyed cache for the last successful identical glance in each live Session. Both this cache and the durable evidence cache include the effective Responses reasoning effort. [`src/index.ts`](../../src/index.ts) aborts plugin-owned calls before unregistering tools. Timeout, caller/plugin cancellation, effort isolation, cache-hit/miss, semaphore, and independent-session behavior are automated. |
|
|
38
38
|
| Observability | [`src/runtime.ts`](../../src/runtime.ts) logs bounded tool name, outcome, total/upstream duration, image count/bytes/pixels, cache hits, model, and error category while excluding base64, credentials, headers, and unbounded upstream output. |
|
|
39
39
|
| Model-context economy | The profile-global runtime publishes one small bootstrap, while [`src/exposure.ts`](../../src/exposure.ts) mounts the ten execution schemas only in an Agent that loads `vision-skills` and hides the bootstrap afterward. Other Agents remain unchanged. Health, connection, and version diagnostics stay exclusively on the Web Settings seam and never become model tools. Unit tests cover Agent isolation and restoration; every real-profile tool flow asserts the initial and activated schema sets. |
|
|
40
40
|
| Maintainability | Every tool calls one [`VisionToolkitRuntime`](../../src/runtime.ts); DSH-specific adaptation remains in [`src/tools.ts`](../../src/tools.ts), [`src/exposure.ts`](../../src/exposure.ts), [`src/index.ts`](../../src/index.ts), [`src/web.ts`](../../src/web.ts), and [`src/client/index.tsx`](../../src/client/index.tsx), while the pinned upstream remains the sole algorithm source. Runtime readiness, Skill/bootstrap publication, and Agent-scoped schemas belong to one lifecycle generation. |
|
|
@@ -58,7 +58,7 @@ This reference maps the DSH Vision Toolkit product brief's committed P0/P1 requi
|
|
|
58
58
|
| Scope | Status | Decision |
|
|
59
59
|
|---|---|---|
|
|
60
60
|
| P2 stable `ctx.visionToolkit` service and capability discovery | **Deferred by design** | The product brief requires at least one independent plugin consumer before stabilizing this API. `VisionToolkitRuntime` remains package-internal, so P0/P1 can evolve without creating a false compatibility promise. |
|
|
61
|
-
| P2 provider ecosystem | **Deferred by design** | The package supports its pinned upstream and one configured endpoint through OpenAI Chat Completions or Anthropic Messages; it does not prebuild an unused provider registry. |
|
|
61
|
+
| P2 provider ecosystem | **Deferred by design** | The package supports its pinned upstream and one configured endpoint through OpenAI Chat Completions, OpenAI Responses, or Anthropic Messages; it does not prebuild an unused provider registry. |
|
|
62
62
|
| P3 exploratory inputs and automation | **Out of scope** | Upload/drag-and-drop, camera/video/audio/document ingestion, interactive annotations, automatic clicking, remote clusters, model routing/voting, and cross-session caches are not part of this release contract. |
|
|
63
63
|
|
|
64
64
|
## Reproducible verification
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
| P0-1 标准 Profile Bundle | **已交付** | [`package.json`](../../package.json)、[`cordis.patch.yml`](../../cordis.patch.yml)、[`src/index.ts`](../../src/index.ts)、已提交的 `lib/` | [`tests/package-layout.spec.ts`](../../tests/package-layout.spec.ts)、[`tests/profile-install.e2e.spec.ts`](../../tests/profile-install.e2e.spec.ts)、干净 `--dump-config`、Web/Headless 启动、禁用和移除检查 |
|
|
12
12
|
| P0-2 上游复用 | **已交付** | [`src/version.ts`](../../src/version.ts)、[`src/runtime-install.ts`](../../src/runtime-install.ts)、[`scripts/upstream-manifest.mjs`](../../scripts/upstream-manifest.mjs)、[`scripts/sync-upstream.mjs`](../../scripts/sync-upstream.mjs)、[`vendor/agent-vision-toolkit/UPSTREAM_MANIFEST.json`](../../vendor/agent-vision-toolkit/UPSTREAM_MANIFEST.json) | [`tests/upstream.spec.ts`](../../tests/upstream.spec.ts)、[`tests/runtime-install.spec.ts`](../../tests/runtime-install.spec.ts)、构建时 manifest 验证 |
|
|
13
13
|
| P0-3 原生工具 | **已交付** | [`src/tools.ts`](../../src/tools.ts) 中的独立定义、[`src/exposure.ts`](../../src/exposure.ts) 中的 Agent 级发布、[`src/runtime.ts`](../../src/runtime.ts) 中的共享执行、[`src/upstream.ts`](../../src/upstream.ts) 中的上游适配 | [`tests/tools.spec.ts`](../../tests/tools.spec.ts)、[`tests/runtime.spec.ts`](../../tests/runtime.spec.ts)、[`tests/upstream.spec.ts`](../../tests/upstream.spec.ts)、[`tests/vision-prompt-guard.spec.ts`](../../tests/vision-prompt-guard.spec.ts)、全部五个 P0 工具的真实 Profile 渐进调用 |
|
|
14
|
-
| P0-4 配置与 Credential | **已交付** | [`src/config.ts`](../../src/config.ts)、[`src/storage-history.ts`](../../src/storage-history.ts) 中配置根目录历史的持久化、[`src/runtime.ts`](../../src/runtime.ts)
|
|
14
|
+
| P0-4 配置与 Credential | **已交付** | [`src/config.ts`](../../src/config.ts)、[`src/storage-history.ts`](../../src/storage-history.ts) 中配置根目录历史的持久化、[`src/runtime.ts`](../../src/runtime.ts) 中的逐操作协议/Credential 解析、[`src/upstream.ts`](../../src/upstream.ts) 中的隔离环境构造 | [`tests/config.spec.ts`](../../tests/config.spec.ts)、[`tests/storage-history.spec.ts`](../../tests/storage-history.spec.ts)、[`tests/runtime.spec.ts`](../../tests/runtime.spec.ts) 中的 OpenAI Responses/可选推理强度传输,以及该文件与 [`tests/errors.spec.ts`](../../tests/errors.spec.ts) 中的 Credential/错误/脱敏用例 |
|
|
15
15
|
| P0-5 skill 生命周期 | **已交付** | [`src/index.ts`](../../src/index.ts) 中的就绪顺序、生命周期中止和全局 disposer;[`src/exposure.ts`](../../src/exposure.ts) 中的逐 Agent 激活、恢复和释放;[`src/skill.ts`](../../src/skill.ts) 中的打包内容 | [`tests/tools.spec.ts`](../../tests/tools.spec.ts) 中的 Agent 隔离、原生/直接/Code Mode 激活、Session 恢复、在途取消和释放用例;[`tests/profile-install.e2e.spec.ts`](../../tests/profile-install.e2e.spec.ts) 中的渐进暴露、禁用和卸载路径 |
|
|
16
16
|
| P0-6 纯文本模型结果 | **已交付** | [`src/tools.ts`](../../src/tools.ts) 中的 JSON 输出 schema 与纯渲染函数;[`src/artifacts.ts`](../../src/artifacts.ts) 中的规范产物描述;[`src/exposure.ts`](../../src/exposure.ts) 中由持久 Skill 加载证据派生的 schema 可见性 | [`tests/tools.spec.ts`](../../tests/tools.spec.ts) 中的工具 schema/展示断言、[`tests/profile-install.e2e.spec.ts`](../../tests/profile-install.e2e.spec.ts) 中逐请求的 schema 与模型可见 transcript(文本记录)断言 |
|
|
17
17
|
| P0-7 稳定错误 | **已交付** | [`src/errors.ts`](../../src/errors.ts),以及 [`src/paths.ts`](../../src/paths.ts)、[`src/runtime.ts`](../../src/runtime.ts)、[`src/upstream.ts`](../../src/upstream.ts) 中的边界验证 | [`tests/errors.spec.ts`](../../tests/errors.spec.ts)、[`tests/paths.spec.ts`](../../tests/paths.spec.ts),以及运行时/上游测试中的解析、超时、取消、Credential 和容量用例 |
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
| P1-2 扩展工具 | **已交付** | [`src/tools.ts`](../../src/tools.ts) 和 [`src/runtime.ts`](../../src/runtime.ts) 中的 `vision_pixel_diff`、`vision_long_screenshot_ocr`、`vision_extract_foreground`、`vision_dominant_colors`、`vision_html_screenshot` | [`tests/upstream.spec.ts`](../../tests/upstream.spec.ts) 和 [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts) 中的 P1 解析器/运行时用例;真实 Profile pixel-diff 和长截图 OCR 调用 |
|
|
26
26
|
| P1-3 专用 Web 展示 | **已交付** | [`src/client/index.tsx`](../../src/client/index.tsx) 中的浏览器插件与专用卡片;[`src/artifact-access.ts`](../../src/artifact-access.ts) 中仅供展示的能力元数据 | [`tests/client.spec.ts`](../../tests/client.spec.ts)、[`tests/artifact-access.spec.ts`](../../tests/artifact-access.spec.ts) 中的安全预览测试、Web 视觉/Console QA |
|
|
27
27
|
| P1-4 健康检查 | **已交付** | [`src/runtime.ts`](../../src/runtime.ts) 中的健康检查/版本运行时契约、[`src/web.ts`](../../src/web.ts) 中的同源 Web 操作,以及 [`src/client/index.tsx`](../../src/client/index.tsx) 中的 Settings 界面;Settings 明确区分携带凭据的轻量 `GET /models` 探测与使用自带诊断图片的显式真实多模态请求,这些管理诊断能力有意不进入模型工具注册表 | [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts) 中的健康检查及真实模型成功/失败用例、[`tests/web.spec.ts`](../../tests/web.spec.ts) 与 [`tests/client.spec.ts`](../../tests/client.spec.ts) 中的显式 API/模型测试行为,以及 [`tests/tools.spec.ts`](../../tests/tools.spec.ts) 中的模型工具缺席断言 |
|
|
28
|
-
| P1-5 Settings | **已交付** | [`src/config.ts`](../../src/config.ts) 中的 namespace/配置、[`src/runtime-manager.ts`](../../src/runtime-manager.ts) 中的 prepare-before-swap manager、[`src/web.ts`](../../src/web.ts) 中的同源私有路由、[`src/client/index.tsx`](../../src/client/index.tsx)
|
|
28
|
+
| P1-5 Settings | **已交付** | [`src/config.ts`](../../src/config.ts) 中的 namespace/配置、[`src/runtime-manager.ts`](../../src/runtime-manager.ts) 中的 prepare-before-swap manager、[`src/web.ts`](../../src/web.ts) 中的同源私有路由、[`src/client/index.tsx`](../../src/client/index.tsx) 中包含条件显示 OpenAI Responses 推理强度的 Settings 分区 | [`tests/runtime-manager.spec.ts`](../../tests/runtime-manager.spec.ts)、[`tests/web.spec.ts`](../../tests/web.spec.ts)、[`tests/client.spec.ts`](../../tests/client.spec.ts) 中的条件显示/校验/保存/重载用例、干净 Web Profile 保存/重启 QA |
|
|
29
29
|
| P1-6 安装与升级体验 | **已交付** | [`src/index.ts`](../../src/index.ts) 中的 Bundle 生命周期、[`src/runtime-install.ts`](../../src/runtime-install.ts) 中的内容寻址 managed 运行时、[`src/runtime-manager.ts`](../../src/runtime-manager.ts) 中的 generation manager,以及 [`src/plugin-update.ts`](../../src/plugin-update.ts) 中按 registry/Profile 安全约束执行的 Settings 更新器和 DSH Web 重启交接 | 运行时中断/并发测试、[`tests/plugin-update.spec.ts`](../../tests/plugin-update.spec.ts) 中的 frozen-lockfile 回滚与 Runtime 就绪检查、软件包布局测试、干净 Profile 生命周期、Settings 持久化和失败候选保留检查 |
|
|
30
30
|
|
|
31
31
|
## 横切需求
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
|---|---|
|
|
35
35
|
| 安全 | [`src/paths.ts`](../../src/paths.ts) 通过 realpath 限制读写范围;[`src/runtime.ts`](../../src/runtime.ts) 在上传前解码并限制图片,并在提交前结构化解析 trace SVG;[`src/upstream.ts`](../../src/upstream.ts) 在每个直接和长截图 OCR 视觉模型提示词中把图片文字/指令标记为不可信;[`src/tools.ts`](../../src/tools.ts) 和 [`src/skill.ts`](../../src/skill.ts) 要求文本 agent 只把衍生输出当作证据而非命令;[`src/artifact-access.ts`](../../src/artifact-access.ts) 每次读取都重新验证签名文件并 sandbox SVG;[`src/errors.ts`](../../src/errors.ts) 对密钥脱敏。路径、符号链接、格式、大小、XML/doctype、提示词 guard、伪造 token、文件替换和 CSP 用例均有自动化覆盖。 |
|
|
36
36
|
| 可移植性 | 运行时和浏览器调用使用 argv 向量与 Node 文件系统/进程 API,不拼接 POSIX Shell。Managed 准备同时提供 `uv` 和 `venv`/pip 路径;Python 与 Chrome 探测包含平台候选。软件包测试拒绝机器本地依赖声明,已提交 fixture/Profile 流程无需真实 Key。 |
|
|
37
|
-
| 性能与取消 | [`src/runtime.ts`](../../src/runtime.ts) 应用一个硬截止时间、传递 `AbortSignal`、限制每个会话的并发数、在远程 I/O 前拒绝解码后过大的图片、在一次 glance 操作中对重复输入去重,并为每个活动会话保留一条按内容/配置键控的最近成功相同 glance
|
|
37
|
+
| 性能与取消 | [`src/runtime.ts`](../../src/runtime.ts) 应用一个硬截止时间、传递 `AbortSignal`、限制每个会话的并发数、在远程 I/O 前拒绝解码后过大的图片、在一次 glance 操作中对重复输入去重,并为每个活动会话保留一条按内容/配置键控的最近成功相同 glance 缓存。该缓存和持久 evidence 缓存都会纳入实际生效的 Responses 推理强度。[`src/index.ts`](../../src/index.ts) 会在注销工具前中止插件拥有的调用。超时、调用方/插件取消、effort 隔离、缓存命中/未命中、信号量和独立会话行为均有自动化覆盖。 |
|
|
38
38
|
| 可观察性 | [`src/runtime.ts`](../../src/runtime.ts) 记录有界的工具名、结果、总耗时/上游耗时、图片数量/字节/像素、缓存命中、模型和错误类别,同时排除 base64、Credential、鉴权头和无界上游输出。 |
|
|
39
39
|
| 模型上下文经济性 | Profile 级运行时只发布一个很小的引导工具,[`src/exposure.ts`](../../src/exposure.ts) 仅为加载 `vision-skills` 的 Agent 挂载 10 个执行 schema,并在成功后隐藏引导工具。其他 Agent 不受影响。健康检查、连接测试和版本诊断只存在于 Web Settings 边界,永远不会成为模型工具。单元测试覆盖 Agent 隔离与恢复;每条真实 Profile 工具流程都会断言初始和激活后的 schema 集合。 |
|
|
40
40
|
| 可维护性 | 每个工具都调用同一个 [`VisionToolkitRuntime`](../../src/runtime.ts);DSH 专用适配保留在 [`src/tools.ts`](../../src/tools.ts)、[`src/exposure.ts`](../../src/exposure.ts)、[`src/index.ts`](../../src/index.ts)、[`src/web.ts`](../../src/web.ts) 和 [`src/client/index.tsx`](../../src/client/index.tsx),固定上游继续作为算法唯一来源。运行时就绪、skill/引导工具发布和 Agent 级 schema 属于同一个生命周期 generation。 |
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
| 范围 | 状态 | 决策 |
|
|
59
59
|
|---|---|---|
|
|
60
60
|
| P2 稳定 `ctx.visionToolkit` 服务与能力发现 | **按设计推迟** | 产品需求要求至少一个独立插件消费方出现后再稳定该 API。`VisionToolkitRuntime` 保持包内部使用,使 P0/P1 可以继续演进,而不会制造虚假的兼容性承诺。 |
|
|
61
|
-
| P2 提供方生态 | **按设计推迟** | 本包支持固定上游,并通过 OpenAI Chat Completions 或 Anthropic Messages 使用一个已配置端点;不会预先构建无人使用的提供方注册表。 |
|
|
61
|
+
| P2 提供方生态 | **按设计推迟** | 本包支持固定上游,并通过 OpenAI Chat Completions、OpenAI Responses 或 Anthropic Messages 使用一个已配置端点;不会预先构建无人使用的提供方注册表。 |
|
|
62
62
|
| P3 探索性输入与自动化 | **范围外** | 上传/拖拽、摄像头/视频/音频/文档输入、交互式标注、自动点击、远程集群、模型路由/投票和跨 Session 缓存不属于本版本契约。 |
|
|
63
63
|
|
|
64
64
|
## 可复现验证
|
package/lib/client.js
CHANGED
|
@@ -87,6 +87,8 @@ const DEFAULT_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKi
|
|
|
87
87
|
const BUILT_IN_FREE_VISION_BASE_URL = 'https://vision.anionex.me/v1';
|
|
88
88
|
const BUILT_IN_FREE_VISION_CREDENTIAL = 'ANIONEX_FREE_VISION';
|
|
89
89
|
const BUILT_IN_FREE_VISION_MODEL = 'gemini-3.7-flash';
|
|
90
|
+
const MAX_REASONING_EFFORT_LENGTH = 64;
|
|
91
|
+
const REASONING_EFFORT_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/u;
|
|
90
92
|
const AIHUBMIX_TUTORIAL_URL_EN = 'https://github.com/Anionex/dsh-vision-toolkit/blob/main/docs/aihubmix-gemini-vision.md';
|
|
91
93
|
const AIHUBMIX_TUTORIAL_URL_ZH = 'https://github.com/Anionex/dsh-vision-toolkit/blob/main/docs/aihubmix-gemini-vision.zh.md';
|
|
92
94
|
const en = {
|
|
@@ -109,6 +111,9 @@ const en = {
|
|
|
109
111
|
credentialHint: 'The built-in free provider needs no user key. For a custom provider, this is the DSH credential reference used to store its key.',
|
|
110
112
|
model: 'Model',
|
|
111
113
|
protocol: 'API protocol',
|
|
114
|
+
reasoningEffort: 'Vision service reasoning effort',
|
|
115
|
+
reasoningEffortHint: 'Optional. Common values: none, minimal, low, medium, high, xhigh. Supported values and billing depend on the model or proxy; higher effort may increase tokens, latency, and cost. Requests send store:false, but you should still verify the provider\'s data-retention policy.',
|
|
116
|
+
reasoningEffortInvalid: 'Vision service reasoning effort must be at most 64 ASCII letters, digits, dots, underscores, or hyphens.',
|
|
112
117
|
anthropicThinking: 'Anthropic thinking',
|
|
113
118
|
anthropicThinkingHint: 'omit has the broadest compatibility. Use disabled or adaptive only when the selected model documents that mode; restore omit first after HTTP 400.',
|
|
114
119
|
userAgent: 'User-Agent',
|
|
@@ -297,6 +302,9 @@ const zh = {
|
|
|
297
302
|
credentialHint: '内置免费视觉服务无需用户密钥;切换到自定义服务时,此处是保存其密钥的 DSH 凭据名称。',
|
|
298
303
|
model: '模型名称',
|
|
299
304
|
protocol: 'API 协议',
|
|
305
|
+
reasoningEffort: '视觉服务推理强度',
|
|
306
|
+
reasoningEffortHint: '可选。常见值:none、minimal、low、medium、high、xhigh。实际支持值和计费由模型或代理决定;较高强度可能增加 token、延迟和费用。请求会发送 store:false,但仍应核查服务商的数据保留政策。',
|
|
307
|
+
reasoningEffortInvalid: '视觉服务推理强度最多 64 位,只能包含 ASCII 字母、数字、点、下划线或连字符。',
|
|
300
308
|
anthropicThinking: 'Anthropic thinking',
|
|
301
309
|
anthropicThinkingHint: 'omit 兼容性最好。仅当所选模型明确支持时使用 disabled 或 adaptive;遇到 HTTP 400 时先恢复 omit。',
|
|
302
310
|
userAgent: 'User-Agent',
|
|
@@ -854,8 +862,11 @@ function draftOf(value) {
|
|
|
854
862
|
credential: value.provider?.credential ?? BUILT_IN_FREE_VISION_CREDENTIAL,
|
|
855
863
|
model: value.provider?.model ?? BUILT_IN_FREE_VISION_MODEL,
|
|
856
864
|
protocol: value.provider?.protocol ?? 'openai',
|
|
865
|
+
reasoningEffort: value.provider?.protocol === 'responses' ? value.provider.reasoningEffort ?? '' : '',
|
|
857
866
|
anthropicThinking: value.provider?.anthropicThinking ?? 'omit',
|
|
858
867
|
userAgent: value.provider?.userAgent ?? DEFAULT_USER_AGENT,
|
|
868
|
+
providerHeaders: { ...(value.provider?.headers ?? {}) },
|
|
869
|
+
providerSessionHeaders: [...(value.provider?.sessionHeaders ?? [])],
|
|
859
870
|
language: value.language ?? 'zh',
|
|
860
871
|
timeoutMs: String(value.timeoutMs ?? 30000),
|
|
861
872
|
maxImageBytes: String(value.maxImageBytes ?? 4194304),
|
|
@@ -891,14 +902,22 @@ function apiKeyFailure(value, t) {
|
|
|
891
902
|
return undefined;
|
|
892
903
|
}
|
|
893
904
|
function valueOf(draft, t) {
|
|
905
|
+
const reasoningEffort = draft.reasoningEffort.trim();
|
|
906
|
+
if (draft.protocol === 'responses' && reasoningEffort.length > 0
|
|
907
|
+
&& (reasoningEffort.length > MAX_REASONING_EFFORT_LENGTH || !REASONING_EFFORT_PATTERN.test(reasoningEffort))) {
|
|
908
|
+
throw new Error(t('reasoningEffortInvalid'));
|
|
909
|
+
}
|
|
894
910
|
return {
|
|
895
911
|
provider: {
|
|
896
912
|
baseUrl: draft.baseUrl.trim(),
|
|
897
913
|
credential: draft.credential.trim(),
|
|
898
914
|
model: draft.model.trim(),
|
|
899
915
|
protocol: draft.protocol,
|
|
916
|
+
...(draft.protocol === 'responses' && reasoningEffort.length > 0 ? { reasoningEffort } : {}),
|
|
900
917
|
anthropicThinking: draft.anthropicThinking,
|
|
901
918
|
userAgent: draft.userAgent.trim(),
|
|
919
|
+
...(Object.keys(draft.providerHeaders).length === 0 ? {} : { headers: { ...draft.providerHeaders } }),
|
|
920
|
+
...(draft.providerSessionHeaders.length === 0 ? {} : { sessionHeaders: [...draft.providerSessionHeaders] }),
|
|
902
921
|
},
|
|
903
922
|
language: draft.language,
|
|
904
923
|
timeoutMs: positiveInteger(draft.timeoutMs, t('timeout'), t),
|
|
@@ -1157,7 +1176,7 @@ function LoadedSettings({ controller, t }) {
|
|
|
1157
1176
|
return;
|
|
1158
1177
|
void controller.applyUpdate(latestVersion);
|
|
1159
1178
|
};
|
|
1160
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "dvt-settings", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-alert notice", children: t('externalNotice') }), !snapshot.writable ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert warning", children: t('readOnly') }) : null, draftError === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert error", children: draftError }), state.error === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert error", children: state.error }), state.message === 'saved' ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('saved') }) : null, state.message === 'restarting' && state.restart !== undefined ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('restarting', { version: state.restart.toVersion }) }) : null, state.message === 'manual-restart-required' && state.restart !== undefined ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('manualRestartRequired', { version: state.restart.toVersion }) }) : null, snapshot.runtime.lastError === undefined ? null : (0, jsx_runtime_1.jsxs)("div", { className: "dvt-alert error", children: [(0, jsx_runtime_1.jsx)("strong", { children: runtimeErrorTitle }), (0, jsx_runtime_1.jsx)("span", { children: snapshot.runtime.lastError })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel dvt-essential", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('provider') }), (0, jsx_runtime_1.jsx)("p", { children: t('providerHint') })] }), (0, jsx_runtime_1.jsx)("span", { className: `dvt-badge ${snapshot.credential.configured ? 'ok' : 'error'}`, children: snapshot.credential.configured ? t('configured') : t('missing') })] }), (0, jsx_runtime_1.jsx)("p", { className: "dvt-tutorial-link", children: (0, jsx_runtime_1.jsx)("a", { href: aihubmixTutorialUrl, target: "_blank", rel: "noreferrer", children: t('aihubmixTutorial') }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('protocol'), children: (0, jsx_runtime_1.jsxs)("select", { disabled: !snapshot.writable || busy, value: draft.protocol, onChange: (event) => { update('protocol', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "openai", children: "OpenAI Chat Completions" }), (0, jsx_runtime_1.jsx)("option", { value: "anthropic", children: "Anthropic Messages" })] }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('baseUrl'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { disabled: !snapshot.writable || busy, value: draft.baseUrl, onChange: (event) => { update('baseUrl', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('model'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { disabled: !snapshot.writable || busy, value: draft.model, onChange: (event) => { update('model', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('apiKey'), hint: keyLocked ? t('apiKeyLocked') : snapshot.credential.source === undefined ? t('apiKeyHint') : `${t('apiKeyHint')} ${t('sourceHint', { source: t('source'), value: credentialSource(snapshot.credential.source, t) })}`, children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('apiKey'), type: "password", autoComplete: "new-password", disabled: busy || keyLocked, placeholder: snapshot.credential.configured ? t('apiKeyPlaceholderConfigured') : t('apiKeyPlaceholderMissing'), value: apiKey, onChange: (event) => { setApiKey(event.target.value); setDraftError(undefined); } }) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-save-row", children: [(0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "primary", disabled: !canSave || busy, onClick: save, children: state.action === 'save' ? t('saving') : t('save') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "outline", disabled: busy, onClick: () => { void controller.load(); }, children: t('reload') })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('health') }), (0, jsx_runtime_1.jsx)("p", { children: t('connectionHint') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-actions", children: [(0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('health'); }, children: state.action === 'health' ? t('testing') : t('runHealth') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('connection'); }, children: state.action === 'connection' ? t('testing') : t('testConnection') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "primary", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('model'); }, children: state.action === 'model' ? t('testingModel') : t('testModel') })] })] }), (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('saveBeforeTesting') }), state.health === undefined ? (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('notTested') }) : (0, jsx_runtime_1.jsx)("div", { className: "dvt-health-grid", children: Object.entries(state.health.checks).map(([name, check]) => {
|
|
1179
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "dvt-settings", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-alert notice", children: t('externalNotice') }), !snapshot.writable ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert warning", children: t('readOnly') }) : null, draftError === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert error", children: draftError }), state.error === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert error", children: state.error }), state.message === 'saved' ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('saved') }) : null, state.message === 'restarting' && state.restart !== undefined ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('restarting', { version: state.restart.toVersion }) }) : null, state.message === 'manual-restart-required' && state.restart !== undefined ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('manualRestartRequired', { version: state.restart.toVersion }) }) : null, snapshot.runtime.lastError === undefined ? null : (0, jsx_runtime_1.jsxs)("div", { className: "dvt-alert error", children: [(0, jsx_runtime_1.jsx)("strong", { children: runtimeErrorTitle }), (0, jsx_runtime_1.jsx)("span", { children: snapshot.runtime.lastError })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel dvt-essential", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('provider') }), (0, jsx_runtime_1.jsx)("p", { children: t('providerHint') })] }), (0, jsx_runtime_1.jsx)("span", { className: `dvt-badge ${snapshot.credential.configured ? 'ok' : 'error'}`, children: snapshot.credential.configured ? t('configured') : t('missing') })] }), (0, jsx_runtime_1.jsx)("p", { className: "dvt-tutorial-link", children: (0, jsx_runtime_1.jsx)("a", { href: aihubmixTutorialUrl, target: "_blank", rel: "noreferrer", children: t('aihubmixTutorial') }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('protocol'), children: (0, jsx_runtime_1.jsxs)("select", { disabled: !snapshot.writable || busy, value: draft.protocol, onChange: (event) => { update('protocol', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "openai", children: "OpenAI Chat Completions" }), (0, jsx_runtime_1.jsx)("option", { value: "responses", children: "OpenAI Responses" }), (0, jsx_runtime_1.jsx)("option", { value: "anthropic", children: "Anthropic Messages" })] }) }), draft.protocol === 'responses' ? (0, jsx_runtime_1.jsx)(Field, { label: t('reasoningEffort'), hint: t('reasoningEffortHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('reasoningEffort'), disabled: !snapshot.writable || busy, placeholder: "none / minimal / low / medium / high / xhigh", value: draft.reasoningEffort, onChange: (event) => { update('reasoningEffort', event.target.value); setDraftError(undefined); } }) }) : null, (0, jsx_runtime_1.jsx)(Field, { label: t('baseUrl'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { disabled: !snapshot.writable || busy, value: draft.baseUrl, onChange: (event) => { update('baseUrl', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('model'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { disabled: !snapshot.writable || busy, value: draft.model, onChange: (event) => { update('model', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('apiKey'), hint: keyLocked ? t('apiKeyLocked') : snapshot.credential.source === undefined ? t('apiKeyHint') : `${t('apiKeyHint')} ${t('sourceHint', { source: t('source'), value: credentialSource(snapshot.credential.source, t) })}`, children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('apiKey'), type: "password", autoComplete: "new-password", disabled: busy || keyLocked, placeholder: snapshot.credential.configured ? t('apiKeyPlaceholderConfigured') : t('apiKeyPlaceholderMissing'), value: apiKey, onChange: (event) => { setApiKey(event.target.value); setDraftError(undefined); } }) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-save-row", children: [(0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "primary", disabled: !canSave || busy, onClick: save, children: state.action === 'save' ? t('saving') : t('save') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "outline", disabled: busy, onClick: () => { void controller.load(); }, children: t('reload') })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('health') }), (0, jsx_runtime_1.jsx)("p", { children: t('connectionHint') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-actions", children: [(0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('health'); }, children: state.action === 'health' ? t('testing') : t('runHealth') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('connection'); }, children: state.action === 'connection' ? t('testing') : t('testConnection') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "primary", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('model'); }, children: state.action === 'model' ? t('testingModel') : t('testModel') })] })] }), (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('saveBeforeTesting') }), state.health === undefined ? (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('notTested') }) : (0, jsx_runtime_1.jsx)("div", { className: "dvt-health-grid", children: Object.entries(state.health.checks).map(([name, check]) => {
|
|
1161
1180
|
const testTag = name === 'model' ? modelTestTag(state.health, check) : undefined;
|
|
1162
1181
|
return (0, jsx_runtime_1.jsxs)("div", { "data-status": check.status, children: [(0, jsx_runtime_1.jsx)("span", { children: t(HEALTH_NAME_KEYS[name] ?? 'health') }), testTag === undefined ? null : (0, jsx_runtime_1.jsx)("em", { className: "dvt-health-test-tag", "data-status": testTag.status, children: t(testTag.label) }), (0, jsx_runtime_1.jsx)("strong", { children: t(HEALTH_STATUS_KEYS[check.status]) }), (0, jsx_runtime_1.jsx)("p", { children: healthDetail(name, check.detail, t) })] }, name);
|
|
1163
1182
|
}) })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel dvt-update-panel", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('updates') }), (0, jsx_runtime_1.jsx)("p", { children: t('updatesHint') })] }), (0, jsx_runtime_1.jsx)("span", { className: `dvt-badge ${pluginUpdate?.updateAvailable ? 'warning' : pluginUpdate !== undefined && pluginUpdate.supported ? 'ok' : ''}`, children: pluginUpdate?.updateAvailable ? t('updateAvailable') : pluginUpdate !== undefined && pluginUpdate.supported ? t('upToDate') : t('pluginVersion') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-update-grid", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { children: t('updateInstalled') }), (0, jsx_runtime_1.jsx)("strong", { children: snapshot.release.pluginVersion })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { children: t('updateLatest') }), (0, jsx_runtime_1.jsx)("strong", { children: latestVersion ?? '—' })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { children: t('updateProfile') }), (0, jsx_runtime_1.jsx)("strong", { children: updateCapability.profile ?? '—' })] })] }), !updateCapability.supported ? (0, jsx_runtime_1.jsxs)("div", { className: "dvt-alert warning", children: [(0, jsx_runtime_1.jsx)("strong", { children: t('updateUnsupported') }), (0, jsx_runtime_1.jsx)("span", { children: updateReason })] }) : null, updateCapability.supported && updateHasUnsavedChanges ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert warning", children: t('updateSaveFirst') }) : null, pluginUpdate?.supported && pluginUpdate.updateAvailable && latestVersion !== undefined ? (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('updateAvailableDetail', { version: latestVersion }) }) : null, pluginUpdate?.supported && !pluginUpdate.updateAvailable && latestVersion !== undefined ? (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('upToDateDetail', { version: latestVersion }) }) : null, (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('manualUpdateHint') }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-manual-update", children: [(0, jsx_runtime_1.jsx)("code", { children: manualUpdateCommand }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", onClick: copyManualUpdate, children: copiedCommand ? t('copied') : t('copy') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-actions", children: [(0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "outline", disabled: busy || !updateCheckSupported || state.restart !== undefined, onClick: () => { void controller.checkUpdate(); }, children: state.action === 'check-update' ? t('checkingUpdate') : t('checkUpdate') }), pluginUpdate?.supported && pluginUpdate.updateAvailable && latestVersion !== undefined ? (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "primary", disabled: busy || state.restart !== undefined || updateHasUnsavedChanges, onClick: applyUpdate, children: state.action === 'apply-update' ? t('updatingPlugin') : t('updateNow') }) : null] })] }), (0, jsx_runtime_1.jsxs)("details", { className: "dvt-advanced", children: [(0, jsx_runtime_1.jsxs)("summary", { children: [(0, jsx_runtime_1.jsxs)("span", { children: [(0, jsx_runtime_1.jsx)("strong", { children: t('advanced') }), (0, jsx_runtime_1.jsx)("small", { children: t('advancedHint') })] }), (0, jsx_runtime_1.jsx)("span", { className: "dvt-details-chevron", "aria-hidden": "true", children: "\u2304" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-advanced-body", children: [(0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsx)("h3", { children: t('provider') }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('credential'), hint: t('credentialHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('credential'), disabled: !snapshot.writable || busy, value: draft.credential, onChange: (event) => { update('credential', event.target.value); } }) }), draft.protocol === 'anthropic' ? (0, jsx_runtime_1.jsx)(Field, { label: t('anthropicThinking'), hint: t('anthropicThinkingHint'), children: (0, jsx_runtime_1.jsxs)("select", { "aria-label": t('anthropicThinking'), value: draft.anthropicThinking, onChange: (event) => { update('anthropicThinking', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "omit", children: "omit (widest compatibility)" }), (0, jsx_runtime_1.jsx)("option", { value: "disabled", children: "disabled (model support required)" }), (0, jsx_runtime_1.jsx)("option", { value: "adaptive", children: "adaptive (model support required)" })] }) }) : null, (0, jsx_runtime_1.jsx)(Field, { label: t('userAgent'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { value: draft.userAgent, onChange: (event) => { update('userAgent', event.target.value); } }) })] })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsx)("h3", { children: t('limits') }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('language'), children: (0, jsx_runtime_1.jsxs)("select", { value: draft.language, onChange: (event) => { update('language', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "zh", children: "\u4E2D\u6587" }), (0, jsx_runtime_1.jsx)("option", { value: "en", children: "English" })] }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('timeout'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { inputMode: "numeric", value: draft.timeoutMs, onChange: (event) => { update('timeoutMs', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('maxBytes'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { inputMode: "numeric", value: draft.maxImageBytes, onChange: (event) => { update('maxImageBytes', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('maxPixels'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { inputMode: "numeric", value: draft.maxImagePixels, onChange: (event) => { update('maxImagePixels', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('concurrency'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { inputMode: "numeric", value: draft.concurrency, onChange: (event) => { update('concurrency', event.target.value); } }) })] })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsx)("h3", { children: t('imageInput') }) }), (0, jsx_runtime_1.jsxs)("label", { className: "dvt-check", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: draft.hiddenVariants, disabled: !snapshot.writable || busy, onChange: (event) => { update('hiddenVariants', event.target.checked); } }), (0, jsx_runtime_1.jsx)("span", { children: t('hiddenVariantsLabel') }), (0, jsx_runtime_1.jsx)("small", { children: t('hiddenVariantsHint') })] })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsx)("h3", { children: t('storage') }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('storageDir'), hint: t('storageDirHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('storageDir'), placeholder: "/tmp/dsh-vision-toolkit", value: draft.storageDir, onChange: (event) => { update('storageDir', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('allowedDirs'), hint: t('allowedDirsHint'), children: (0, jsx_runtime_1.jsx)("textarea", { rows: 3, value: draft.allowedDirs, onChange: (event) => { update('allowedDirs', event.target.value); } }) })] })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsx)("h3", { children: t('runtime') }), (0, jsx_runtime_1.jsx)("span", { className: `dvt-badge ${snapshot.runtime.ready ? 'ok' : 'error'}`, children: snapshot.runtime.ready ? snapshot.runtime.upstream?.source === 'managed' ? t('runtimeManaged') : snapshot.runtime.upstream?.source === 'external' ? t('runtimeExternal') : t('runtimeReady') : t('runtimeUnavailable') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('runtimeMode'), children: (0, jsx_runtime_1.jsxs)("select", { value: draft.runtimeMode, onChange: (event) => { update('runtimeMode', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "managed", children: t('runtimeManaged') }), (0, jsx_runtime_1.jsx)("option", { value: "external", children: t('runtimeExternal') })] }) }), draft.runtimeMode === 'external' ? (0, jsx_runtime_1.jsx)(Field, { label: t('toolkitPath'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { value: draft.toolkitPath, onChange: (event) => { update('toolkitPath', event.target.value); } }) }) : null, (0, jsx_runtime_1.jsx)(Field, { label: t('python'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { placeholder: "python3", value: draft.python, onChange: (event) => { update('python', event.target.value); } }) })] }), snapshot.runtime.upstream === undefined ? null : (0, jsx_runtime_1.jsxs)("div", { className: "dvt-runtime-facts", children: [(0, jsx_runtime_1.jsx)("code", { children: snapshot.runtime.upstream.path }), (0, jsx_runtime_1.jsxs)("code", { children: [snapshot.runtime.upstream.python, " \u00B7 ", snapshot.runtime.upstream.pythonVersion] }), (0, jsx_runtime_1.jsx)("code", { children: snapshot.runtime.upstream.runtimeHome })] })] })] })] }), (0, jsx_runtime_1.jsxs)("footer", { className: "dvt-settings-footer", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { className: "dvt-kicker", children: t('pluginKind') }), (0, jsx_runtime_1.jsx)("h2", { children: t('settingsTitle') }), (0, jsx_runtime_1.jsx)("p", { children: t('settingsIntro') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-release", children: [(0, jsx_runtime_1.jsxs)("span", { children: [t('pluginVersion'), " ", (0, jsx_runtime_1.jsx)("strong", { children: snapshot.release.pluginVersion })] }), (0, jsx_runtime_1.jsxs)("span", { children: [t('upstreamVersion'), " ", (0, jsx_runtime_1.jsx)("strong", { children: snapshot.release.upstreamVersion })] }), (0, jsx_runtime_1.jsxs)("span", { children: [t('activeGeneration'), " ", (0, jsx_runtime_1.jsx)("strong", { children: t('activeGenerationValue', { generation: snapshot.runtime.generation }) })] })] })] })] }));
|