@anionex/dsh-vision-toolkit 0.1.37 → 0.1.39

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/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: f38885c9ef8b326808f9a43c8433e9e6a5c929db
6
- README.zh.md: 06e3133487ea5465a8c81e04a1a98308217c8432
5
+ README.md: 00b2ae0fd8b991b7723a4fbaa16018732959577f
6
+ README.zh.md: 1fca5b629ea5f062df6acd7d35ce5abfc913f864
package/README.md CHANGED
@@ -18,7 +18,7 @@
18
18
 
19
19
  **A more powerful vision toolkit—give text-only models in DeepSeek Harness eyes: image Q&A, long-screenshot OCR, UI restoration, and GUI visual tasks in one toolkit and Skill.**
20
20
 
21
- 🚀 Paste an image and ask directly | Install with one command | Built-in free vision | Broad use cases
21
+ 🚀 Paste an image and ask directly | Install with one command | Broad use cases
22
22
 
23
23
  [Highlights](#highlights) | [Quick start](#quick-start-three-steps) | [Toolbox](#toolbox) | [Configuration and limits](#configuration-and-limits) | [Troubleshooting](#troubleshooting) | [Community](#development-and-community)
24
24
 
@@ -35,8 +35,7 @@
35
35
  ## Highlights
36
36
 
37
37
  - **Paste an image and ask directly.** In DSH Web, pasting an image switches the text-only model to its `(Vision Toolkit)` variant automatically — no manual path copying or model changes. Native thumbnails, session history, and workspace paths stay intact; Web can preview artifacts.
38
- - **One command to install.** The built-in free Gemini 3.7 Flash vision service is ready after installation, with no API key required.
39
- - **Built-in free vision quota.** The shared service works immediately after installation with a quota of **100 images per machine per day**.
38
+ - **One command to install.** After installation, configure a vision provider in **Settings → Vision Toolkit** and start using the tools.
40
39
  - **Not just a caption — the content that matters.** The model does not produce a generic description; it extracts evidence around the current task, such as “Where is the error?” or “Where is the button?”.
41
40
  - **A battle-tested visual-task methodology.** The bundled Skill tells the agent what to look at for different visual tasks, which tool to choose, how to proceed, and how to verify the result.
42
41
 
@@ -45,9 +44,7 @@
45
44
  This project has two layers:
46
45
 
47
46
  1. **Visual tools and a Skill:** the agent learns when to inspect, ground, OCR, crop, trace, or compare pixels.
48
- 2. **Native DSH integration:** those capabilities live inside Profiles, sessions, Settings, Artifacts, and the Web UI, with a free Gemini 3.7 Flash vision service ready after installation.
49
-
50
- > **Install and use it immediately.** The default setup includes a free Gemini 3.7 Flash vision service and requires no API key.
47
+ 2. **Native DSH integration:** those capabilities live inside Profiles, sessions, Settings, Artifacts, and the Web UI.
51
48
 
52
49
  ```sh
53
50
  dsh plugin --profile web add @anionex/dsh-vision-toolkit
@@ -64,8 +61,8 @@ dsh plugin --profile web add @anionex/dsh-vision-toolkit
64
61
 
65
62
  <table>
66
63
  <tr>
67
- <td width="220"><a href="https://inferera.com/?aff=5wj6sgx8"><img src="assets/logo_aihubmix.png" alt="AIHubMix" height="48"></a></td>
68
- <td>Thanks to <a href="https://inferera.com/?aff=5wj6sgx8">AIHubMix</a> for sponsoring this project! AIHubMix is a stable, high-concurrency AI model API gateway that connects Claude, GPT, Gemini, DeepSeek, and other mainstream models through a single API key, compatible with multiple protocols, with <b>free model options</b> available. Use the <a href="https://inferera.com/?aff=5wj6sgx8">Inferera entry</a> to sign up.</td>
64
+ <td width="220"><a href="https://aihubmix.com/?aff=5wj6sgx8"><img src="assets/logo_aihubmix.png" alt="AIHubMix" height="48"></a></td>
65
+ <td>Thanks to <a href="https://aihubmix.com/?aff=5wj6sgx8">AIHubMix</a> for sponsoring this project! AIHubMix is a stable, high-concurrency AI model API gateway that connects Claude, GPT, Gemini, DeepSeek, and other mainstream models through a single API key, compatible with multiple protocols, with <b>free model options</b> available. To sign up, use the <a href="https://aihubmix.com/?aff=5wj6sgx8">AIHubMix entry</a> outside mainland China or the <a href="https://inferera.com/?aff=5wj6sgx8">Inferera entry</a> within mainland China.</td>
69
66
  </tr>
70
67
  </table>
71
68
 
@@ -86,12 +83,12 @@ dsh plugin --profile web add @anionex/dsh-vision-toolkit
86
83
 
87
84
  ## Recent updates
88
85
 
89
- - **2026-08-20 · AIHubMix setup guide:** Added a screenshot-based guide for getting an API key through the Inferera entry and using the free Gemini 3.7 Flash vision model; Settings now links directly to this guide.
86
+ - **2026-08-20 · AIHubMix setup guide:** Added a screenshot-based guide for getting an API key through the Inferera entry and configuring the Gemini 3.7 Flash vision model; Settings now links directly to this guide.
90
87
  - **2026-08-19 · Transparent routing by default:** The model selector keeps one entry per model with the original name, and image input (paste, history, `read_image`) works without manually switching to a `(Vision Toolkit)` variant. Disable “Transparent variant routing” in advanced settings → image input to restore the explicit entries.
91
88
  - **2026-08-16 · Windows Python:** Added Microsoft Store Python support, fixing first-time isolated-runtime setup failures for affected Windows users.
92
- - **2026-08-17 · Free vision upgrade:** Switched the built-in no-key service to Gemini 3.7 Flash and fixed Qwen/Gemini bounding-box coordinate order.
89
+ - **2026-08-17 · Vision upgrade:** Switched the default model to Gemini 3.7 Flash and fixed Qwen/Gemini bounding-box coordinate order.
93
90
  - **2026-08-16 · Image paste:** Text-only routes now switch to a `(Vision Toolkit)` variant and keep a workspace path, fixing blocked pastes and images that could not be reused later.
94
- - **2026-08-16 · More shared capacity:** Expanded the free service capacity to reduce peak-time `429` responses.
91
+ - **2026-08-16 · Service stability:** Expanded service capacity to reduce peak-time `429` responses.
95
92
  - **2026-08-16 · Real model test:** Added a full image-request test in Settings, fixing the false confidence caused by a successful `/models` request to a model that still cannot process images.
96
93
 
97
94
  ## Who it is for
@@ -178,7 +175,7 @@ For the full Desktop install, update, and troubleshooting walkthrough, see [Inst
178
175
 
179
176
  ### 2. Restart and check it
180
177
 
181
- Restart a running Web Profile, then open **Settings → Vision Toolkit**. The free provider is already configured; run **Test vision model** to confirm it is reachable.
178
+ Restart a running Web Profile, then open **Settings → Vision Toolkit**, configure a vision provider, and run **Test vision model** to confirm it is reachable.
182
179
 
183
180
  The first start prepares an isolated runtime: the plugin prefers a system Python 3.11+; when none is found, it downloads a hash-verified standalone Python (about 35 MB) from the domestic mirror (`dsh-vision-python-bootstrap-1317715800.cos.ap-guangzhou.myqcloud.com`) on first use, falling back to the GitHub release when the mirror is unreachable. The locked runtime dependencies (Pillow, NumPy, vtracer) are installed from the Tencent Cloud PyPI mirror (`mirrors.cloud.tencent.com/pypi/simple`) first and fall back to the official PyPI index. A normal installation does not require an `agent-vision-toolkit` source checkout or a local path setting.
184
181
 
@@ -255,37 +252,11 @@ For routes that DSH positively identifies as text-only, the plugin registers a s
255
252
 
256
253
  ## Configuration and limits
257
254
 
258
- ### Built-in free service
259
-
260
- The default setup uses:
261
-
262
- ```text
263
- Base URL: https://vision.anionex.me/v1
264
- Model: gemini-3.7-flash
265
- API Key: https://agent-vision.anionex.me (filled automatically)
266
- ```
267
-
268
- Requests that still use the previous `qwen/qwen3.6-27b` model name remain compatible and are routed to the Qwen backend.
269
-
270
- This is a shared zero-configuration entry point, not an unlimited private endpoint. Request safeguards include:
271
-
272
- | Limit | Current value |
273
- | --- | --- |
274
- | Daily quota | 100 images per machine per day |
275
- | Images per request | Up to 5 |
276
- | Image size | 4 MiB per image |
277
- | Decoded pixels | 20,000,000 per image |
278
- | Output | Up to 4,096 tokens per request |
279
-
280
- These safeguards prevent unusually large requests from monopolizing memory or request time. When shared capacity is reached, the service returns a readable `429` response with `Retry-After` instead of collapsing into an unexplained model failure.
281
-
282
- Existing clients that still send `api_key="free"` remain compatible.
283
-
284
- ### Bring your own vision model
255
+ ### Configure a vision model
285
256
 
286
- For higher quotas, private endpoints, or another model, change the provider in **Settings → Vision Toolkit** and store the API key as a DSH Credential. Settings stores the Credential reference and never reads the saved secret back into the browser.
257
+ Configure the vision provider in **Settings → Vision Toolkit** and store the API key as a DSH Credential. Settings stores the Credential reference and never reads the saved secret back into the browser.
287
258
 
288
- **Step-by-step AIHubMix tutorial:** [Get an AIHubMix API key and use free Gemini 3.7 Flash for vision](docs/aihubmix-gemini-vision.md). It includes screenshots for account/API-key setup, the exact Vision Toolkit settings, free-model selection, and troubleshooting.
259
+ **Step-by-step AIHubMix tutorial:** [Get an AIHubMix API key and configure Gemini 3.7 Flash for vision](docs/aihubmix-gemini-vision.md). It includes screenshots for account/API-key setup, the exact Vision Toolkit settings, model selection, and troubleshooting.
289
260
 
290
261
  You can also configure a Profile patch:
291
262
 
@@ -315,7 +286,7 @@ For advanced setups — overriding `runtime.python`, using `runtime.mode: extern
315
286
  | --- | --- |
316
287
  | 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`, and a port-only address hits an unknown endpoint and returns this error |
317
288
  | 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` |
318
- | The free service returns 429 | Wait for the `Retry-After` interval, or switch to your own endpoint when you need stable higher volume |
289
+ | The vision service returns 429 | Wait for the `Retry-After` interval, or switch to your own endpoint when you need stable higher volume |
319
290
  | The image exceeds a size or pixel limit | Crop or resize it first; the error identifies whether bytes or decoded pixels caused the rejection |
320
291
  | A custom Credential is missing | Enter the API key in **Settings → Vision Toolkit** and confirm the Credential name matches the provider configuration |
321
292
  | 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 |
package/README.zh.md CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  **更强大的视觉工具箱——给 DeepSeek Harness 里的纯文本模型装上眼睛:图片问答、长图 OCR、前端 UI 还原、GUI 视觉任务,一套视觉工具箱和一个 Skill。**
19
19
 
20
- 🚀 粘贴图片,直接提问 | 一行命令安装即用 | 内置免费视觉 | 场景丰富
20
+ 🚀 粘贴图片,直接提问 | 一行命令安装即用 | 场景丰富
21
21
 
22
22
  [亮点](#亮点) | [快速开始](#快速开始三步完成) | [工具一览](#工具一览) | [配置与限制](#配置与限制) | [常见问题](#常见问题) | [交流群](#开发与社区)
23
23
 
@@ -34,8 +34,7 @@
34
34
  ## 亮点
35
35
 
36
36
  - **粘贴图片,直接提问。** 在 DSH Web 里粘贴图片,文本模型会自动切换到看图模式变体,不需要手动复制路径或更换模型。图片保留原生缩略图、会话记录和工作区路径;Web 可以预览产物。
37
- - **一行命令安装即用。** 安装插件后默认使用内置免费 Gemini 3.7 Flash 视觉服务,不需要申请 API Key。
38
- - **内置免费视觉模型额度。** 安装后即可直接使用共享服务,每台机器每天有**100 张图**的免费额度。
37
+ - **一行命令安装即用。** 安装插件后即可在 **设置 → 视觉工具** 中配置视觉模型并开始使用。
39
38
  - **不只是看图描述,是获取图中真正需要关注的内容。** 模型不只是生成通用描述,而是围绕“报错在哪里”“按钮在哪”等当前任务提取证据。
40
39
  - **一套经过实战验证的视觉任务方法论**:项目提供的skill,会告诉 agent 面对不同视觉任务时应该看什么、选择哪个工具、按什么步骤推进,以及最后如何验证结果。
41
40
 
@@ -46,8 +45,6 @@
46
45
  1. **视觉工具和 Skill**:让 Agent 知道什么时候该看图、定位、OCR、裁剪、描摹或做像素对比。
47
46
  2. **DSH 原生接入**:把这些能力放进 Profile、会话、Settings、Artifacts 和 Web 界面。
48
47
 
49
- > **安装即可使用。** 默认接入内置免费 Gemini 3.7 Flash 视觉服务,不需要申请 API Key。
50
-
51
48
  ```sh
52
49
  dsh plugin --profile web add @anionex/dsh-vision-toolkit
53
50
  ```
@@ -63,8 +60,8 @@ dsh plugin --profile web add @anionex/dsh-vision-toolkit
63
60
 
64
61
  <table>
65
62
  <tr>
66
- <td width="220"><a href="https://inferera.com/?aff=5wj6sgx8"><img src="assets/logo_aihubmix.png" alt="AIHubMix" height="48"></a></td>
67
- <td>感谢 <a href="https://inferera.com/?aff=5wj6sgx8">AIHubMix</a> 赞助本项目!AIHubMix 是稳定、高并发的 AI 大模型 API 聚合平台,一个 API Key 即可接入 Claude、GPT、Gemini、DeepSeek 等主流模型,兼容多种协议,并提供<b>免费模型选择</b>。注册时请使用 <a href="https://inferera.com/?aff=5wj6sgx8">Inferera 入口</a>。</td>
63
+ <td width="220"><a href="https://aihubmix.com/?aff=5wj6sgx8"><img src="assets/logo_aihubmix.png" alt="AIHubMix" height="48"></a></td>
64
+ <td>感谢 <a href="https://aihubmix.com/?aff=5wj6sgx8">AIHubMix</a> 赞助本项目!AIHubMix 是稳定、高并发的 AI 大模型 API 聚合平台,一个 API Key 即可接入 Claude、GPT、Gemini、DeepSeek 等主流模型,兼容多种协议,并提供<b>免费模型选择</b>。注册时,海外用户请使用 <a href="https://aihubmix.com/?aff=5wj6sgx8">AIHubMix 入口</a>,中国大陆用户请使用 <a href="https://inferera.com/?aff=5wj6sgx8">Inferera 入口</a>。</td>
68
65
  </tr>
69
66
  </table>
70
67
 
@@ -85,12 +82,12 @@ dsh plugin --profile web add @anionex/dsh-vision-toolkit
85
82
 
86
83
  ## 最近更新
87
84
 
88
- - **2026-08-20 · AIHubMix 申请教程:** 新增通过 Inferera 入口申请 API Key、使用免费 Gemini 3.7 Flash 视觉模型的图文教程,并在视觉工具设置中直接提供入口。
85
+ - **2026-08-20 · AIHubMix 申请教程:** 新增通过 Inferera 入口申请 API Key 并配置 Gemini 3.7 Flash 视觉模型的图文教程,并在视觉工具设置中直接提供入口。
89
86
  - **2026-08-19 · 透明变体路由默认开启:** 模型选择器默认只显示每个模型一项并保留原模型名,粘贴图片、历史图片和内置 `read_image` 工具都能直接使用,不再需要手动切换到 `(Vision Toolkit)` 变体;如需恢复显式条目,可在 设置 → 高级设置 → 图片输入 关闭“透明变体路由”。
90
87
  - **2026-08-16 · Windows Python:** 支持 Microsoft Store Python,解决 Windows 用户首次创建隔离环境失败的问题。
91
- - **2026-08-17 · 免费视觉升级:** 默认模型切换到 Gemini 3.7 Flash,并修复 Qwen/Gemini 检测框坐标顺序错位的问题。
88
+ - **2026-08-17 · 视觉升级:** 默认模型切换到 Gemini 3.7 Flash,并修复 Qwen/Gemini 检测框坐标顺序错位的问题。
92
89
  - **2026-08-16 · 图片粘贴:** 文本模型自动切换到 `(Vision Toolkit)` 变体并保留工作区路径,解决粘贴图片被拦截或后续无法复用的问题。
93
- - **2026-08-16 · 共享容量:** 扩大免费服务容量,减少高峰期出现 `429` 的情况。
90
+ - **2026-08-16 · 服务稳定性:** 扩大服务容量,减少高峰期出现 `429` 的情况。
94
91
  - **2026-08-16 · 真实模型测试:** Settings 新增完整图片请求测试,解决 `/models` 可访问却不能证明模型真的会看图的问题。
95
92
 
96
93
  ## 适合谁用
@@ -178,7 +175,7 @@ dsh plugin --profile desktop add @anionex/dsh-vision-toolkit
178
175
 
179
176
  ### 2. 重启并确认
180
177
 
181
- 重启正在运行的 Web Profile,打开 **设置 → 视觉工具**。默认免费服务已经配置好;你可以直接运行**测试视觉模型**确认连接。
178
+ 重启正在运行的 Web Profile,打开 **设置 → 视觉工具**,配置视觉模型,然后运行**测试视觉模型**确认连接。
182
179
 
183
180
  首次启动会自动准备隔离运行环境:插件优先使用系统已有的 Python 3.11+;如果系统没有,会自动从国内镜像(`dsh-vision-python-bootstrap-1317715800.cos.ap-guangzhou.myqcloud.com`)下载一个带完整性校验的托管 Python(约 35MB,仅首次需要网络),镜像不可用时自动回退到 GitHub 官方发布源。锁定依赖(Pillow、NumPy、vtracer)会优先从腾讯云 PyPI 镜像(`mirrors.cloud.tencent.com/pypi/simple`)安装,镜像不可用时回退到官方 PyPI。普通安装不需要下载 `agent-vision-toolkit` 源码,也不需要设置本地路径。
184
181
 
@@ -253,37 +250,11 @@ flowchart LR
253
250
 
254
251
  ## 配置与限制
255
252
 
256
- ### 默认免费服务
257
-
258
- 默认配置使用:
259
-
260
- ```text
261
- Base URL: https://vision.anionex.me/v1
262
- Model: gemini-3.7-flash
263
- API Key: https://agent-vision.anionex.me(自动填写)
264
- ```
265
-
266
- 仍然使用旧模型名 `qwen/qwen3.6-27b` 的请求保持兼容,会自动路由到 Qwen 后端。
267
-
268
- 这是共享的免费入口,不是无限量私有服务。请求保护规则包括:
269
-
270
- | 限制 | 当前值 |
271
- | --- | --- |
272
- | 每日额度 | 每台机器每天 100 张图 |
273
- | 单次请求图片数 | 最多 5 张 |
274
- | 单张图片大小 | 4 MiB |
275
- | 单张图片像素 | 20,000,000 |
276
- | 单次输出 | 最多 4,096 tokens |
277
-
278
- 这些保护规则避免异常大的请求占满内存或请求时间。共享容量用尽时,服务会返回带 `Retry-After` 的明确 `429` 响应,不会只得到一个含糊的“模型失败”。
279
-
280
- 仍然发送 `api_key="free"` 的旧客户端可以继续使用。
281
-
282
- ### 使用自己的视觉模型
253
+ ### 配置视觉模型
283
254
 
284
- 如果你需要更高额度、私有端点或其他模型,可以在 **设置 → 视觉工具** 中修改提供方,并把 API Key 保存为 DSH Credential。Settings 只保存 Credential 引用,不会回显密钥。
255
+ 在 **设置 → 视觉工具** 中配置视觉模型提供方,并把 API Key 保存为 DSH Credential。Settings 只保存 Credential 引用,不会回显密钥。
285
256
 
286
- **AIHubMix 图文教程:** [申请 AIHubMix API Key,并用免费 Gemini 3.7 Flash 识图](docs/aihubmix-gemini-vision.zh.md)。教程包含账号与 API Key 获取截图、Vision Toolkit 的准确配置、免费模型选择和常见问题排查。
257
+ **AIHubMix 图文教程:** [申请 AIHubMix API Key 并配置 Gemini 3.7 Flash 识图](docs/aihubmix-gemini-vision.zh.md)。教程包含账号与 API Key 获取截图、Vision Toolkit 的准确配置、模型选择和常见问题排查。
287
258
 
288
259
  也可以在 Profile patch 中配置:
289
260
 
@@ -313,7 +284,7 @@ API Key: https://agent-vision.anionex.me(自动填写)
313
284
  | --- | --- |
314
285
  | 视觉模型测试失败:`Vision API returned an incompatible response structure` | 通常是 API 地址少了路径前缀。LM Studio、Ollama 等本地 OpenAI 兼容服务需填写 `http://127.0.0.1:1234/v1`(带 `/v1`),插件会在其后拼接 `/chat/completions`;只填端口号会命中服务的未知端点并返回该错误 |
315
286
  | 粘贴图片后仍提示模型不支持图片 | 重启 Web Profile 并刷新页面,确认当前模型已切换到带 `(Vision Toolkit)` 的变体;也可以把图片先放进会话工作区,再调用 `/vision-skills` |
316
- | 免费服务提示 429 | 按错误中的 `Retry-After` 等待后重试;如果需要稳定高额度,切换到自己的视觉端点 |
287
+ | 视觉服务提示 429 | 按错误中的 `Retry-After` 等待后重试;如果需要稳定高额度,切换到自己的视觉端点 |
317
288
  | 图片过大或像素超限 | 先裁剪或缩放图片;错误会明确显示是字节还是像素限制 |
318
289
  | 自定义 Credential 缺失 | 在 **设置 → 视觉工具** 填写 API Key,并确认 Credential 名称与配置一致 |
319
290
  | 首次运行时准备失败 | 自动下载托管 Python 需要网络和磁盘权限(默认先走国内镜像,失败时回退 GitHub);失败时检查网络或包缓存,也可以安装 Python 3.11+ 或在 Settings 中配置 `runtime.python`,然后重新测试 |
@@ -0,0 +1,350 @@
1
+ /** Durable, Session-scoped cache for image descriptions used by model variants. */
2
+ import { createHash } from 'node:crypto';
3
+ import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain';
4
+ import { z } from 'zod';
5
+ import { UPSTREAM_COMMIT } from "./version.js";
6
+ /** Bump only when the model-visible evidence contract changes incompatibly. */
7
+ export const EVIDENCE_CONTRACT_VERSION = 1;
8
+ /** Persistent cache bounds keep the Profile storage proportional and predictable. */
9
+ const DEFAULT_PERSISTED_ENTRY_LIMIT = 512;
10
+ const DEFAULT_PERSISTED_BYTE_LIMIT = 8 * 1024 * 1024;
11
+ const DEFAULT_PERSISTED_ENTRY_BYTE_LIMIT = 64 * 1024;
12
+ const MAX_SCHEMA_TEXT_CHARS = 256 * 1024;
13
+ const MAX_SCHEMA_RECORD_CHARS = 512 * 1024;
14
+ const hexDigestSchema = z.string().regex(/^[0-9a-f]{64}$/u);
15
+ const sessionIdentitySchema = z.object({
16
+ createdAt: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
17
+ cwd: z.string().optional(),
18
+ });
19
+ const evidenceRecordSchema = z.object({
20
+ contractVersion: z.number().int().nonnegative(),
21
+ sessionId: z.string().min(1),
22
+ session: sessionIdentitySchema,
23
+ attachmentId: z.string().min(1),
24
+ promptHash: hexDigestSchema,
25
+ runtimeHash: hexDigestSchema,
26
+ text: z.string().min(1).max(MAX_SCHEMA_TEXT_CHARS),
27
+ storedAt: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
28
+ });
29
+ /** Plugin-owned sidecar domain; the host backend handles atomicity and file safety. */
30
+ export const evidenceCacheDomainSpec = defineDomain({
31
+ name: 'vision_toolkit_evidence',
32
+ version: 0,
33
+ tables: {
34
+ // Keep the durable boundary tolerant of one damaged cache payload: each
35
+ // record is parsed and validated independently below, so corruption causes
36
+ // a miss instead of preventing the entire optional domain from opening.
37
+ evidence: domainTable(z.string().max(MAX_SCHEMA_RECORD_CHARS)),
38
+ },
39
+ });
40
+ function hash(value) {
41
+ return createHash('sha256').update(value).digest('hex');
42
+ }
43
+ /** Fingerprint every runtime setting that can change the generated description. */
44
+ export function evidenceRuntimeFingerprint(config, credentialSha256, sslVerify) {
45
+ return hash(JSON.stringify({
46
+ upstreamCommit: UPSTREAM_COMMIT,
47
+ provider: {
48
+ baseUrl: config.provider.baseUrl,
49
+ credential: {
50
+ ref: String(config.provider.credential),
51
+ sha256: credentialSha256 ?? null,
52
+ },
53
+ model: config.provider.model,
54
+ protocol: config.provider.protocol,
55
+ anthropicThinking: config.provider.anthropicThinking,
56
+ sslVerify: sslVerify ?? null,
57
+ userAgent: config.provider.userAgent,
58
+ },
59
+ language: config.language,
60
+ timeoutMs: config.timeoutMs,
61
+ concurrency: config.concurrency,
62
+ maxImageBytes: config.maxImageBytes,
63
+ maxImagePixels: config.maxImagePixels,
64
+ runtime: config.runtime,
65
+ }));
66
+ }
67
+ /** Build a non-secret cache key from the Session, attachment, focus, and runtime contract. */
68
+ export function createEvidenceCacheKey(input) {
69
+ const promptHash = hash(input.prompt);
70
+ const digest = hash(JSON.stringify([
71
+ EVIDENCE_CONTRACT_VERSION,
72
+ input.sessionId ?? '',
73
+ input.sessionIdentity?.createdAt ?? '',
74
+ input.sessionIdentity?.cwd ?? '',
75
+ input.attachmentId,
76
+ promptHash,
77
+ input.runtimeHash,
78
+ ]));
79
+ return Object.freeze({
80
+ digest,
81
+ contractVersion: EVIDENCE_CONTRACT_VERSION,
82
+ ...(input.sessionId === undefined ? {} : { sessionId: input.sessionId }),
83
+ ...(input.sessionIdentity === undefined ? {} : { sessionCreatedAt: input.sessionIdentity.createdAt }),
84
+ ...(input.sessionIdentity?.cwd === undefined ? {} : { sessionCwd: input.sessionIdentity.cwd }),
85
+ attachmentId: input.attachmentId,
86
+ promptHash,
87
+ runtimeHash: input.runtimeHash,
88
+ });
89
+ }
90
+ /** Bounded promise cache; concurrent readers join one load and rejected loads are evicted. */
91
+ export class EvidenceCache {
92
+ limit;
93
+ persistence;
94
+ entries = new Map();
95
+ constructor(limit, persistence) {
96
+ this.limit = limit;
97
+ this.persistence = persistence;
98
+ }
99
+ /** Read a memory/durable hit or compute and persist one model-visible result. */
100
+ read(key, load) {
101
+ const memoryKey = typeof key === 'string' ? key : key.digest;
102
+ const existing = this.entries.get(memoryKey);
103
+ if (existing !== undefined) {
104
+ // Refresh recency: Map iteration order is insertion order.
105
+ this.entries.delete(memoryKey);
106
+ this.entries.set(memoryKey, existing);
107
+ return existing;
108
+ }
109
+ const pending = (async () => {
110
+ if (typeof key !== 'string' && this.persistence !== undefined) {
111
+ try {
112
+ const persisted = await this.persistence.read(key);
113
+ if (persisted !== undefined)
114
+ return persisted;
115
+ }
116
+ catch {
117
+ // Persistence is an optimization; a damaged/unavailable sidecar must
118
+ // never block the model request from recomputing evidence.
119
+ }
120
+ }
121
+ const block = await load();
122
+ if (typeof key !== 'string' && this.persistence !== undefined) {
123
+ try {
124
+ await this.persistence.write(key, block);
125
+ }
126
+ catch {
127
+ // Keep the process-local result even when durability fails.
128
+ }
129
+ }
130
+ return block;
131
+ })().then(block => block, (error) => {
132
+ // Only evict our own entry: this promise may have been LRU-evicted and
133
+ // the key re-populated by a newer read meanwhile.
134
+ if (this.entries.get(memoryKey) === pending) {
135
+ this.entries.delete(memoryKey);
136
+ }
137
+ throw error;
138
+ });
139
+ this.entries.set(memoryKey, pending);
140
+ while (this.entries.size > this.limit) {
141
+ const oldest = this.entries.keys().next().value;
142
+ if (oldest === undefined)
143
+ break;
144
+ this.entries.delete(oldest);
145
+ }
146
+ return pending;
147
+ }
148
+ /** Drop process-local descriptions; durable rows stay versioned by their runtime fingerprint. */
149
+ clear() {
150
+ this.entries.clear();
151
+ }
152
+ }
153
+ function identityOf(header) {
154
+ return Object.freeze({
155
+ createdAt: header.createdAt,
156
+ ...(header.cwd === undefined ? {} : { cwd: header.cwd }),
157
+ });
158
+ }
159
+ function sameIdentity(record, header) {
160
+ return record.session.createdAt === header.createdAt && record.session.cwd === header.cwd;
161
+ }
162
+ function matchesKey(record, key) {
163
+ return record.contractVersion === key.contractVersion
164
+ && record.sessionId === key.sessionId
165
+ && record.attachmentId === key.attachmentId
166
+ && record.promptHash === key.promptHash
167
+ && record.runtimeHash === key.runtimeHash;
168
+ }
169
+ function byteLength(text) {
170
+ return Buffer.byteLength(text, 'utf8');
171
+ }
172
+ function messageOf(error) {
173
+ return error instanceof Error ? error.message : String(error);
174
+ }
175
+ function parseRecord(value) {
176
+ try {
177
+ const parsed = JSON.parse(value);
178
+ const result = evidenceRecordSchema.safeParse(parsed);
179
+ return result.success ? result.data : undefined;
180
+ }
181
+ catch {
182
+ return undefined;
183
+ }
184
+ }
185
+ /** Official DSH storage-domain sidecar used to survive Profile restarts. */
186
+ export class SessionEvidenceStore {
187
+ ctx;
188
+ storage;
189
+ storageFiber;
190
+ storageReady;
191
+ mutationTail = Promise.resolve();
192
+ flushes = new Map();
193
+ warned = false;
194
+ maxEntries;
195
+ maxBytes;
196
+ maxEntryBytes;
197
+ now;
198
+ constructor(ctx, options = {}) {
199
+ this.ctx = ctx;
200
+ this.maxEntries = options.maxEntries ?? DEFAULT_PERSISTED_ENTRY_LIMIT;
201
+ this.maxBytes = options.maxBytes ?? DEFAULT_PERSISTED_BYTE_LIMIT;
202
+ this.maxEntryBytes = options.maxEntryBytes ?? DEFAULT_PERSISTED_ENTRY_BYTE_LIMIT;
203
+ this.now = options.now ?? Date.now;
204
+ if (typeof ctx.inject !== 'function')
205
+ return;
206
+ this.storageFiber = ctx.inject(['storageDomain'], async (storageCtx) => {
207
+ const domain = await storageCtx.storageDomain.open(evidenceCacheDomainSpec);
208
+ const binding = { table: domain.table('evidence') };
209
+ try {
210
+ await this.enqueueMutation(async () => { await this.prune(binding.table); });
211
+ this.storage = binding;
212
+ }
213
+ catch (error) {
214
+ await domain.close();
215
+ throw error;
216
+ }
217
+ return async () => {
218
+ if (this.storage === binding)
219
+ this.storage = undefined;
220
+ await this.mutationTail;
221
+ await domain.close();
222
+ };
223
+ });
224
+ this.storageReady = Promise.resolve(this.storageFiber).then(() => undefined, (error) => { this.warnOnce(error); });
225
+ }
226
+ /** Release the optional storage binding with the owning variant lifecycle. */
227
+ dispose() {
228
+ const fiber = this.storageFiber;
229
+ this.storageFiber = undefined;
230
+ this.storageReady = undefined;
231
+ if (fiber !== undefined)
232
+ void fiber.dispose().catch(error => { this.warnOnce(error); });
233
+ }
234
+ async read(key) {
235
+ const session = this.sessionFor(key);
236
+ if (session === undefined)
237
+ return undefined;
238
+ const binding = await this.prepareStorage();
239
+ if (binding === undefined)
240
+ return undefined;
241
+ const stored = binding.table.get(key.digest);
242
+ const record = stored === undefined ? undefined : parseRecord(stored);
243
+ if (record === undefined || !matchesKey(record, key) || !sameIdentity(record, session.header)) {
244
+ return undefined;
245
+ }
246
+ if (byteLength(record.text) > this.maxEntryBytes)
247
+ return undefined;
248
+ return { type: 'text', text: record.text };
249
+ }
250
+ async write(key, block) {
251
+ if (block.type !== 'text' || block.text.length === 0 || byteLength(block.text) > this.maxEntryBytes)
252
+ return;
253
+ const session = this.sessionFor(key);
254
+ if (session === undefined)
255
+ return;
256
+ const binding = await this.prepareStorage();
257
+ if (binding === undefined)
258
+ return;
259
+ try {
260
+ const participated = await this.flushSession(session);
261
+ if (!participated)
262
+ return;
263
+ const record = Object.freeze({
264
+ contractVersion: key.contractVersion,
265
+ sessionId: key.sessionId,
266
+ session: identityOf(session.header),
267
+ attachmentId: key.attachmentId,
268
+ promptHash: key.promptHash,
269
+ runtimeHash: key.runtimeHash,
270
+ text: block.text,
271
+ storedAt: this.now(),
272
+ });
273
+ await this.enqueueMutation(async () => {
274
+ await binding.table.put(key.digest, JSON.stringify(record));
275
+ await this.prune(binding.table);
276
+ });
277
+ }
278
+ catch (error) {
279
+ this.warnOnce(error);
280
+ }
281
+ }
282
+ sessionFor(key) {
283
+ if (key.sessionId === undefined)
284
+ return undefined;
285
+ const session = this.ctx.sessions.get(key.sessionId);
286
+ if (session === undefined)
287
+ return undefined;
288
+ if (key.sessionCreatedAt !== undefined
289
+ && (session.header.createdAt !== key.sessionCreatedAt || session.header.cwd !== key.sessionCwd)) {
290
+ return undefined;
291
+ }
292
+ return session;
293
+ }
294
+ async prepareStorage() {
295
+ if (this.storage !== undefined)
296
+ return this.storage;
297
+ if (this.ctx.get('storageDomain') === undefined)
298
+ return undefined;
299
+ await this.storageReady;
300
+ return this.storage;
301
+ }
302
+ enqueueMutation(operation) {
303
+ const result = this.mutationTail.then(operation);
304
+ this.mutationTail = result.then(() => undefined, () => undefined);
305
+ return result;
306
+ }
307
+ flushSession(session) {
308
+ const key = `${session.id}\u0000${session.header.createdAt}\u0000${session.header.cwd ?? ''}`;
309
+ const existing = this.flushes.get(key);
310
+ if (existing !== undefined)
311
+ return existing;
312
+ const pending = this.ctx.sessions.flush(session).finally(() => {
313
+ if (this.flushes.get(key) === pending)
314
+ this.flushes.delete(key);
315
+ });
316
+ this.flushes.set(key, pending);
317
+ return pending;
318
+ }
319
+ async prune(table) {
320
+ const records = [...table.entries()].map(([key, stored]) => {
321
+ const record = parseRecord(stored);
322
+ return {
323
+ key,
324
+ bytes: byteLength(stored),
325
+ storedAt: record?.storedAt ?? -1,
326
+ valid: record !== undefined && byteLength(record.text) <= this.maxEntryBytes,
327
+ };
328
+ });
329
+ let totalBytes = records.reduce((sum, record) => sum + record.bytes, 0);
330
+ let totalEntries = records.length;
331
+ records.sort((left, right) => Number(left.valid) - Number(right.valid)
332
+ || left.storedAt - right.storedAt
333
+ || left.key.localeCompare(right.key));
334
+ for (const record of records) {
335
+ if (record.valid && totalEntries <= this.maxEntries && totalBytes <= this.maxBytes)
336
+ break;
337
+ if (await table.delete(record.key)) {
338
+ totalEntries -= 1;
339
+ totalBytes -= record.bytes;
340
+ }
341
+ }
342
+ }
343
+ warnOnce(error) {
344
+ if (this.warned)
345
+ return;
346
+ this.warned = true;
347
+ this.ctx.logger?.warn('dsh-vision-toolkit: persistent image evidence cache is unavailable; using the process cache only. %s', messageOf(error).slice(0, 500));
348
+ }
349
+ }
350
+ //# sourceMappingURL=evidence-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evidence-cache.js","sourceRoot":"","sources":["../src/evidence-cache.ts"],"names":[],"mappings":"AAAA,mFAAmF;AAEnF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAIxC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAA;AAE3E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAE9C,+EAA+E;AAC/E,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAA;AAE1C,qFAAqF;AACrF,MAAM,6BAA6B,GAAG,GAAG,CAAA;AACzC,MAAM,4BAA4B,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAA;AACpD,MAAM,kCAAkC,GAAG,EAAE,GAAG,IAAI,CAAA;AACpD,MAAM,qBAAqB,GAAG,GAAG,GAAG,IAAI,CAAA;AACxC,MAAM,uBAAuB,GAAG,GAAG,GAAG,IAAI,CAAA;AAE1C,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAA;AAC3D,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC;IACtE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC3B,CAAC,CAAA;AACF,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IAC/C,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,OAAO,EAAE,qBAAqB;IAC9B,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,UAAU,EAAE,eAAe;IAC3B,WAAW,EAAE,eAAe;IAC5B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,qBAAqB,CAAC;IAClD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC;CACtE,CAAC,CAAA;AAKF,uFAAuF;AACvF,MAAM,CAAC,MAAM,uBAAuB,GAAG,YAAY,CAAC;IAClD,IAAI,EAAE,yBAAyB;IAC/B,OAAO,EAAE,CAAC;IACV,MAAM,EAAE;QACN,wEAAwE;QACxE,2EAA2E;QAC3E,wEAAwE;QACxE,QAAQ,EAAE,WAAW,CAA4B,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;KAC1F;CACF,CAAC,CAAA;AAoBF,SAAS,IAAI,CAAC,KAAa;IACzB,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACzD,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,0BAA0B,CACxC,MAAmC,EACnC,gBAAyB,EACzB,SAAkB;IAElB,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;QACzB,cAAc,EAAE,eAAe;QAC/B,QAAQ,EAAE;YACR,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO;YAChC,UAAU,EAAE;gBACV,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;gBACvC,MAAM,EAAE,gBAAgB,IAAI,IAAI;aACjC;YACD,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK;YAC5B,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ;YAClC,iBAAiB,EAAE,MAAM,CAAC,QAAQ,CAAC,iBAAiB;YACpD,SAAS,EAAE,SAAS,IAAI,IAAI;YAC5B,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,SAAS;SACrC;QACD,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,OAAO,EAAE,MAAM,CAAC,OAAO;KACxB,CAAC,CAAC,CAAA;AACL,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,sBAAsB,CAAC,KAMtC;IACC,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;QACjC,yBAAyB;QACzB,KAAK,CAAC,SAAS,IAAI,EAAE;QACrB,KAAK,CAAC,eAAe,EAAE,SAAS,IAAI,EAAE;QACtC,KAAK,CAAC,eAAe,EAAE,GAAG,IAAI,EAAE;QAChC,KAAK,CAAC,YAAY;QAClB,UAAU;QACV,KAAK,CAAC,WAAW;KAClB,CAAC,CAAC,CAAA;IACH,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,MAAM;QACN,eAAe,EAAE,yBAAyB;QAC1C,GAAG,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC;QACxE,GAAG,CAAC,KAAK,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,KAAK,CAAC,eAAe,CAAC,SAAS,EAAE,CAAC;QACrG,GAAG,CAAC,KAAK,CAAC,eAAe,EAAE,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC;QAC9F,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,UAAU;QACV,WAAW,EAAE,KAAK,CAAC,WAAW;KAC/B,CAAC,CAAA;AACJ,CAAC;AAED,8FAA8F;AAC9F,MAAM,OAAO,aAAa;IAIL;IACA;IAJF,OAAO,GAAG,IAAI,GAAG,EAAiC,CAAA;IAEnE,YACmB,KAAa,EACb,WAAiC;QADjC,UAAK,GAAL,KAAK,CAAQ;QACb,gBAAW,GAAX,WAAW,CAAsB;IACjD,CAAC;IAEJ,iFAAiF;IACjF,IAAI,CAAC,GAA8B,EAAE,IAAiC;QACpE,MAAM,SAAS,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAA;QAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAC5C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,2DAA2D;YAC3D,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;YAC9B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;YACrC,OAAO,QAAQ,CAAA;QACjB,CAAC;QAED,MAAM,OAAO,GAAG,CAAC,KAAK,IAA2B,EAAE;YACjD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;gBAC9D,IAAI,CAAC;oBACH,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;oBAClD,IAAI,SAAS,KAAK,SAAS;wBAAE,OAAO,SAAS,CAAA;gBAC/C,CAAC;gBAAC,MAAM,CAAC;oBACP,qEAAqE;oBACrE,2DAA2D;gBAC7D,CAAC;YACH,CAAC;YAED,MAAM,KAAK,GAAG,MAAM,IAAI,EAAE,CAAA;YAC1B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;gBAC9D,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;gBAC1C,CAAC;gBAAC,MAAM,CAAC;oBACP,4DAA4D;gBAC9D,CAAC;YACH,CAAC;YACD,OAAO,KAAK,CAAA;QACd,CAAC,CAAC,EAAE,CAAC,IAAI,CACP,KAAK,CAAC,EAAE,CAAC,KAAK,EACd,CAAC,KAAc,EAAE,EAAE;YACjB,uEAAuE;YACvE,kDAAkD;YAClD,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,OAAO,EAAE,CAAC;gBAC5C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;YAChC,CAAC;YACD,MAAM,KAAK,CAAA;QACb,CAAC,CACF,CAAA;QAED,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QACpC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YACtC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAA;YAC/C,IAAI,MAAM,KAAK,SAAS;gBAAE,MAAK;YAC/B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAC7B,CAAC;QACD,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,iGAAiG;IACjG,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;IACtB,CAAC;CACF;AAkBD,SAAS,UAAU,CAAC,MAAqB;IACvC,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,GAAG,CAAC,MAAM,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC;KACzD,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,MAAsB,EAAE,MAAqB;IACjE,OAAO,MAAM,CAAC,OAAO,CAAC,SAAS,KAAK,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,KAAK,MAAM,CAAC,GAAG,CAAA;AAC3F,CAAC;AAED,SAAS,UAAU,CAAC,MAAsB,EAAE,GAAqB;IAC/D,OAAO,MAAM,CAAC,eAAe,KAAK,GAAG,CAAC,eAAe;WAChD,MAAM,CAAC,SAAS,KAAK,GAAG,CAAC,SAAS;WAClC,MAAM,CAAC,YAAY,KAAK,GAAG,CAAC,YAAY;WACxC,MAAM,CAAC,UAAU,KAAK,GAAG,CAAC,UAAU;WACpC,MAAM,CAAC,WAAW,KAAK,GAAG,CAAC,WAAW,CAAA;AAC7C,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;AACxC,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC/D,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACzC,MAAM,MAAM,GAAG,oBAAoB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;QACrD,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAA;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,MAAM,OAAO,oBAAoB;IAaZ;IAZX,OAAO,CAA4B;IACnC,YAAY,CAA0C;IACtD,YAAY,CAA2B;IACvC,YAAY,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAA;IACtC,OAAO,GAAG,IAAI,GAAG,EAA4B,CAAA;IACtD,MAAM,GAAG,KAAK,CAAA;IACL,UAAU,CAAQ;IAClB,QAAQ,CAAQ;IAChB,aAAa,CAAQ;IACrB,GAAG,CAAc;IAElC,YACmB,GAAY,EAC7B,UAAuC,EAAE;QADxB,QAAG,GAAH,GAAG,CAAS;QAG7B,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,6BAA6B,CAAA;QACrE,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,4BAA4B,CAAA;QAChE,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,kCAAkC,CAAA;QAChF,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAA;QAElC,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,UAAU;YAAE,OAAM;QAC5C,IAAI,CAAC,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,UAAmB,EAAE,EAAE;YAC9E,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAA;YAC3E,MAAM,OAAO,GAAmB,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAA;YACnE,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA,CAAC,CAAC,CAAC,CAAA;gBAC3E,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;YACxB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;gBACpB,MAAM,KAAK,CAAA;YACb,CAAC;YACD,OAAO,KAAK,IAAI,EAAE;gBAChB,IAAI,IAAI,CAAC,OAAO,KAAK,OAAO;oBAAE,IAAI,CAAC,OAAO,GAAG,SAAS,CAAA;gBACtD,MAAM,IAAI,CAAC,YAAY,CAAA;gBACvB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;YACtB,CAAC,CAAA;QACH,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,CACzD,GAAG,EAAE,CAAC,SAAS,EACf,CAAC,KAAc,EAAE,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA,CAAC,CAAC,CAC7C,CAAA;IACH,CAAC;IAED,8EAA8E;IAC9E,OAAO;QACL,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAA;QAC/B,IAAI,CAAC,YAAY,GAAG,SAAS,CAAA;QAC7B,IAAI,CAAC,YAAY,GAAG,SAAS,CAAA;QAC7B,IAAI,KAAK,KAAK,SAAS;YAAE,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA,CAAC,CAAC,CAAC,CAAA;IACxF,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAqB;QAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;QACpC,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,SAAS,CAAA;QAC3C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAA;QAC3C,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,SAAS,CAAA;QAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,MAA2B,CAAC,CAAA;QACjE,MAAM,MAAM,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;QACrE,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9F,OAAO,SAAS,CAAA;QAClB,CAAC;QACD,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,aAAa;YAAE,OAAO,SAAS,CAAA;QAClE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAA;IAC5C,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAqB,EAAE,KAAmB;QACpD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,aAAa;YAAE,OAAM;QAC3G,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;QACpC,IAAI,OAAO,KAAK,SAAS;YAAE,OAAM;QACjC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAA;QAC3C,IAAI,OAAO,KAAK,SAAS;YAAE,OAAM;QAEjC,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAA;YACrD,IAAI,CAAC,YAAY;gBAAE,OAAM;YACzB,MAAM,MAAM,GAAmB,MAAM,CAAC,MAAM,CAAC;gBAC3C,eAAe,EAAE,GAAG,CAAC,eAAe;gBACpC,SAAS,EAAE,GAAG,CAAC,SAAmB;gBAClC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC;gBACnC,YAAY,EAAE,GAAG,CAAC,YAAY;gBAC9B,UAAU,EAAE,GAAG,CAAC,UAAU;gBAC1B,WAAW,EAAE,GAAG,CAAC,WAAW;gBAC5B,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE;aACrB,CAAC,CAAA;YACF,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK,IAAI,EAAE;gBACpC,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,MAA2B,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAA;gBAChF,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;YACjC,CAAC,CAAC,CAAA;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QACtB,CAAC;IACH,CAAC;IAEO,UAAU,CAAC,GAAqB;QACtC,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS;YAAE,OAAO,SAAS,CAAA;QACjD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,SAAsB,CAAC,CAAA;QACjE,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,SAAS,CAAA;QAC3C,IAAI,GAAG,CAAC,gBAAgB,KAAK,SAAS;eACjC,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,KAAK,GAAG,CAAC,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YAClG,OAAO,SAAS,CAAA;QAClB,CAAC;QACD,OAAO,OAAO,CAAA;IAChB,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,OAAO,CAAA;QACnD,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,KAAK,SAAS;YAAE,OAAO,SAAS,CAAA;QACjE,MAAM,IAAI,CAAC,YAAY,CAAA;QACvB,OAAO,IAAI,CAAC,OAAO,CAAA;IACrB,CAAC;IAEO,eAAe,CAAC,SAA8B;QACpD,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAChD,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACjE,OAAO,MAAM,CAAA;IACf,CAAC;IAEO,YAAY,CAAC,OAA4D;QAC/E,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,EAAE,SAAS,OAAO,CAAC,MAAM,CAAC,SAAS,SAAS,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,EAAE,CAAA;QAC7F,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACtC,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAO,QAAQ,CAAA;QAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE;YAC5D,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,OAAO;gBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QACjE,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAC9B,OAAO,OAAO,CAAA;IAChB,CAAC;IAEO,KAAK,CAAC,KAAK,CAAC,KAAyC;QAC3D,MAAM,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE;YACzD,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAA;YAClC,OAAO;gBACL,GAAG;gBACH,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;gBACzB,QAAQ,EAAE,MAAM,EAAE,QAAQ,IAAI,CAAC,CAAC;gBAChC,KAAK,EAAE,MAAM,KAAK,SAAS,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,aAAa;aAC7E,CAAA;QACH,CAAC,CAAC,CAAA;QACF,IAAI,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;QACvE,IAAI,YAAY,GAAG,OAAO,CAAC,MAAM,CAAA;QACjC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;eACjE,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ;eAC9B,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;QACvC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,KAAK,IAAI,YAAY,IAAI,IAAI,CAAC,UAAU,IAAI,UAAU,IAAI,IAAI,CAAC,QAAQ;gBAAE,MAAK;YACzF,IAAI,MAAM,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBACnC,YAAY,IAAI,CAAC,CAAA;gBACjB,UAAU,IAAI,MAAM,CAAC,KAAK,CAAA;YAC5B,CAAC;QACH,CAAC;IACH,CAAC;IAEO,QAAQ,CAAC,KAAc;QAC7B,IAAI,IAAI,CAAC,MAAM;YAAE,OAAM;QACvB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;QAClB,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CACnB,sGAAsG,EACtG,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAC/B,CAAA;IACH,CAAC;CACF"}