dsh-draw 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@ All notable changes to this project are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.2] - 2026-08-22
9
+
10
+ ### Changed
11
+
12
+ - Upgraded every `@deepseek-ai/dsh-*` devDependency to `0.1.1-rc.2` and the `dshWorkshop.compatibility.dshVersions` declaration to `0.1.1-rc.2` (rc.2 compatibility release); peers stay `>=0.1.0-rc.8 <0.2.0` and `@deepseek-ai/cordis` stays `^4.0.1`.
13
+ - Re-verified the rc.2 credential read/write surface (`resolve`/`describe`/`set`/`unset`) and the attachment `saveImage`/`ImageAttachmentRef`/`ImageAttachmentLimits` face; both remain source-compatible, so no plugin code changes were required.
14
+ - The monthly compat workflow now installs and smokes against DeepSeek Harness `0.1.1-rc.2`.
15
+
16
+ ## [0.1.1] - 2026-08-21
17
+
18
+ ### Changed
19
+
20
+ - Upgraded every `@deepseek-ai/dsh-*` peer to `>=0.1.0-rc.8 <0.2.0` and every devDependency to `0.1.0-rc.8` (rc.8 compatibility release); `@deepseek-ai/cordis` stays at `^4.0.1`.
21
+ - `tests/harness.ts` pins the rc.8 `ImageAttachmentLimits` face (adds `maxImageDimension`).
22
+
23
+ ### Fixed
24
+
25
+ - Sessions that used image generation no longer refuse to reopen on rc.6/rc.7 hosts (issue #2). `draw/generated` is declared only by this package, so it sits outside the host's static `KNOWN_SESSION_EVENT_TYPES` whitelist, and those hosts' `Session.append` cannot stamp the `ignorable` envelope — every generated image left the log unloadable after restart with `SessionFormatUnsupportedError`. The append now goes through an adaptive gate (`src/event-gate.ts`): the event is logged only when the host knows the type or a mount-time probe on a detached `SessionStore` proves envelope support. On gated hosts the accounting payload rides a new in-memory fallback ledger (WeakMap-keyed by session), so quota stays exact for the live session and the log stays reloadable; durable, log-folded accounting resumes on hosts with a plugin event surface.
26
+ - Regression coverage: `tests/event-gate.spec.ts` pins the gate decisions, the rc.6 probe reading, fallback-ledger quota accounting, and the append-failure degradation; the assembly regenerate test now asserts the rc.6-safe behavior (clean log, exact quota).
27
+
8
28
  ## [0.1.0] - 2026-08-16
9
29
 
10
30
  - Initial release: unified image_generate tool with config-driven engine routing, health-aware fallback, durable attachments, per-session quota accounting, and credential-reference key storage.
package/README.es.md CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
  | Superficie | Estado |
26
26
  |---|---|
27
- | Harness | DeepSeek Harness `0.1.0-rc.6` (compatibilidad declarada para `0.1.0-rc.6`) |
27
+ | Harness | DeepSeek Harness `0.1.1-rc.2` (compatibilidad declarada para `0.1.1-rc.2`) |
28
28
  | Node | `^22.19.0 \|\| >=24.0.0` |
29
29
  | Motores | Cualquier endpoint de imágenes compatible con OpenAI; presets para OpenAI Images (`gpt-image-1`) y Zhipu CogView (`cogview-3-flash`) |
30
30
  | Superficies | Herramienta host `image_generate` + tarjeta de resultado web + pestaña de ajustes de Plugins |
@@ -116,8 +116,8 @@ Ejemplo de sobrescritura en el parche de tu perfil:
116
116
  ## Permisos y datos
117
117
 
118
118
  - **Permisos**: el plugin solo hace llamadas HTTPS salientes a los endpoints de motor configurados; el resto es de solo lectura. Las únicas escrituras de la pestaña de ajustes son las llamadas de establecer/eliminar credenciales en el seam oficial `ctx.credentials`.
119
- - **Datos**: las imágenes generadas se guardan a través del almacén de adjuntos oficial bajo la política de adjuntos del harness. El uso de cuota se pliega desde los eventos de sesión `draw/generated` — nada más se almacena.
120
- - **Registro de sesión**: el evento `draw/generated` registra motor, modelo, solicitud estandarizada, totales de bytes e ids de adjunto — los hechos de auditoría, nunca las claves API.
119
+ - **Datos**: las imágenes generadas se guardan a través del almacén de adjuntos oficial bajo la política de adjuntos del harness. El uso de cuota se pliega desde los eventos de sesión `draw/generated`, más el libro auxiliar en memoria en hosts que no pueden registrar esos eventos — nada más se almacena.
120
+ - **Registro de sesión**: el evento `draw/generated` registra motor, modelo, solicitud estandarizada, totales de bytes e ids de adjunto — los hechos de auditoría, nunca las claves API. El evento solo se añade cuando el host conoce el tipo o admite el sobre `ignorable` (sondeado al montar); en hosts rc.6/rc.7 la carga va al libro auxiliar en memoria, de modo que generar imágenes ya no hace que la sesión se niegue a reabrirse.
121
121
 
122
122
  ## Límites de seguridad
123
123
 
@@ -131,13 +131,14 @@ Ejemplo de sobrescritura en el parche de tu perfil:
131
131
  - **Solo modelos de imagen.** Sin endpoints de vídeo, audio o edición; sin comprensión visual.
132
132
  - **Compatibilidad de motores.** Los motores deben hablar la forma `POST /images/generations` de OpenAI (entrega base64 o URL); los extras específicos de cada proveedor quedan fuera.
133
133
  - **La conciencia de coste es estructural.** El plugin cuenta llamadas y bytes pero no conoce el precio de los motores — combínalo con `dsh-budget` para la gobernanza de coste.
134
+ - **Durabilidad de cuota en rc.6/rc.7.** En hosts cuyo registro de sesión no puede llevar `draw/generated` (lista blanca estática de eventos, sin sobre `ignorable`), la cuota sigue exacta en la sesión viva desde el libro auxiliar en memoria pero se reinicia al reiniciar; la contabilidad duradera vuelve en hosts con una superficie de eventos para plugins.
134
135
 
135
136
  ## Desarrollo
136
137
 
137
138
  ```sh
138
139
  pnpm install # node ^22.19 || >=24
139
140
  pnpm run typecheck # tsc: src + tests contra el checkout local del harness
140
- pnpm run typecheck:ci # tsc contra las caras publicadas 0.1.0-rc.6 (sin paths)
141
+ pnpm run typecheck:ci # tsc contra las caras publicadas 0.1.1-rc.2 (sin paths)
141
142
  pnpm test # vitest: 77 tests, 11 suites (transporte guionado, Context/Session/ToolRuntime reales)
142
143
  pnpm run build # declaraciones tsc + bundles tsdown (lib/)
143
144
  pnpm run verify:self-contained # las especificaciones de dependencias resuelven desde el registry
package/README.hi.md CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
  | सतह | स्थिति |
26
26
  |---|---|
27
- | Harness | DeepSeek Harness `0.1.0-rc.6` (`0.1.0-rc.6` के लिए घोषित संगतता) |
27
+ | Harness | DeepSeek Harness `0.1.1-rc.2` (`0.1.1-rc.2` के लिए घोषित संगतता) |
28
28
  | Node | `^22.19.0 \|\| >=24.0.0` |
29
29
  | इंजन | कोई भी OpenAI-संगत images एंडपॉइंट; OpenAI Images (`gpt-image-1`) और Zhipu CogView (`cogview-3-flash`) प्रीसेट |
30
30
  | सतहें | Host `image_generate` टूल + वेब परिणाम कार्ड + Plugins सेटिंग टैब |
@@ -116,8 +116,8 @@ dsh --profile web --dump-config | grep -A2 'id: dsh-draw'
116
116
  ## अनुमतियाँ और डेटा
117
117
 
118
118
  - **अनुमतियाँ**: प्लगइन केवल कॉन्फ़िगर किए इंजन एंडपॉइंट पर आउटबाउंड HTTPS कॉल करता है; बाकी सब केवल-पठन है। सेटिंग टैब का एकमात्र लेखन आधिकारिक `ctx.credentials` सीम पर क्रेडेंशियल सेट/हटाना है।
119
- - **डेटा**: बनी छवियाँ आधिकारिक अटैचमेंट स्टोर से हार्नेस की अपनी नीति के तहत सहेजी जाती हैं। कोटा उपयोग `draw/generated` सत्र इवेंट से मोड़ा जाता है — और कुछ संग्रहीत नहीं होता।
120
- - **सत्र लॉग**: `draw/generated` इवेंट इंजन, मॉडल, मानकीकृत अनुरोध, बाइट योग और अटैचमेंट आईडी दर्ज करता है — ऑडिट तथ्य, कभी API कुंजियाँ नहीं।
119
+ - **डेटा**: बनी छवियाँ आधिकारिक अटैचमेंट स्टोर से हार्नेस की अपनी नीति के तहत सहेजी जाती हैं। कोटा उपयोग `draw/generated` सत्र इवेंट से मोड़ा जाता है, साथ में उन होस्ट पर इन-मेमोरी फ़ॉलबैक लेजर से जो उन इवेंट को लॉग नहीं कर सकते — और कुछ संग्रहीत नहीं होता।
120
+ - **सत्र लॉग**: `draw/generated` इवेंट इंजन, मॉडल, मानकीकृत अनुरोध, बाइट योग और अटैचमेंट आईडी दर्ज करता है — ऑडिट तथ्य, कभी API कुंजियाँ नहीं। इवेंट तभी जोड़ा जाता है जब होस्ट उस प्रकार को जानता हो या `ignorable` एनवेलप स्वीकार करता हो (माउंट पर जांचा जाता है); rc.6/rc.7 होस्ट पर पेलोड इन-मेमोरी फ़ॉलबैक लेजर में जाता है, इसलिए इमेज बनाने से सत्र अब दोबारा खुलने से इनकार नहीं करता।
121
121
 
122
122
  ## सुरक्षा सीमाएँ
123
123
 
@@ -131,13 +131,14 @@ dsh --profile web --dump-config | grep -A2 'id: dsh-draw'
131
131
  - **केवल छवि मॉडल।** कोई वीडियो, ऑडियो या संपादन एंडपॉइंट नहीं; कोई विज़ुअल समझ नहीं।
132
132
  - **इंजन संगतता।** इंजन को OpenAI `POST /images/generations` आकार बोलना चाहिए (base64 या URL डिलीवरी); प्रदाता-विशेष अतिरिक्त दायरे से बाहर हैं।
133
133
  - **लागत-जागरूकता संरचनात्मक है।** प्लगइन कॉल और बाइट गिनता है, इंजन मूल्य नहीं जानता — लागत प्रशासन के लिए `dsh-budget` के साथ जोड़ें।
134
+ - **rc.6/rc.7 पर कोटा स्थायित्व।** जिन होस्ट का सत्र लॉग `draw/generated` को सुरक्षित रूप से नहीं रख सकता (स्थिर इवेंट श्वेतसूची, कोई `ignorable` एनवेलप नहीं), कोटा इन-मेमोरी फ़ॉलबैक लेजर से जीवित सत्र में सटीक रहता है लेकिन पुनः आरंभ पर रीसेट हो जाता है; प्लगइन इवेंट सतह वाले होस्ट पर टिकाऊ लेखांकन फिर से शुरू होता है।
134
135
 
135
136
  ## विकास
136
137
 
137
138
  ```sh
138
139
  pnpm install # node ^22.19 || >=24
139
140
  pnpm run typecheck # tsc: src + tests स्थानीय हार्नेस चेकआउट के विरुद्ध
140
- pnpm run typecheck:ci # tsc प्रकाशित 0.1.0-rc.6 फ़ेस के विरुद्ध (बिना paths)
141
+ pnpm run typecheck:ci # tsc प्रकाशित 0.1.1-rc.2 फ़ेस के विरुद्ध (बिना paths)
141
142
  pnpm test # vitest: 77 टेस्ट, 11 सुइट (स्क्रिप्टेड ट्रांसपोर्ट, वास्तविक Context/Session/ToolRuntime)
142
143
  pnpm run build # tsc घोषणाएँ + tsdown बंडल (lib/)
143
144
  pnpm run verify:self-contained # निर्भरता स्पेक registry से हल होती हैं
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  <div align="center">
2
2
 
3
3
  # 🎨 dsh-draw
4
+ [![Gitee](https://img.shields.io/badge/Gitee-mirror-c71d23?logo=gitee)](https://gitee.com/perrylink/dsh-draw)
4
5
 
5
6
  **Unified static-image generation routing for DeepSeek Harness.**
6
7
 
@@ -24,7 +25,7 @@
24
25
 
25
26
  | Surface | Status |
26
27
  |---|---|
27
- | Harness | DeepSeek Harness `0.1.0-rc.6` (compat declared for `0.1.0-rc.6`) |
28
+ | Harness | DeepSeek Harness `0.1.1-rc.2` (compat declared for `0.1.1-rc.2`) |
28
29
  | Node | `^22.19.0 \|\| >=24.0.0` |
29
30
  | Engines | Any OpenAI-compatible images endpoint; presets for OpenAI Images (`gpt-image-1`) and Zhipu CogView (`cogview-3-flash`) |
30
31
  | Surfaces | Host `image_generate` tool + web result card + Plugins settings tab |
@@ -116,8 +117,8 @@ Example override in your profile patch:
116
117
  ## Permissions & data
117
118
 
118
119
  - **Permissions**: the plugin makes outbound HTTPS calls to the configured engine endpoints only; every other surface is read-only. The settings tab's only writes are credential set/remove calls on the official `ctx.credentials` seam.
119
- - **Data**: generated images are saved through the official attachment store under the harness's own attachment policy. Quota usage is folded from the `draw/generated` session events — nothing else is stored.
120
- - **Session log**: the `draw/generated` event records engine, model, standardized request, byte totals, and attachment ids — the audit facts, never the API keys.
120
+ - **Data**: generated images are saved through the official attachment store under the harness's own attachment policy. Quota usage is folded from the `draw/generated` session events, plus the in-memory fallback ledger on hosts that cannot log those events — nothing else is stored.
121
+ - **Session log**: the `draw/generated` event records engine, model, standardized request, byte totals, and attachment ids — the audit facts, never the API keys. The event is appended only when the host knows the type or honors the `ignorable` envelope (probed at mount); on rc.6/rc.7 hosts the payload goes to the in-memory fallback ledger instead, so generating images can no longer make a session refuse to reopen.
121
122
 
122
123
  ## Security boundaries
123
124
 
@@ -131,13 +132,14 @@ Example override in your profile patch:
131
132
  - **Image models only.** No video, audio, or edit endpoints; no vision understanding.
132
133
  - **Engine compatibility.** Engines must speak the OpenAI `POST /images/generations` shape (base64 or URL delivery); provider-specific extras are out of scope.
133
134
  - **Cost awareness is structural.** The plugin counts calls and bytes but does not know engine pricing — pair with `dsh-budget` for cost governance.
135
+ - **Quota durability on rc.6/rc.7.** On hosts whose session log cannot carry `draw/generated` (static event whitelist, no `ignorable` envelope), quota stays exact for the live session from the in-memory fallback ledger but resets on restart; durable accounting resumes on hosts with a plugin event surface.
134
136
 
135
137
  ## Development
136
138
 
137
139
  ```sh
138
140
  pnpm install # node ^22.19 || >=24
139
141
  pnpm run typecheck # tsc: src + tests against the local harness checkout
140
- pnpm run typecheck:ci # tsc against the published 0.1.0-rc.6 faces (no paths)
142
+ pnpm run typecheck:ci # tsc against the published 0.1.1-rc.2 faces (no paths)
141
143
  pnpm test # vitest: 77 tests, 11 suites (scripted transport, real Context/Session/ToolRuntime)
142
144
  pnpm run build # tsc declarations + tsdown bundles (lib/)
143
145
  pnpm run verify:self-contained # dependency specs resolve from the registry
package/README.pt.md CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
  | Superfície | Status |
26
26
  |---|---|
27
- | Harness | DeepSeek Harness `0.1.0-rc.6` (compatibilidade declarada para `0.1.0-rc.6`) |
27
+ | Harness | DeepSeek Harness `0.1.1-rc.2` (compatibilidade declarada para `0.1.1-rc.2`) |
28
28
  | Node | `^22.19.0 \|\| >=24.0.0` |
29
29
  | Motores | Qualquer endpoint de imagens compatível com OpenAI; presets para OpenAI Images (`gpt-image-1`) e Zhipu CogView (`cogview-3-flash`) |
30
30
  | Superfícies | Ferramenta host `image_generate` + cartão de resultado web + aba de ajustes de Plugins |
@@ -116,8 +116,8 @@ Exemplo de sobrescrita no patch do seu perfil:
116
116
  ## Permissões e dados
117
117
 
118
118
  - **Permissões**: o plugin só faz chamadas HTTPS de saída para os endpoints de motor configurados; todo o resto é somente leitura. As únicas escritas da aba de ajustes são as chamadas de definir/remover credenciais no seam oficial `ctx.credentials`.
119
- - **Dados**: as imagens geradas são salvas através do armazenamento de anexos oficial sob a política de anexos do harness. O uso de cota é dobrado dos eventos de sessão `draw/generated` — nada mais é armazenado.
120
- - **Registro de sessão**: o evento `draw/generated` registra motor, modelo, solicitação padronizada, totais de bytes e ids de anexo — os fatos de auditoria, nunca as chaves de API.
119
+ - **Dados**: as imagens geradas são salvas através do armazenamento de anexos oficial sob a política de anexos do harness. O uso de cota é dobrado dos eventos de sessão `draw/generated`, mais o livro auxiliar em memória em hosts que não podem registrar esses eventos — nada mais é armazenado.
120
+ - **Registro de sessão**: o evento `draw/generated` registra motor, modelo, solicitação padronizada, totais de bytes e ids de anexo — os fatos de auditoria, nunca as chaves API. O evento só é anexado quando o host conhece o tipo ou honra o envelope `ignorable` (sondado na montagem); em hosts rc.6/rc.7 a carga vai para o livro auxiliar em memória, então gerar imagens não faz mais a sessão recusar reabrir.
121
121
 
122
122
  ## Limites de segurança
123
123
 
@@ -131,13 +131,14 @@ Exemplo de sobrescrita no patch do seu perfil:
131
131
  - **Somente modelos de imagem.** Sem endpoints de vídeo, áudio ou edição; sem compreensão visual.
132
132
  - **Compatibilidade de motores.** Os motores devem falar a forma `POST /images/generations` da OpenAI (entrega base64 ou URL); extras específicos de cada provedor ficam de fora.
133
133
  - **Consciência de custo é estrutural.** O plugin conta chamadas e bytes, mas não conhece o preço dos motores — combine com `dsh-budget` para a governança de custo.
134
+ - **Durabilidade de cota em rc.6/rc.7.** Em hosts cujo registro de sessão não pode carregar `draw/generated` (lista estática de eventos, sem envelope `ignorable`), a cota continua exata na sessão viva a partir do livro auxiliar em memória, mas zera ao reiniciar; a contabilidade durável retorna em hosts com uma superfície de eventos para plugins.
134
135
 
135
136
  ## Desenvolvimento
136
137
 
137
138
  ```sh
138
139
  pnpm install # node ^22.19 || >=24
139
140
  pnpm run typecheck # tsc: src + tests contra o checkout local do harness
140
- pnpm run typecheck:ci # tsc contra as faces publicadas 0.1.0-rc.6 (sem paths)
141
+ pnpm run typecheck:ci # tsc contra as faces publicadas 0.1.1-rc.2 (sem paths)
141
142
  pnpm test # vitest: 77 testes, 11 suítes (transporte roteirizado, Context/Session/ToolRuntime reais)
142
143
  pnpm run build # declarações tsc + bundles tsdown (lib/)
143
144
  pnpm run verify:self-contained # as especificações de dependências resolvem pelo registry
package/README.zh.md CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
  | 方面 | 状态 |
26
26
  |---|---|
27
- | Harness | DeepSeek Harness `0.1.0-rc.6`(声明兼容 `0.1.0-rc.6`) |
27
+ | Harness | DeepSeek Harness `0.1.1-rc.2`(声明兼容 `0.1.1-rc.2`) |
28
28
  | Node | `^22.19.0 \|\| >=24.0.0` |
29
29
  | 引擎 | 任意 OpenAI 兼容图像端点;内置 OpenAI Images(`gpt-image-1`)与智谱 CogView(`cogview-3-flash`)预设 |
30
30
  | 界面 | Host `image_generate` 工具 + Web 结果卡片 + Plugins 设置页签 |
@@ -116,8 +116,8 @@ profile patch 中的覆盖示例:
116
116
  ## 权限与数据
117
117
 
118
118
  - **权限**:插件仅对配置的引擎端点发起 HTTPS 出站请求;其余界面全部只读。设置页签唯一的写入是对官方 `ctx.credentials` 接缝的凭据设置/移除。
119
- - **数据**:生成的图片经官方附件存储落盘,受 harness 附件策略约束。配额用量从 `draw/generated` 会话事件折叠 —— 不额外存储任何东西。
120
- - **会话日志**:`draw/generated` 事件记录引擎、模型、标准化请求、字节总量与附件 id —— 审计事实,绝不包含 API 密钥。
119
+ - **数据**:生成的图片经官方附件存储落盘,受 harness 附件策略约束。配额用量从 `draw/generated` 会话事件折叠;在无法安全落盘该事件的宿主上另加内存兜底账簿 —— 除此之外不存储任何东西。
120
+ - **会话日志**:`draw/generated` 事件记录引擎、模型、标准化请求、字节总量与附件 id —— 审计事实,绝不包含 API 密钥。仅当宿主收录该类型或支持 `ignorable` 信封时才落盘(挂载期探测);在 rc.6/rc.7 宿主上载荷改记内存兜底账簿,生成图片不再导致会话重启后无法打开。
121
121
 
122
122
  ## 安全边界
123
123
 
@@ -131,13 +131,14 @@ profile patch 中的覆盖示例:
131
131
  - **仅图像模型。** 无视频、音频或编辑端点;无视觉理解。
132
132
  - **引擎兼容性。** 引擎需支持 OpenAI `POST /images/generations` 形状(base64 或 URL 交付);厂商专属扩展不在范围内。
133
133
  - **成本感知是结构性的。** 插件统计调用次数与字节,但不了解引擎定价 —— 与 `dsh-budget` 配合做成本治理。
134
+ - **rc.6/rc.7 上的配额持久性。** 会话日志无法安全携带 `draw/generated` 的宿主(静态事件白名单、无 `ignorable` 信封)上,配额经内存兜底账簿在活会话内保持精确,但重启后重置;宿主具备插件事件面后恢复持久记账。
134
135
 
135
136
  ## 开发
136
137
 
137
138
  ```sh
138
139
  pnpm install # node ^22.19 || >=24
139
140
  pnpm run typecheck # tsc:src + tests,对照本地 harness checkout
140
- pnpm run typecheck:ci # tsc:对照已发布的 0.1.0-rc.6 类型(无 paths)
141
+ pnpm run typecheck:ci # tsc:对照已发布的 0.1.1-rc.2 类型(无 paths)
141
142
  pnpm test # vitest:77 个测试、11 个套件(scripted 传输、真实 Context/Session/ToolRuntime)
142
143
  pnpm run build # tsc 声明 + tsdown 打包(lib/)
143
144
  pnpm run verify:self-contained # 依赖声明全部来自 registry
package/lib/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { i as statusToView, n as imageToWire, r as probeToWire, t as DRAW_INVOCATIONS } from "./wire-Cc4JZ3jR.js";
2
2
  import { credentialRef } from "@deepseek-ai/dsh-credentials";
3
3
  import z from "@deepseek-ai/schemastery";
4
+ import { Context } from "@deepseek-ai/cordis";
5
+ import SessionStore, { KNOWN_SESSION_EVENT_TYPES, SessionId } from "@deepseek-ai/dsh-session";
4
6
  import { TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
5
- import { SessionId } from "@deepseek-ai/dsh-session";
6
7
  import { AttachmentId } from "@deepseek-ai/dsh-attachment";
7
8
  import { defineTool } from "@deepseek-ai/dsh-tools";
8
9
  //#region src/config.ts
@@ -209,7 +210,9 @@ function engineById(config, id) {
209
210
  //#endregion
210
211
  //#region src/session-events.ts
211
212
  /**
212
- * Append one `draw/generated` event to a session.
213
+ * Append one `draw/generated` event to a session, ungated. Plugin paths use
214
+ * {@link commitDrawGenerated} instead; this raw form remains for tests and
215
+ * hosts that know the type.
213
216
  *
214
217
  * @param session - owning session.
215
218
  * @param event - accounting payload.
@@ -219,6 +222,47 @@ function appendDrawGenerated(session, event) {
219
222
  return session.append("draw/generated", event);
220
223
  }
221
224
  /**
225
+ * In-memory accounting ledger for hosts whose session log cannot carry
226
+ * `draw/generated` safely (rc.6/rc.7 static whitelist, no ignorable envelope).
227
+ * Keyed by Session identity: the entries live exactly as long as the session.
228
+ */
229
+ const fallbackLedger = /* @__PURE__ */ new WeakMap();
230
+ /**
231
+ * Commit one completed generation: append the `draw/generated` event when the
232
+ * gate allows (with the ignorable envelope when required), otherwise record
233
+ * the payload in the in-memory fallback ledger. A failed append degrades to
234
+ * the same ledger and never breaks the draw.
235
+ *
236
+ * @param session - owning session.
237
+ * @param event - accounting payload.
238
+ * @param gate - the adaptive gate (event-gate.ts).
239
+ * @param warn - log warning sink for append failures.
240
+ */
241
+ function commitDrawGenerated(session, event, gate, warn) {
242
+ const decision = gate("draw/generated");
243
+ if (decision.append) try {
244
+ if (decision.ignorable) session.append("draw/generated", event, { ignorable: true });
245
+ else session.append("draw/generated", event);
246
+ return;
247
+ } catch (error) {
248
+ warn(`draw session event append failed: ${error instanceof Error ? error.message : String(error)}`);
249
+ }
250
+ const list = fallbackLedger.get(session) ?? [];
251
+ list.push(event);
252
+ fallbackLedger.set(session, list);
253
+ }
254
+ /**
255
+ * The in-memory fallback records of a session, in commit order. Populated
256
+ * only while the host cannot carry `draw/generated` in the log; quota folds
257
+ * these on top of the logged events.
258
+ *
259
+ * @param session - session whose ledger is read.
260
+ * @returns the fallback payloads.
261
+ */
262
+ function fallbackDrawGeneratedEvents(session) {
263
+ return fallbackLedger.get(session) ?? [];
264
+ }
265
+ /**
222
266
  * Collect the `draw/generated` events of a session in log order. Declaration
223
267
  * merging narrows `event.type === 'draw/generated'` to the payload type.
224
268
  *
@@ -231,9 +275,11 @@ function drawGeneratedEvents(session) {
231
275
  //#endregion
232
276
  //#region src/quota.ts
233
277
  /**
234
- * Fold one session's `draw/generated` events into current usage.
278
+ * Fold one session's `draw/generated` events into current usage: logged events
279
+ * first, then the in-memory fallback ledger (disjoint — a commit lands in
280
+ * exactly one of them).
235
281
  *
236
- * @param session - session whose log is folded.
282
+ * @param session - session whose log and ledger are folded.
237
283
  * @returns generation and byte totals.
238
284
  */
239
285
  function quotaState(session) {
@@ -243,6 +289,10 @@ function quotaState(session) {
243
289
  generations += 1;
244
290
  bytes += event.data.bytes;
245
291
  }
292
+ for (const event of fallbackDrawGeneratedEvents(session)) {
293
+ generations += 1;
294
+ bytes += event.bytes;
295
+ }
246
296
  return {
247
297
  generations,
248
298
  bytes
@@ -366,15 +416,18 @@ var Drawer = class {
366
416
  config;
367
417
  router;
368
418
  deps;
419
+ events;
369
420
  /**
370
421
  * @param config - resolved plugin configuration.
371
422
  * @param router - the engine router.
372
423
  * @param deps - per-call dependencies (public: the remote service reads them for probes).
424
+ * @param events - the gated session-event sink (log when safe, in-memory ledger otherwise).
373
425
  */
374
- constructor(config, router, deps) {
426
+ constructor(config, router, deps, events) {
375
427
  this.config = config;
376
428
  this.router = router;
377
429
  this.deps = deps;
430
+ this.events = events;
378
431
  }
379
432
  /**
380
433
  * Run one generation end to end: normalize and validate, check quota,
@@ -461,7 +514,7 @@ var Drawer = class {
461
514
  ...ref.name !== void 0 ? { name: ref.name } : {}
462
515
  }));
463
516
  const elapsedMs = Date.now() - startedAt;
464
- appendDrawGenerated(session, {
517
+ commitDrawGenerated(session, {
465
518
  engine: routed.engine,
466
519
  model: routed.model,
467
520
  source: options.source,
@@ -472,7 +525,7 @@ var Drawer = class {
472
525
  bytes: totalBytes,
473
526
  attachmentIds: images.map((image) => image.attachmentId),
474
527
  elapsedMs
475
- });
528
+ }, this.events.gate, this.events.warn);
476
529
  const after = checkQuotaGenerations(session, limits);
477
530
  return {
478
531
  ok: true,
@@ -504,6 +557,78 @@ var Drawer = class {
504
557
  }
505
558
  };
506
559
  //#endregion
560
+ //#region src/event-gate.ts
561
+ /**
562
+ * The session-event adaptive gate. `draw/generated` is declared only in this
563
+ * package's types, so it sits outside every host's `KNOWN_SESSION_EVENT_TYPES`:
564
+ * hosts whose session reader validates a static whitelist (rc.6/rc.7) refuse to
565
+ * reopen a session containing the event unless it carries the `ignorable`
566
+ * envelope — and those hosts' `Session.append` cannot stamp it. The gate
567
+ * therefore appends only when the type is host-known or a mount-time probe
568
+ * proves envelope support; otherwise the accounting payload goes to the
569
+ * in-memory fallback ledger (see session-events.ts) so quota keeps working for
570
+ * the live session without poisoning the log.
571
+ *
572
+ * @module dsh-draw/event-gate
573
+ */
574
+ /**
575
+ * Build the gate: host-known types append plainly; unknown types append with
576
+ * the ignorable envelope only when the host append honors it; everything else
577
+ * is skipped — an unknown, unmarked event would make the session refuse to
578
+ * load on restart under the rc.6/rc.7 static whitelist.
579
+ *
580
+ * @param knownTypes - the host's KNOWN_SESSION_EVENT_TYPES.
581
+ * @param ignorableAppend - whether the host append stamps the envelope.
582
+ * @returns the decision function.
583
+ */
584
+ function makeEventGate(knownTypes, ignorableAppend = false) {
585
+ return (type) => {
586
+ if (knownTypes.has(type)) return {
587
+ append: true,
588
+ ignorable: false
589
+ };
590
+ if (ignorableAppend) return {
591
+ append: true,
592
+ ignorable: true
593
+ };
594
+ return {
595
+ append: false,
596
+ ignorable: false
597
+ };
598
+ };
599
+ }
600
+ /**
601
+ * Probe whether the host `Session.append` stamps the ignorable envelope. Runs
602
+ * on a fully detached SessionStore — never host persistence — appending one
603
+ * probe event with `{ ignorable: true }` and reading the marker back. rc.6's
604
+ * append silently drops the unknown options key (probe false); every failure
605
+ * is contained as "unsupported".
606
+ *
607
+ * @returns whether the envelope is honored.
608
+ */
609
+ function probeIgnorableAppend() {
610
+ try {
611
+ return new SessionStore(new Context()).create().append("draw/generated", {
612
+ engine: "probe",
613
+ model: "probe",
614
+ source: "tool",
615
+ prompt: "probe",
616
+ size: "auto",
617
+ quality: "auto",
618
+ count: 0,
619
+ bytes: 0,
620
+ attachmentIds: [],
621
+ elapsedMs: 0
622
+ }, { ignorable: true })?.ignorable === true;
623
+ } catch {
624
+ return false;
625
+ }
626
+ }
627
+ /** The gate over the host's known types plus the probed envelope support. */
628
+ function makeHostEventGate() {
629
+ return makeEventGate(KNOWN_SESSION_EVENT_TYPES, probeIgnorableAppend());
630
+ }
631
+ //#endregion
507
632
  //#region src/http.ts
508
633
  /**
509
634
  * A transport-level failure. `status` is absent for network/timeout failures;
@@ -1066,7 +1191,7 @@ var EngineRouter = class {
1066
1191
  * @module dsh-draw/version
1067
1192
  */
1068
1193
  /** Plugin version; must equal the `version` field in `package.json`. */
1069
- const PLUGIN_VERSION = "0.1.0";
1194
+ const PLUGIN_VERSION = "0.1.2";
1070
1195
  //#endregion
1071
1196
  //#region src/service.ts
1072
1197
  /**
@@ -1415,6 +1540,9 @@ async function apply(ctx, config) {
1415
1540
  },
1416
1541
  attachments: () => ctx.get("attachments"),
1417
1542
  sessions: () => ctx.get("sessions")
1543
+ }, {
1544
+ gate: makeHostEventGate(),
1545
+ warn: (message) => logger.warn(message)
1418
1546
  });
1419
1547
  ctx.effect(() => ctx.tools.register(imageGenerateTool(drawer, resolved)), "dsh-draw: image_generate tool");
1420
1548
  await ctx.plugin(DrawService, {
@@ -1426,4 +1554,4 @@ async function apply(ctx, config) {
1426
1554
  logger.info(`image generation enabled: ${resolved.engines.map((engine) => engine.id).join(", ")} (preferred ${resolved.defaultEngine})`);
1427
1555
  }
1428
1556
  //#endregion
1429
- export { Config, DEFAULT_ENGINES, DRAW_INVOCATIONS, DrawService, Drawer, EngineCallError, EngineRouter, HttpError, PLUGIN_VERSION, REDACTED, appendDrawGenerated, apply, callEngine, checkQuotaBytes, checkQuotaGenerations, defaultHttpTransport, drawGeneratedEvents, engineById, fusedSignal, imageGenerateTool, imageToWire, inject, name, normalizeRequest, probeToWire, quotaState, resolveConfig, sanitizeError, sanitizeText, sanitizeUrl, statusToView, translateRequest };
1557
+ export { Config, DEFAULT_ENGINES, DRAW_INVOCATIONS, DrawService, Drawer, EngineCallError, EngineRouter, HttpError, PLUGIN_VERSION, REDACTED, appendDrawGenerated, apply, callEngine, checkQuotaBytes, checkQuotaGenerations, commitDrawGenerated, defaultHttpTransport, drawGeneratedEvents, engineById, fallbackDrawGeneratedEvents, fusedSignal, imageGenerateTool, imageToWire, inject, makeEventGate, makeHostEventGate, name, normalizeRequest, probeIgnorableAppend, probeToWire, quotaState, resolveConfig, sanitizeError, sanitizeText, sanitizeUrl, statusToView, translateRequest };
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import { type ReactElement } from 'react';
10
10
  import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
11
- import type { PresentedDrawPanel } from './present.ts';
11
+ import type { PresentedDrawPanel } from './present.js';
12
12
  /** Registration-side injected face: the panel RPCs (RemoteResult already unwrapped). */
13
13
  export interface DrawSettingsTabInjected {
14
14
  /** Read the panel snapshot. */
@@ -8,11 +8,11 @@
8
8
  * @module dsh-draw/client
9
9
  */
10
10
  import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
11
- import { type DrawLocaleKey } from './locales.ts';
12
- export type { DrawResultCardInjected, DrawResultCardProps } from './DrawResultCard.tsx';
13
- export type { DrawSettingsTabInjected, DrawSettingsTabProps } from './DrawSettingsTab.tsx';
14
- export type { DrawLocaleKey } from './locales.ts';
15
- export { presentDrawPanel, presentDrawResult, type PresentedDrawPanel, type PresentedDrawResult, type PresentedEngineRow } from './present.ts';
11
+ import { type DrawLocaleKey } from './locales.js';
12
+ export type { DrawResultCardInjected, DrawResultCardProps } from './DrawResultCard.js';
13
+ export type { DrawSettingsTabInjected, DrawSettingsTabProps } from './DrawSettingsTab.js';
14
+ export type { DrawLocaleKey } from './locales.js';
15
+ export { presentDrawPanel, presentDrawResult, type PresentedDrawPanel, type PresentedDrawResult, type PresentedEngineRow } from './present.js';
16
16
  declare module '@deepseek-ai/dsh-client-ui-slots' {
17
17
  interface LocaleNamespaceMap {
18
18
  /** Image-generation card and panel copy. */
@@ -7,7 +7,7 @@
7
7
  * @module dsh-draw/client/present
8
8
  */
9
9
  import type { ToolCallBlock } from '@deepseek-ai/dsh-client-runtime/client';
10
- import type { DrawStatusSnapshot } from '../wire.ts';
10
+ import type { DrawStatusSnapshot } from '../wire.js';
11
11
  /** One image of the presented result card. */
12
12
  export interface PresentedImage {
13
13
  attachmentId: string;
@@ -8,7 +8,7 @@
8
8
  * @module dsh-draw/client/remote
9
9
  */
10
10
  import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
11
- import type { CredentialActionResult, DrawProbeResult, DrawRegenerateResult, DrawStatusSnapshot } from '../wire.ts';
11
+ import type { CredentialActionResult, DrawProbeResult, DrawRegenerateResult, DrawStatusSnapshot } from '../wire.js';
12
12
  declare module '@deepseek-ai/dsh-typert-protocol' {
13
13
  interface TypertRemoteNamespace$draw {
14
14
  /** Read the panel snapshot (engines, health, credentials, quota). */
@@ -8,10 +8,11 @@
8
8
  */
9
9
  import type { AttachmentStore } from '@deepseek-ai/dsh-attachment';
10
10
  import type { Session, SessionStore } from '@deepseek-ai/dsh-session';
11
- import type { ResolvedConfig } from './config.ts';
12
- import type { EngineDeps } from './engine.ts';
13
- import { type QuotaLimits, type QuotaState } from './quota.ts';
14
- import { EngineRouter, type AttemptView } from './router.ts';
11
+ import type { ResolvedConfig } from './config.js';
12
+ import type { EngineDeps } from './engine.js';
13
+ import { type QuotaLimits, type QuotaState } from './quota.js';
14
+ import { EngineRouter, type AttemptView } from './router.js';
15
+ import type { EventGate } from './event-gate.js';
15
16
  /** One durable result image as the canonical value and wire carry it. */
16
17
  export interface DrawImage {
17
18
  /** Opaque attachment id. */
@@ -85,6 +86,13 @@ export interface DrawerDeps {
85
86
  /** Per-call session store accessor for the regenerate path. */
86
87
  sessions?: () => SessionStore | undefined;
87
88
  }
89
+ /** How the drawer commits the `draw/generated` accounting event. */
90
+ export interface DrawerEventSink {
91
+ /** The adaptive session-event gate, probed at mount. */
92
+ gate: EventGate;
93
+ /** Log warning sink for append failures. */
94
+ warn: (message: string) => void;
95
+ }
88
96
  /**
89
97
  * The shared generation path.
90
98
  */
@@ -92,12 +100,14 @@ export declare class Drawer {
92
100
  private readonly config;
93
101
  private readonly router;
94
102
  readonly deps: DrawerDeps;
103
+ private readonly events;
95
104
  /**
96
105
  * @param config - resolved plugin configuration.
97
106
  * @param router - the engine router.
98
107
  * @param deps - per-call dependencies (public: the remote service reads them for probes).
108
+ * @param events - the gated session-event sink (log when safe, in-memory ledger otherwise).
99
109
  */
100
- constructor(config: ResolvedConfig, router: EngineRouter, deps: DrawerDeps);
110
+ constructor(config: ResolvedConfig, router: EngineRouter, deps: DrawerDeps, events: DrawerEventSink);
101
111
  /**
102
112
  * Run one generation end to end: normalize and validate, check quota,
103
113
  * route through the engine chain, commit images to the attachment store,
@@ -1 +1 @@
1
- {"version":3,"file":"drawer.d.ts","sourceRoot":"","sources":["../../src/drawer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"drawer.d.ts","sourceRoot":"","sources":["../../src/drawer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAsB,MAAM,6BAA6B,CAAA;AACtF,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,aAAa,CAAA;AAC5D,OAAO,EAA0C,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAA;AACtG,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAE5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAGhD,yEAAyE;AACzE,MAAM,WAAW,SAAS;IACxB,4BAA4B;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,oBAAoB;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,yBAAyB;AACzB,MAAM,WAAW,WAAW;IAC1B,oBAAoB;IACpB,EAAE,EAAE,IAAI,CAAA;IACR,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAA;IACd,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,qDAAqD;IACrD,IAAI,EAAE,QAAQ,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,CAAA;IAClD,6BAA6B;IAC7B,MAAM,EAAE,SAAS,SAAS,EAAE,CAAA;IAC5B,6CAA6C;IAC7C,KAAK,EAAE,UAAU,CAAA;IACjB,6BAA6B;IAC7B,MAAM,EAAE,WAAW,CAAA;IACnB,2DAA2D;IAC3D,YAAY,EAAE,OAAO,CAAA;IACrB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAA;IACjB,2CAA2C;IAC3C,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAA;CACjC;AAED,kDAAkD;AAClD,MAAM,MAAM,iBAAiB,GACzB,gBAAgB,GAChB,mBAAmB,GACnB,aAAa,GACb,YAAY,GACZ,yBAAyB,GACzB,oBAAoB,CAAA;AAExB,6EAA6E;AAC7E,MAAM,WAAW,WAAW;IAC1B,oBAAoB;IACpB,EAAE,EAAE,KAAK,CAAA;IACT,oCAAoC;IACpC,MAAM,EAAE,iBAAiB,CAAA;IACzB,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,uDAAuD;IACvD,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,SAAS,WAAW,EAAE,CAAA;CAClC;AAED,oBAAoB;AACpB,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,WAAW,CAAA;AAEnD,kCAAkC;AAClC,MAAM,WAAW,WAAW;IAC1B,2BAA2B;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,4EAA4E;IAC5E,OAAO,EAAE,OAAO,GAAG,SAAS,CAAA;IAC5B,kEAAkE;IAClE,MAAM,EAAE,MAAM,GAAG,YAAY,CAAA;CAC9B;AAED,sFAAsF;AACtF,MAAM,WAAW,UAAU;IACzB,kDAAkD;IAClD,MAAM,EAAE,UAAU,CAAA;IAClB,iFAAiF;IACjF,WAAW,CAAC,EAAE,MAAM,eAAe,GAAG,SAAS,CAAA;IAC/C,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,YAAY,GAAG,SAAS,CAAA;CAC1C;AAED,oEAAoE;AACpE,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,IAAI,EAAE,SAAS,CAAA;IACf,4CAA4C;IAC5C,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC;AAED;;GAEG;AACH,qBAAa,MAAM;IAQf,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,QAAQ,CAAC,IAAI,EAAE,UAAU;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAVzB;;;;;OAKG;IACH,YACmB,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,YAAY,EAC5B,IAAI,EAAE,UAAU,EACR,MAAM,EAAE,eAAe,EACtC;IAEJ;;;;;;;;OAQG;IACG,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAiFxE;IAED,yFAAyF;YAC3E,UAAU;CAqBzB"}
@@ -7,7 +7,7 @@
7
7
  * @module dsh-draw/drawer
8
8
  */
9
9
  import { checkQuotaBytes, checkQuotaGenerations } from './quota.js';
10
- import { appendDrawGenerated } from './session-events.js';
10
+ import { commitDrawGenerated } from './session-events.js';
11
11
  import { normalizeRequest } from './translate.js';
12
12
  /**
13
13
  * The shared generation path.
@@ -16,15 +16,18 @@ export class Drawer {
16
16
  config;
17
17
  router;
18
18
  deps;
19
+ events;
19
20
  /**
20
21
  * @param config - resolved plugin configuration.
21
22
  * @param router - the engine router.
22
23
  * @param deps - per-call dependencies (public: the remote service reads them for probes).
24
+ * @param events - the gated session-event sink (log when safe, in-memory ledger otherwise).
23
25
  */
24
- constructor(config, router, deps) {
26
+ constructor(config, router, deps, events) {
25
27
  this.config = config;
26
28
  this.router = router;
27
29
  this.deps = deps;
30
+ this.events = events;
28
31
  }
29
32
  /**
30
33
  * Run one generation end to end: normalize and validate, check quota,
@@ -92,7 +95,7 @@ export class Drawer {
92
95
  ...(ref.name !== undefined ? { name: ref.name } : {}),
93
96
  }));
94
97
  const elapsedMs = Date.now() - startedAt;
95
- appendDrawGenerated(session, {
98
+ commitDrawGenerated(session, {
96
99
  engine: routed.engine,
97
100
  model: routed.model,
98
101
  source: options.source,
@@ -103,7 +106,7 @@ export class Drawer {
103
106
  bytes: totalBytes,
104
107
  attachmentIds: images.map(image => image.attachmentId),
105
108
  elapsedMs,
106
- });
109
+ }, this.events.gate, this.events.warn);
107
110
  const after = checkQuotaGenerations(session, limits);
108
111
  return {
109
112
  ok: true,
@@ -8,9 +8,9 @@
8
8
  * @module dsh-draw/engine
9
9
  */
10
10
  import type { ImageMediaType } from '@deepseek-ai/dsh-attachment';
11
- import type { ResolvedEngineConfig } from './config.ts';
12
- import { type HttpTransport } from './http.ts';
13
- import type { TranslatedImageRequest } from './translate.ts';
11
+ import type { ResolvedEngineConfig } from './config.js';
12
+ import { type HttpTransport } from './http.js';
13
+ import type { TranslatedImageRequest } from './translate.js';
14
14
  /** Failure phases of one engine call, each mapping to a router decision. */
15
15
  export type EngineFailurePhase = 'credential' | 'request' | 'parse';
16
16
  /**