dsh-claude-move 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  All notable changes to this project are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow [SemVer](https://semver.org/).
4
4
 
5
+ ## [0.4.0] - 2026-09-04
6
+
7
+ ### Added
8
+
9
+ - Dual-baseline `sessionPersistence` runtime shim in `index.mjs` (feature-detected by API shape, never by version): the published legacy API (`create`/`append`/`readFrom`, `list()` returning headers) and the unreleased checkout handle seam (`create` returning a `SessionHandle`, `list()`/`stat()` returning snapshots) both work. On the handle path every append is followed by `flush()` (durability barrier) and a paired `close()` (single-writer ownership, released in `finally` on failure); headers are stamped with the backend's current format version (backend-declared `generationFormat.currentVersion`, falling back to a lazy `@deepseek-ai/dsh-session` `SESSION_FORMAT_VERSION` import — never hardcoded) plus an explicit `isSeeded: false`, and missing assistant model sources fall back to the provider string (the checkout backend refuses to read v-mismatched headers or empty model sources — found and fixed by checkout round-trip verification). The legacy path keeps the exact 0.3.x call sequence with no normalization. `lib/` is untouched.
10
+ - Guard against silently clearing `imports.json`: `annotateImports` now resolves `header.id` from both header and snapshot list elements, and throws loudly (skipping `cleanStale`) when an element cannot be resolved — on the handle baseline the old code read `header.id` as `undefined` and `cleanStale` would have wiped every mapping.
11
+ - Dual test suites: a handle-shape `sessionPersistence` mock (create→handle with `read`/`append`/`flush`/`close`, `open(id, access)`, snapshot `list()`) covering the full import/incremental/force/streamed/export flows, paired close (no handle leaks), single-writer conflict loud failures, flush ordering, and header/model normalization; plus legacy-shape regression tests for the mis-clear mapping guard.
12
+
13
+ ### Changed
14
+
15
+ - `compat.yml` profile pins moved from `0.1.1-rc.2` to `0.1.2-rc.1`, with a workflow note that no published version carries the handle seam, so the handle path can only be verified against a local checkout.
16
+
17
+ ### Notes
18
+
19
+ - No data-format changes: `index.json`/`imports.json`/`move.json` caches and imported session logs stay interoperable; no re-import needed; the Config schema is unchanged.
20
+
5
21
  ## [0.3.3] - 2026-09-02
6
22
 
7
23
  ### Changed
package/README.es.md CHANGED
@@ -25,6 +25,7 @@
25
25
 
26
26
  - Dirigido a `dsh 0.1.2-alpha.5` (perfil web); las dependencias peer requieren `>=0.1.0-rc.8 <0.2.0`. Node `^22.19 || >=24`.
27
27
  0.1.2-alpha.5 (adaptado el 2026-09-02): el sobre de sesión conserva su campo ignorable solo para compatibilidad de lectura de logs almacenados - Session.append aún no puede estamparlo, por lo que el comportamiento de la puerta no cambia.
28
+ - 0.4.0 incluye un shim de doble línea base para `sessionPersistence` en tiempo de ejecución, detectado por la forma de la API (nunca por versión): funcionan tanto la API heredada publicada (`create`/`append`/`readFrom`, `list()` devolviendo cabeceras) como la costura de handle del checkout aún no publicado (`create` devolviendo un `SessionHandle`, `list()`/`stat()` devolviendo instantáneas). En la ruta handle, cada append va seguido de `flush()` (barrera de durabilidad) y un `close()` emparejado (propiedad de escritor único); las cabeceras se sellan con la versión de formato actual del backend y un `isSeeded` explícito, y las fuentes de modelo de asistente ausentes caen al provider — verificado contra el backend real 0.1.3-alpha.1 del checkout. La limpieza del escaneo de importación se niega a ejecutarse cuando el `header.id` de un elemento listado no se puede resolver, por lo que `imports.json` nunca se vacía en silencio. Ninguna versión publicada lleva la costura de handle, así que la ruta handle solo se verifica contra un checkout local (el workflow de compatibilidad cubre la línea publicada).
28
29
  - Última verificación con una instalación nueva desde tarball: escaneo real, importación por lotes real (reimportación idempotente), adjuntado al workspace y artefactos de persistencia confirmados; macOS/Linux cubiertos por la matriz de CI.
29
30
 
30
31
  ### Matriz de compatibilidad (solo costuras públicas)
@@ -32,7 +33,8 @@
32
33
  | Superficie | Uso | Respaldo cuando falta |
33
34
  |---|---|---|
34
35
  | Servicios de host (`tools` / `sessionPersistence` / `workspaceRegistry` / `commands` / `systemPrompt` / `skills` / `webServer`) | obligatorio donde se indica | los servicios opcionales se registran reactivamente; la falta de `fs` falla en voz alta |
35
- | `sessionPersistence.listSnapshots` / `readFrom` / `fs` con capacidad `streamText` / `ctx.jobs` / `ctx.agents.resume` | detectado por característica | `list()` / lectura de archivo completo con rechazo en voz alta / mapa de jobs propio / inyección de traspaso |
36
+ | `sessionPersistence` doble línea base: heredada `listSnapshots` / `readFrom` / `append` frente a handle `open` / `stat` / `list()` de instantáneas | detectado por característica en tiempo de ejecución (forma de API, nunca versión) | la guarda de resolución de `header.id` aborta el escaneo en voz alta en vez de vaciar `imports.json` en silencio |
37
+ | `fs` con capacidad `streamText` / `ctx.jobs` / `ctx.agents.resume` | detectado por característica | lectura de archivo completo con rechazo en voz alta / mapa de jobs propio / inyección de traspaso |
36
38
  | Servicios de shell del cliente (`sessions.refresh/open`, `workspaces.refresh`) | detectado por característica al aplicar el panel | recarga completa de la página |
37
39
  | Las capacidades de plataforma más nuevas nunca son requisitos estrictos — el plugin sigue arrancando en rc.8. | | |
38
40
 
@@ -308,6 +310,10 @@ Este proyecto es uno de los [33 complementos de DeepSeek Harness](https://github
308
310
  | **[dsh-dsh-test-drive](https://github.com/PerryLink/dsh-dsh-test-drive)** | Pruebas de instalación y humo aisladas para plugins de DeepSeek Harness. | |
309
311
  | **[dsh-dsh-translate](https://github.com/PerryLink/dsh-dsh-translate)** | Traducción de parámetros entre proveedores y reparación determinista de JSON para DeepSeek Harness. | |
310
312
 
313
+ ### Instalar desde el mercado de DSH Desktop
314
+
315
+ Todos los plugins de PerryLink pueden explorarse en el mercado integrado de DSH Desktop: **Market → Sources → add source → pegar** `https://perrylink-dsh-catalog.perrylink.workers.dev/catalog-source.json` **→ seleccionarlo**. La instalación sigue pasando por la verificación de identidad npm del mercado y tu confirmación.
316
+
311
317
  ## Licencia
312
318
 
313
319
  [Apache License 2.0](LICENSE) © 2026 dsh-claude-move contributors
package/README.hi.md CHANGED
@@ -25,6 +25,7 @@
25
25
 
26
26
  - `dsh 0.1.2-alpha.5` (web profile) को लक्षित करता है; peer निर्भरताएँ `>=0.1.0-rc.8 <0.2.0` चाहिए। Node `^22.19 || >=24`।
27
27
  0.1.2-alpha.5 (2026-09-02 को अनुकूलित): सत्र लिफ़ाफ़ा अपना ignorable फ़ील्ड केवल संग्रहीत-लॉग पठन संगतता के लिए रखता है - Session.append अभी भी इसे स्टैम्प नहीं कर सकता, इसलिए गेट व्यवहार अपरिवर्तित है।
28
+ - 0.4.0 में `sessionPersistence` के लिए दोहरी-बेसलाइन रनटाइम shim शामिल है, जिसे API आकार से पहचाना जाता है (संस्करण से कभी नहीं): प्रकाशित लीगेसी API (`create`/`append`/`readFrom`, `list()` हेडर लौटाता है) और अप्रकाशित checkout की हैंडल सीम (`create` एक `SessionHandle` लौटाता है, `list()`/`stat()` स्नैपशॉट लौटाते हैं) दोनों काम करते हैं। हैंडल पथ पर हर append के बाद `flush()` (स्थायित्व अवरोध) और युग्मित `close()` (एकल-लेखक स्वामित्व) होता है; हेडर पर बैकएंड का वर्तमान प्रारूप संस्करण और स्पष्ट `isSeeded` अंकित होता है, तथा अनुपस्थित assistant model स्रोत provider पर गिरते हैं — वास्तविक 0.1.3-alpha.1 बैकएंड के विरुद्ध checkout-सत्यापित। जब किसी सूचीबद्ध तत्व का `header.id` हल नहीं हो पाता तो आयात-स्कैन की सफ़ाई चलने से मना कर देती है, इसलिए `imports.json` कभी चुपचाप खाली नहीं होता। किसी भी प्रकाशित संस्करण में हैंडल सीम नहीं है, इसलिए हैंडल पथ केवल स्थानीय checkout के विरुद्ध सत्यापित होता है (compat वर्कफ़्लो प्रकाशित लाइन को कवर करता है)।
28
29
  - अंतिम बार एक ताज़ा tarball इंस्टॉल के विरुद्ध सत्यापित: वास्तविक स्कैन, वास्तविक बैच आयात (आइडेम्पोटेंट पुनः-आयात), workspace जुड़ाव और पर्सिस्टेंस आर्टिफैक्ट की पुष्टि; macOS/Linux CI मैट्रिक्स द्वारा कवर हैं।
29
30
 
30
31
  ### अनुकूलता मैट्रिक्स (केवल सार्वजनिक सीम)
@@ -32,7 +33,8 @@
32
33
  | सतह | उपयोग | अनुपस्थित होने पर फ़ॉलबैक |
33
34
  |---|---|---|
34
35
  | Host सेवाएँ (`tools` / `sessionPersistence` / `workspaceRegistry` / `commands` / `systemPrompt` / `skills` / `webServer`) | जहाँ सूचीबद्ध वहाँ आवश्यक | वैकल्पिक सेवाएँ प्रतिक्रियात्मक रूप से पंजीकृत होती हैं; `fs` अनुपस्थित होने पर ज़ोर से विफल |
35
- | `sessionPersistence.listSnapshots` / `readFrom` / `streamText`-सक्षम `fs` / `ctx.jobs` / `ctx.agents.resume` | फ़ीचर-डिटेक्टेड | `list()` / पूरी फ़ाइल पढ़ना और ज़ोर से अस्वीकार / स्वयं का job map / हैंडऑफ़ इंजेक्ट |
36
+ | `sessionPersistence` दोहरी बेसलाइन: लीगेसी `listSnapshots` / `readFrom` / `append` बनाम हैंडल `open` / `stat` / स्नैपशॉट `list()` | रनटाइम पर फ़ीचर-डिटेक्टेड (API आकार, संस्करण कभी नहीं) | `header.id` रिज़ॉल्यूशन गार्ड स्कैन को ज़ोर से रोक देता है, `imports.json` चुपचाप खाली करने के बजाय |
37
+ | `streamText`-सक्षम `fs` / `ctx.jobs` / `ctx.agents.resume` | फ़ीचर-डिटेक्टेड | पूरी फ़ाइल पढ़ना और ज़ोर से अस्वीकार / स्वयं का job map / हैंडऑफ़ इंजेक्ट |
36
38
  | Client shell सेवाएँ (`sessions.refresh/open`, `workspaces.refresh`) | पैनल apply पर फ़ीचर-डिटेक्टेड | पूर्ण-पृष्ठ रीलोड |
37
39
  | नई प्लेटफ़ॉर्म क्षमताएँ कभी कठोर आवश्यकताएँ नहीं होतीं — प्लगइन rc.8 पर बूट होने योग्य रहता है। | | |
38
40
 
@@ -308,6 +310,10 @@ CI GitHub Actions ([test.yml](.github/workflows/test.yml)) के माध्
308
310
  | **[dsh-dsh-test-drive](https://github.com/PerryLink/dsh-dsh-test-drive)** | DeepSeek Harness प्लगिनों के लिए पृथक इंस्टॉल-एंड-स्मोक टेस्ट ड्राइव। | |
309
311
  | **[dsh-dsh-translate](https://github.com/PerryLink/dsh-dsh-translate)** | DeepSeek Harness के लिए वेंडर पैरामीटर अनुवाद और नियतात्मक JSON मरम्मत। | |
310
312
 
313
+ ### DSH Desktop मार्केट से इंस्टॉल करें
314
+
315
+ सभी PerryLink प्लगइन DSH Desktop के बिल्ट-इन मार्केट में देखे जा सकते हैं: **Market → Sources → add source → पेस्ट करें** `https://perrylink-dsh-catalog.perrylink.workers.dev/catalog-source.json` **→ चुनें**। इंस्टॉलेशन मार्केट के npm-identity सत्यापन और आपकी पुष्टि से ही होता है।
316
+
311
317
  ## लाइसेंस
312
318
 
313
319
  [Apache License 2.0](LICENSE) © 2026 dsh-claude-move contributors
package/README.md CHANGED
@@ -26,6 +26,7 @@
26
26
 
27
27
  - Targets `dsh 0.1.2-alpha.5` (web profile); peer dependencies require `>=0.1.0-rc.8 <0.2.0`. Node `^22.19 || >=24`.
28
28
  0.1.2-alpha.5 (adapted 2026-09-02): the session envelope keeps its ignorable field for stored-log read compatibility only - Session.append still cannot stamp it, so audit-gate behavior is unchanged.
29
+ - 0.4.0 ships a dual-baseline `sessionPersistence` runtime shim, feature-detected by API shape (never by version): the published legacy API (`create`/`append`/`readFrom`, `list()` returning headers) and the unreleased checkout handle seam (`create` returning a `SessionHandle`, `list()`/`stat()` returning snapshots) both work. On the handle path every append is followed by `flush()` (durability barrier) and a paired `close()` (single-writer ownership); headers are stamped with the backend's current format version plus an explicit `isSeeded`, and missing assistant model sources fall back to the provider — checkout-verified against the real 0.1.3-alpha.1 backend. Import-scan cleanup refuses to run when a listed element's `header.id` cannot be resolved, so `imports.json` is never silently cleared. No published version carries the handle seam, so the handle path is verified against a local checkout only (the compat workflow covers the published line).
29
30
  - Last verified against a fresh tarball install: real scan, real batch import (idempotent re-import), workspace attach and persistence artifacts confirmed; macOS/Linux covered by the CI matrix.
30
31
 
31
32
  ### Compatibility matrix (public seams only)
@@ -33,7 +34,8 @@
33
34
  | Surface | Used | Fallback when absent |
34
35
  |---|---|---|
35
36
  | Host services (`tools` / `sessionPersistence` / `workspaceRegistry` / `commands` / `systemPrompt` / `skills` / `webServer`) | required where listed | optional services register reactively; missing `fs` fails loud |
36
- | `sessionPersistence.listSnapshots` / `readFrom` / `streamText`-capable `fs` / `ctx.jobs` / `ctx.agents.resume` | feature-detected | `list()` / whole-file read with loud rejection / own job map / handoff inject |
37
+ | `sessionPersistence` dual baseline: legacy `listSnapshots` / `readFrom` / `append` vs handle `open` / `stat` / snapshot `list()` | feature-detected at runtime (API shape, never version) | the `header.id` resolution guard aborts the scan loudly instead of silently clearing `imports.json` |
38
+ | `streamText`-capable `fs` / `ctx.jobs` / `ctx.agents.resume` | feature-detected | whole-file read with loud rejection / own job map / handoff inject |
37
39
  | Client shell services (`sessions.refresh/open`, `workspaces.refresh`) | feature-detected at panel apply | full-page reload |
38
40
  | Newer platform capabilities are never hard requirements — the plugin stays bootable on rc.8. | | |
39
41
 
@@ -309,6 +311,10 @@ This project is one of the [33 DeepSeek Harness plugins](https://github.com/Perr
309
311
  | **[dsh-dsh-test-drive](https://github.com/PerryLink/dsh-dsh-test-drive)** | Isolated install-and-smoke test drives for DeepSeek Harness plugins. | |
310
312
  | **[dsh-dsh-translate](https://github.com/PerryLink/dsh-dsh-translate)** | Vendor parameter translation and deterministic JSON repair for DeepSeek Harness. | |
311
313
 
314
+ ### Install from the DSH Desktop Market
315
+
316
+ All PerryLink plugins are browsable in the built-in DSH Desktop Market: **Market → Sources → add source → paste** `https://perrylink-dsh-catalog.perrylink.workers.dev/catalog-source.json` **→ select it**. Installation still goes through the Market's npm-identity verification and your confirmation.
317
+
312
318
  ## License
313
319
 
314
320
  [Apache License 2.0](LICENSE) © 2026 dsh-claude-move contributors
package/README.pt.md CHANGED
@@ -25,6 +25,7 @@
25
25
 
26
26
  - Direcionado a `dsh 0.1.2-alpha.5` (perfil web); dependências peer exigem `>=0.1.0-rc.8 <0.2.0`. Node `^22.19 || >=24`.
27
27
  0.1.2-alpha.5 (adaptado em 2026-09-02): o envelope de sessão mantém seu campo ignorable apenas para compatibilidade de leitura de logs armazenados - o Session.append ainda não consegue estampá-lo, então o comportamento da porta não muda.
28
+ - A 0.4.0 inclui um shim de linha de base dupla para `sessionPersistence` em tempo de execução, detectado pela forma da API (nunca pela versão): funcionam tanto a API legada publicada (`create`/`append`/`readFrom`, `list()` retornando cabeçalhos) quanto a costura de handle do checkout ainda não publicado (`create` retornando um `SessionHandle`, `list()`/`stat()` retornando snapshots). No caminho handle, todo append é seguido de `flush()` (barreira de durabilidade) e um `close()` pareado (propriedade de escritor único); os cabeçalhos são carimbados com a versão de formato atual do backend e um `isSeeded` explícito, e fontes de modelo de assistente ausentes caem para o provider — verificado contra o backend real 0.1.3-alpha.1 do checkout. A limpeza da varredura de importação se recusa a executar quando o `header.id` de um elemento listado não pode ser resolvido, então `imports.json` nunca é esvaziado silenciosamente. Nenhuma versão publicada tem a costura de handle, então o caminho handle só é verificado contra um checkout local (o fluxo de compatibilidade cobre a linha publicada).
28
29
  - Última verificação contra uma instalação nova de tarball: varredura real, importação em lote real (reimportação idempotente), anexo ao workspace e artefatos de persistência confirmados; macOS/Linux cobertos pela matriz de CI.
29
30
 
30
31
  ### Matriz de compatibilidade (somente costuras públicas)
@@ -32,7 +33,8 @@
32
33
  | Superfície | Uso | Fallback quando ausente |
33
34
  |---|---|---|
34
35
  | Serviços de host (`tools` / `sessionPersistence` / `workspaceRegistry` / `commands` / `systemPrompt` / `skills` / `webServer`) | obrigatório onde listado | serviços opcionais registram reativamente; `fs` ausente falha em voz alta |
35
- | `sessionPersistence.listSnapshots` / `readFrom` / `fs` com capacidade `streamText` / `ctx.jobs` / `ctx.agents.resume` | detectado por recurso | `list()` / leitura de arquivo inteiro com rejeição em voz alta / mapa de jobs próprio / injeção de handoff |
36
+ | `sessionPersistence` linha de base dupla: legada `listSnapshots` / `readFrom` / `append` versus handle `open` / `stat` / `list()` de snapshots | detectado por recurso em tempo de execução (forma da API, nunca versão) | a guarda de resolução de `header.id` aborta a varredura em voz alta em vez de esvaziar `imports.json` silenciosamente |
37
+ | `fs` com capacidade `streamText` / `ctx.jobs` / `ctx.agents.resume` | detectado por recurso | leitura de arquivo inteiro com rejeição em voz alta / mapa de jobs próprio / injeção de handoff |
36
38
  | Serviços de shell do cliente (`sessions.refresh/open`, `workspaces.refresh`) | detectado por recurso ao aplicar o painel | recarga completa da página |
37
39
  | Capacidades de plataforma mais novas nunca são requisitos rígidos — o plugin continua inicializável no rc.8. | | |
38
40
 
@@ -308,6 +310,10 @@ Este projeto é um dos [33 plugins de DeepSeek Harness](https://github.com/Perry
308
310
  | **[dsh-dsh-test-drive](https://github.com/PerryLink/dsh-dsh-test-drive)** | Test drives isolados de instalação e smoke para plugins de DeepSeek Harness. | |
309
311
  | **[dsh-dsh-translate](https://github.com/PerryLink/dsh-dsh-translate)** | Tradução de parâmetros entre fornecedores e reparo determinístico de JSON para DeepSeek Harness. | |
310
312
 
313
+ ### Instalar a partir do mercado do DSH Desktop
314
+
315
+ Todos os plugins PerryLink podem ser explorados no mercado integrado do DSH Desktop: **Market → Sources → add source → colar** `https://perrylink-dsh-catalog.perrylink.workers.dev/catalog-source.json` **→ selecionar**. A instalação continua passando pela verificação de identidade npm do mercado e pela sua confirmação.
316
+
311
317
  ## Licença
312
318
 
313
319
  [Apache License 2.0](LICENSE) © 2026 dsh-claude-move contributors
package/README.zh.md CHANGED
@@ -25,6 +25,7 @@
25
25
 
26
26
  - 面向 `dsh 0.1.2-alpha.5`(web profile);peer 依赖要求 `>=0.1.0-rc.8 <0.2.0`。Node `^22.19 || >=24`。
27
27
  0.1.2-alpha.5(2026-09-02 已适配):会话信封保留 ignorable 字段但仅用于存量日志读取兼容——Session.append 仍无法盖章,门控行为不变。
28
+ - 0.4.0 内置 `sessionPersistence` 双基线运行时 shim,按 API 形状特性探测(绝不按版本号猜测):已发布旧 API(`create`/`append`/`readFrom`,`list()` 返回会话头)与未发布 checkout 的 handle seam(`create` 返回 `SessionHandle`,`list()`/`stat()` 返回快照)都能工作。handle 路径上每次 append 之后必须 `flush()`(耐久屏障)并成对 `close()`(单写所有权);header 盖上后端当前格式版本并显式补 `isSeeded`,缺失的 assistant model 来源回退 provider——已对照真实 0.1.3-alpha.1 后端做 checkout 实测。导入扫描的清理在列表元素的 `header.id` 无法解析时拒绝执行,绝不静默清空 `imports.json`。任何已发布版本都没有 handle seam,因此 handle 路径只能对照本地 checkout 验证(compat workflow 覆盖已发布线)。
28
29
  - 最近针对全新 tarball 安装验证:真实扫描、真实批量导入(幂等重导入)、工作区挂载与持久化产物均已确认;macOS/Linux 由 CI 矩阵覆盖。
29
30
 
30
31
  ### 兼容性矩阵(仅公开接缝)
@@ -32,7 +33,8 @@
32
33
  | 接缝 | 使用 | 缺失时的回退 |
33
34
  |---|---|---|
34
35
  | Host 服务(`tools` / `sessionPersistence` / `workspaceRegistry` / `commands` / `systemPrompt` / `skills` / `webServer`) | 所列之处必需 | 可选服务响应式注册;缺失 `fs` 大声失败 |
35
- | `sessionPersistence.listSnapshots` / `readFrom` / 支持 `streamText` `fs` / `ctx.jobs` / `ctx.agents.resume` | 特性探测 | `list()` / 整文件读取并大声拒绝 / 自有 job map / 交接注入 |
36
+ | `sessionPersistence` 双基线:旧 API(`listSnapshots` / `readFrom` / `append`)或 handle(`open` / `stat`、快照 `list()`) | 运行时按 API 形状特性探测 | `header.id` 解析守卫:解析失败大声中止扫描,绝不静默清空 `imports.json` |
37
+ | 支持 `streamText` 的 `fs` / `ctx.jobs` / `ctx.agents.resume` | 特性探测 | 整文件读取并大声拒绝 / 自有 job map / 交接注入 |
36
38
  | Client shell 服务(`sessions.refresh/open`、`workspaces.refresh`) | 面板 apply 时特性探测 | 整页刷新 |
37
39
  | 较新的平台能力从不是硬性要求 —— 插件在 rc.8 上始终保持可启动。 | | |
38
40
 
@@ -308,6 +310,10 @@ CI 通过 GitHub Actions([test.yml](.github/workflows/test.yml))在 Linux/ma
308
310
  | **[dsh-dsh-test-drive](https://github.com/PerryLink/dsh-dsh-test-drive)** | DeepSeek Harness 插件的隔离试装冒烟。 | |
309
311
  | **[dsh-dsh-translate](https://github.com/PerryLink/dsh-dsh-translate)** | DeepSeek Harness 的厂商参数翻译与确定性 JSON 修复。 | |
310
312
 
313
+ ### 从 DSH Desktop 市场安装
314
+
315
+ 所有 PerryLink 插件均可在 DSH Desktop 内置市场中浏览:**市场 → 来源 → 添加来源 → 粘贴** `https://perrylink-dsh-catalog.perrylink.workers.dev/catalog-source.json` **→ 选中**。安装仍需通过市场的 npm 身份校验与你的确认。
316
+
311
317
  ## 许可证
312
318
 
313
319
  [Apache License 2.0](LICENSE) © 2026 dsh-claude-move contributors
package/index.mjs CHANGED
@@ -10,6 +10,8 @@
10
10
  //
11
11
  // 只消费公开服务:tools / systemPrompt / skills / sessionPersistence /
12
12
  // workspaceRegistry / commands / webServer / approval(后几者经 ctx.get 可选读取)。
13
+ // sessionPersistence 走 0.4.0 双基线运行时 shim(handle 基线 / 旧 API,见下方
14
+ // "双基线运行时 shim" 段),任何已发布 dsh 与 checkout HEAD 均可工作。
13
15
  // 源文件只读,缓存只写 resolveCacheDir();迁移落点只写 $DSH_HOME/skills、
14
16
  // $DSH_HOME/AGENTS.md 与 imports 工作区目录。
15
17
 
@@ -63,6 +65,205 @@ export const name = 'claude-move'
63
65
 
64
66
  export const inject = ['tools']
65
67
 
68
+ // ── sessionPersistence 双基线运行时 shim(0.4.0)──────────────────────────────
69
+ //
70
+ // 宿主 master(0.1.3-alpha.1,bec6805d6a refactor)把 sessionPersistence 改成
71
+ // handle 化:create 返回 SessionHandle(read/append/flush/close;close 释放
72
+ // 单写所有权),服务级 append/readFrom/locate/listSnapshots 移除,list()/stat()
73
+ // 返回快照对象({ header, revision, eventCount?, sizeBytes? })。任何已发布
74
+ // 版本(≤0.1.2-rc.1)都没有该 seam,仍是旧 API:create 返回 void、服务级
75
+ // append/readFrom、list() 返回 SessionHeader、listSnapshots 返回 { header }。
76
+ // 两个基线一律按运行时 API 形状探测(create 返回值 / 服务成员 / list 元素),
77
+ // 绝不按版本号猜测。handle 路径上每次 append 后必须 flush()(耐久屏障)并
78
+ // 成对 close()(单写所有权),header/事件经 normalizeHandleHeader /
79
+ // normalizeHandleEvents 规范化到后端当前格式(checkout 实测:版本戳与
80
+ // isSeeded 缺一不可,model source 必须非空);旧路径的调用序列与 0.3.x
81
+ // 逐字节一致,不做任何规范化。
82
+
83
+ const persistenceShapeCache = new WeakMap()
84
+
85
+ /** create 返回值是否 SessionHandle(handle 基线的最权威判定)。 */
86
+ function isSessionHandle(value) {
87
+ return value !== null && typeof value === 'object'
88
+ && typeof value.read === 'function'
89
+ && typeof value.append === 'function'
90
+ && typeof value.flush === 'function'
91
+ && typeof value.close === 'function'
92
+ }
93
+
94
+ /**
95
+ * 探测服务基线(API 形状,非版本号):有 open(旧 API 无)→ handle;
96
+ * 有 readFrom/append(handle 化后均无)→ legacy;其余未知。create 实测结果
97
+ * 可经 notePersistenceShape 覆写缓存。
98
+ * @param sp - sessionPersistence 服务。
99
+ * @returns 'handle' | 'legacy' | null。
100
+ */
101
+ function detectPersistenceShape(sp) {
102
+ if (!sp) return null
103
+ const cached = persistenceShapeCache.get(sp)
104
+ if (cached !== undefined) return cached
105
+ if (typeof sp.open === 'function') return 'handle'
106
+ if (typeof sp.readFrom === 'function' || typeof sp.append === 'function') return 'legacy'
107
+ return null
108
+ }
109
+
110
+ /** 记录实测基线(create 返回值判定优先,供后续 append/read 调用复用)。 */
111
+ function notePersistenceShape(sp, shape) {
112
+ if (sp && (shape === 'handle' || shape === 'legacy')) persistenceShapeCache.set(sp, shape)
113
+ }
114
+
115
+ /** 列表元素无法解析 header.id 的哨兵错误:annotateImports 用它响亮失败并跳过清理。 */
116
+ class StoredSessionIdResolutionError extends Error {}
117
+
118
+ /**
119
+ * 从 list()/listSnapshots() 元素解析存储会话 id:handle 基线的快照对象取
120
+ * header.id,旧基线的 SessionHeader 取 id。两者都解析不出 → null。
121
+ * @param element - 列表元素。
122
+ * @returns 会话 id 或 null。
123
+ */
124
+ function storedSessionIdOf(element) {
125
+ if (element !== null && typeof element === 'object') {
126
+ if (element.header !== null && typeof element.header === 'object'
127
+ && typeof element.header.id === 'string' && element.header.id.length > 0) {
128
+ return element.header.id
129
+ }
130
+ if (typeof element.id === 'string' && element.id.length > 0) return element.id
131
+ }
132
+ return null
133
+ }
134
+
135
+ /**
136
+ * 列表元素序列 → id 数组。任一元素无法解析 header.id 即响亮抛出——这是
137
+ * cleanStale 误清空防线:id 集合必须完整可解析,绝不允许把 undefined 塞进
138
+ * 集合让「映射指向已不存在会话」全量误判。
139
+ * @param elements - list()/listSnapshots() 返回的元素序列。
140
+ * @returns id 数组。
141
+ */
142
+ function idsOfListedElements(elements) {
143
+ return elements.map((element) => {
144
+ const id = storedSessionIdOf(element)
145
+ if (id === null) {
146
+ throw new StoredSessionIdResolutionError(
147
+ 'sessionPersistence 列表元素无法解析 header.id(既非 SessionHeader 也非快照对象):'
148
+ + 'claude-move 拒绝继续,防止 imports.json 导入映射被误清空')
149
+ }
150
+ return id
151
+ })
152
+ }
153
+
154
+ /**
155
+ * 读一份已存储会话的完整事件与头(双基线):handle 路径 open(id, 'read') →
156
+ * read(0) → close(读句柄绝不持有写所有权);旧路径 readFrom(id, 0)。
157
+ * 基线无法判定返回 null;会话不存在时抛出后端错误。
158
+ * @param sp - sessionPersistence 服务。
159
+ * @param id - 会话 id。
160
+ * @returns `{ meta, events }` 或 null。
161
+ */
162
+ async function readStoredEvents(sp, id) {
163
+ const shape = detectPersistenceShape(sp)
164
+ if (shape === 'handle') {
165
+ const handle = await sp.open(id, 'read')
166
+ try {
167
+ const events = await handle.read(0)
168
+ return { meta: handle.header, events }
169
+ } finally {
170
+ await handle.close()
171
+ }
172
+ }
173
+ if (shape === 'legacy' && typeof sp.readFrom === 'function') {
174
+ const suffix = await sp.readFrom(id, 0)
175
+ return { meta: suffix?.meta, events: suffix?.events }
176
+ }
177
+ return null
178
+ }
179
+
180
+ // handle 基线 header/事件规范化(0.4.0 checkout 实测):checkout 后端给所有
181
+ // 产物盖当前格式版本的文件名,并要求 header.version 等于后端当前
182
+ // SESSION_FORMAT_VERSION、isSeeded 显式给出;读取时还会按当前格式语义校验
183
+ // assistant/message 的 model source 必须是非空字符串。旧基线不需要这些,
184
+ // 规范化只发生在 handle 路径。
185
+
186
+ const handleFormatVersionCache = new WeakMap()
187
+
188
+ /**
189
+ * 惰性读取 handle 基线的当前会话格式版本(绝无硬编码、绝不按版本号猜测):
190
+ * 首选后端实例自报的 generationFormat.currentVersion(checkout JSONL 后端
191
+ * 公开字段),回退宿主 `@deepseek-ai/dsh-session` 的 SESSION_FORMAT_VERSION
192
+ * 导出。两者都不可得返回 null(header 保持原样,后端读取时响亮拒绝,绝不
193
+ * 静默写坏)。
194
+ * @param sp - sessionPersistence 服务。
195
+ * @returns 当前格式版本号或 null。
196
+ */
197
+ async function currentHandleFormatVersion(sp) {
198
+ const cached = handleFormatVersionCache.get(sp)
199
+ if (cached !== undefined) return cached
200
+ let version = null
201
+ const backend = sp?.generationFormat
202
+ if (backend !== null && typeof backend === 'object'
203
+ && Number.isSafeInteger(backend.currentVersion) && backend.currentVersion >= 0) {
204
+ version = backend.currentVersion
205
+ } else {
206
+ try {
207
+ const sessionMod = await import('@deepseek-ai/dsh-session')
208
+ if (Number.isSafeInteger(sessionMod.SESSION_FORMAT_VERSION) && sessionMod.SESSION_FORMAT_VERSION >= 0) {
209
+ version = sessionMod.SESSION_FORMAT_VERSION
210
+ }
211
+ } catch {
212
+ // 宿主 session 包不可解析:保持原 header(读取时响亮失败,绝不静默)。
213
+ }
214
+ }
215
+ handleFormatVersionCache.set(sp, version)
216
+ return version
217
+ }
218
+
219
+ /**
220
+ * handle 基线 header 规范化:version 盖为后端当前格式版本;isSeeded 缺省时
221
+ * 显式置 false(后端序列化会丢弃 undefined,缺 key 的产物不可读)。
222
+ * @param sp - sessionPersistence 服务。
223
+ * @param meta - 原 SessionHeader。
224
+ * @returns 规范化后的新 header 对象(原对象不变)。
225
+ */
226
+ async function normalizeHandleHeader(sp, meta) {
227
+ const header = { ...meta }
228
+ const version = await currentHandleFormatVersion(sp)
229
+ if (typeof version === 'number' && version >= 0) header.version = version
230
+ if (header.isSeeded === undefined) header.isSeeded = false
231
+ return header
232
+ }
233
+
234
+ /**
235
+ * handle 基线的事件批规范化:当前格式语义要求 assistant/message 的 model
236
+ * source 是非空字符串;缺 model 的源记录(真实 Claude transcript 恒有
237
+ * message.model,仅防御合成/历史记录)回退 provider 字符串,避免落盘出
238
+ * 无法读取的会话。未改动时原样返回调用方数组。
239
+ * @param events - 事件批。
240
+ * @returns 规范化后的事件批。
241
+ */
242
+ function normalizeHandleEvents(events) {
243
+ let changed = false
244
+ const out = events.map((event) => {
245
+ if (event?.type !== 'assistant/message') return event
246
+ const source = event.data?.message?.source
247
+ if (source !== null && typeof source === 'object'
248
+ && source.kind === 'model'
249
+ && (typeof source.model !== 'string' || source.model.length === 0)) {
250
+ changed = true
251
+ return {
252
+ ...event,
253
+ data: {
254
+ ...event.data,
255
+ message: {
256
+ ...event.data.message,
257
+ source: { ...source, model: typeof source.provider === 'string' && source.provider.length > 0 ? source.provider : 'claude-code' },
258
+ },
259
+ },
260
+ }
261
+ }
262
+ return event
263
+ })
264
+ return changed ? out : events
265
+ }
266
+
66
267
  /** 批量导入「读取 + 转换」阶段的默认并发上限(落盘阶段保持串行,保证幂等确定性)。 */
67
268
  export const DEFAULT_IMPORT_CONCURRENCY = 4
68
269
 
@@ -350,7 +551,10 @@ export async function annotateSettings(index) {
350
551
 
351
552
  /**
352
553
  * 用 sessionPersistence 列表 + imports 映射标注每个会话的导入状态(F4 幂等基础)。
353
- * 优先 `listSnapshots()`(更便宜的 header+revision 快照),回退 `list()`。
554
+ * 双基线(0.4.0):listSnapshots 优先(快照 .header.id),回退 list()——
555
+ * handle 基线 list() 返回快照对象(header.id),旧基线返回 SessionHeader(id),
556
+ * 统一经 storedSessionIdOf 解析;任一元素解析不出 header.id 时响亮抛出并
557
+ * 跳过 cleanStale,绝不把 undefined 计入集合导致 imports.json 被误清空。
354
558
  * `cleanStale`(全量扫描时)惰性清理「映射指向已不存在会话」的残留记录并
355
559
  * 报告清理条数(B4:用户在 UI 删除导入会话后映射不再残留)。
356
560
  * @param ctx - Cordis 上下文。
@@ -366,14 +570,16 @@ export async function annotateImports(ctx, cacheDir, index, cleanStale = false)
366
570
  if (sp) {
367
571
  try {
368
572
  if (typeof sp.listSnapshots === 'function') {
369
- for (const snap of await sp.listSnapshots()) imported.add(snap.header.id)
370
- listSucceeded = true
573
+ for (const id of idsOfListedElements(await sp.listSnapshots())) imported.add(id)
371
574
  } else if (typeof sp.list === 'function') {
372
- for (const header of await sp.list()) imported.add(header.id)
373
- listSucceeded = true
575
+ for (const id of idsOfListedElements(await sp.list())) imported.add(id)
374
576
  }
375
- } catch {
577
+ listSucceeded = true
578
+ } catch (err) {
579
+ // 元素无法解析 header.id:响亮抛出(cleanStale 随之中止,绝不静默清空映射)。
580
+ if (err instanceof StoredSessionIdResolutionError) throw err
376
581
  // 持久化不可读:全部按未导入处理,也不做清理。
582
+ console.error('[claude-move] sessionPersistence 列表读取失败:', String((err && err.message) || err))
377
583
  listSucceeded = false
378
584
  }
379
585
  }
@@ -521,13 +727,14 @@ export function resolveImportTarget(raw, claudeHome) {
521
727
  return path.resolve(expanded)
522
728
  }
523
729
 
524
- /** 已持久化会话 id 集合(批量导入一次快照,避免逐文件 O(n) 列表)。 */
730
+ /** 已持久化会话 id 集合(批量导入一次快照,避免逐文件 O(n) 列表;双基线快照/头解析)。 */
525
731
  async function listPersistedIds(ctx) {
526
732
  const sp = ctx.get('sessionPersistence')
527
733
  if (!sp || typeof sp.list !== 'function') return new Set()
528
734
  try {
529
- return new Set((await sp.list()).map((h) => h.id))
530
- } catch {
735
+ return new Set(idsOfListedElements(await sp.list()))
736
+ } catch (err) {
737
+ if (err instanceof StoredSessionIdResolutionError) throw err
531
738
  return new Set()
532
739
  }
533
740
  }
@@ -752,12 +959,12 @@ function unwrapImport(entry) {
752
959
  return null
753
960
  }
754
961
 
755
- /** 读取已存储日志的事件数(服务支持 readFrom 时);不可用返回 null。 */
962
+ /** 读取已存储日志的事件数(双基线 readFrom / open+read);不可用返回 null。 */
756
963
  async function storedEventCount(ctx, dshId) {
757
964
  const sp = ctx.get('sessionPersistence')
758
- if (!sp || typeof sp.readFrom !== 'function') return null
965
+ if (!sp || detectPersistenceShape(sp) === null) return null
759
966
  try {
760
- const read = await sp.readFrom(dshId, 0)
967
+ const read = await readStoredEvents(sp, dshId)
761
968
  return Array.isArray(read?.events) ? read.events.length : null
762
969
  } catch {
763
970
  return null
@@ -769,7 +976,8 @@ async function storedEventCount(ctx, dshId) {
769
976
  * 复制式语义,绝不删除/改写既有内容:
770
977
  * - 首次导入:目标 id 由「显式 sessionId > 源 sessionId > 文件名 slug」确定,
771
978
  * 若目标 id 已被占用则后缀避让(import-<src>-<n>),绝不静默丢弃历史;
772
- * 落盘 = create + append(append-only),随后按 cwd 挂接工作区。
979
+ * 落盘 = create + append(append-only;handle 基线 append 后 flush + close,
980
+ * 见双基线 shim),随后按 cwd 挂接工作区。
773
981
  * - 重复导入且源文件已增长(turns 变多):把新增轮次以连续 seq 续写到同一
774
982
  * DSH 会话(增量同步),旧事件一个字节不动。
775
983
  * - force:为同一源文件创建一份**新的**完整副本(新 id),旧副本原样保留。
@@ -793,9 +1001,9 @@ export async function persistConverted(ctx, converted, args, persisted, sourcePa
793
1001
  /** 探测同名持久化会话是否为空日志(上次 create 成功、append 失败残留);无法确定返回 false。 */
794
1002
  async function isEmptyStoredSession(ctx, dshId) {
795
1003
  const sp = ctx.get('sessionPersistence')
796
- if (!sp || typeof sp.readFrom !== 'function') return false
1004
+ if (!sp || detectPersistenceShape(sp) === null) return false
797
1005
  try {
798
- const read = await sp.readFrom(dshId, 0)
1006
+ const read = await readStoredEvents(sp, dshId)
799
1007
  return Array.isArray(read?.events) && read.events.length === 0
800
1008
  } catch {
801
1009
  // 读不到/读失败:按「非空」处理,走保守路径。
@@ -872,11 +1080,7 @@ async function persistConvertedInner(ctx, converted, args, persisted, sourcePath
872
1080
  }
873
1081
  const tail = tailSessionEvents(converted, { fromTurn: known.turns + 1, fromSeq })
874
1082
  if (tail.events.length > 0) {
875
- const sp = ctx.get('sessionPersistence')
876
- if (!sp || typeof sp.append !== 'function') {
877
- throw new Error('会话持久化服务(sessionPersistence)不可用:claude-move 增量续写需要该服务')
878
- }
879
- await sp.append(knownId, tail.events)
1083
+ await spAppend(ctx, knownId, tail.events)
880
1084
  }
881
1085
  await rememberImport(ctx, sourcePath, {
882
1086
  dshId: knownId, turns: turns.length, events: fromSeq + tail.events.length,
@@ -949,22 +1153,66 @@ async function persistConvertedInner(ctx, converted, args, persisted, sourcePath
949
1153
  }
950
1154
  }
951
1155
 
952
- /** append 一份事件批次;服务缺失响亮抛出。 */
1156
+ /**
1157
+ * append 一份事件批次(双基线);服务缺失响亮抛出。handle 路径
1158
+ * open(id, 'write') 独占单写所有权,append 后必须 flush()(耐久屏障)并
1159
+ * 成对 close()(释放所有权);事件批经 normalizeHandleEvents 规范化。
1160
+ * 已被他人持有时 open 响亮拒绝(单写冲突)。
1161
+ */
953
1162
  async function spAppend(ctx, id, events) {
954
1163
  const sp = ctx.get('sessionPersistence')
955
- if (!sp || typeof sp.append !== 'function') {
1164
+ if (!sp) {
1165
+ throw new Error('会话持久化服务(sessionPersistence)不可用:claude-move 导入需要该服务')
1166
+ }
1167
+ if (detectPersistenceShape(sp) === 'handle') {
1168
+ if (typeof sp.open !== 'function') {
1169
+ throw new Error('会话持久化服务(sessionPersistence)不可用:claude-move 增量续写需要该服务')
1170
+ }
1171
+ const handle = await sp.open(id, 'write')
1172
+ try {
1173
+ await handle.append(normalizeHandleEvents(events))
1174
+ await handle.flush()
1175
+ } finally {
1176
+ await handle.close()
1177
+ }
1178
+ return
1179
+ }
1180
+ if (typeof sp.append !== 'function') {
956
1181
  throw new Error('会话持久化服务(sessionPersistence)不可用:claude-move 导入需要该服务')
957
1182
  }
958
1183
  await sp.append(id, events)
959
1184
  }
960
1185
 
961
- /** create + append 一份完整会话日志;服务缺失/落盘失败响亮抛出。 */
1186
+ /**
1187
+ * create + append 一份完整会话日志(双基线);服务缺失/落盘失败响亮抛出。
1188
+ * handle 路径:按服务形状先探测(有 open 即 handle),create 前经
1189
+ * normalizeHandleHeader 盖当前格式版本与 isSeeded(checkout 后端要求),
1190
+ * create 返回值实测判定(isSessionHandle),append 后 flush() 并在 finally
1191
+ * 成对 close()——任何失败路径都释放单写所有权;空事件批次(流式导入的首个
1192
+ * create)只 flush 物化空会话。旧路径 create+append 调用序列与 0.3.x 逐字节
1193
+ * 一致(header/事件不做任何规范化)。
1194
+ */
962
1195
  async function spPersist(ctx, meta, events) {
963
1196
  const sp = ctx.get('sessionPersistence')
964
- if (!sp || typeof sp.create !== 'function' || typeof sp.append !== 'function') {
1197
+ if (!sp || typeof sp.create !== 'function') {
1198
+ throw new Error('会话持久化服务(sessionPersistence)不可用:claude-move 导入需要该服务')
1199
+ }
1200
+ const header = detectPersistenceShape(sp) === 'handle' ? await normalizeHandleHeader(sp, meta) : meta
1201
+ const created = await sp.create(header)
1202
+ if (isSessionHandle(created)) {
1203
+ notePersistenceShape(sp, 'handle')
1204
+ try {
1205
+ if (events.length > 0) await created.append(normalizeHandleEvents(events))
1206
+ await created.flush()
1207
+ } finally {
1208
+ await created.close()
1209
+ }
1210
+ return
1211
+ }
1212
+ notePersistenceShape(sp, 'legacy')
1213
+ if (typeof sp.append !== 'function') {
965
1214
  throw new Error('会话持久化服务(sessionPersistence)不可用:claude-move 导入需要该服务')
966
1215
  }
967
- await sp.create(meta)
968
1216
  await sp.append(meta.id, events)
969
1217
  }
970
1218
 
@@ -1580,9 +1828,9 @@ export function resolveExportTarget(raw, sessionId, config) {
1580
1828
  }
1581
1829
 
1582
1830
  /**
1583
- * 执行一次回迁导出:读 DSH 会话日志(sessionPersistence.readFrom)→ 反向折叠为
1584
- * Claude 可 resume JSONL → 写入目标文件。只读源会话、只写导出落点文件,绝不
1585
- * 改写 DSH 会话日志。会话不可读/无可导出内容时大声失败。
1831
+ * 执行一次回迁导出:读 DSH 会话日志(sessionPersistence 双基线 readFrom /
1832
+ * open+read)→ 反向折叠为 Claude 可 resume JSONL → 写入目标文件。只读源会话、
1833
+ * 只写导出落点文件,绝不改写 DSH 会话日志。会话不可读/无可导出内容时大声失败。
1586
1834
  * @param ctx - Cordis 上下文。
1587
1835
  * @param config - 插件配置。
1588
1836
  * @param args - 工具参数 `{ sessionId, path? }`。
@@ -1591,7 +1839,7 @@ export function resolveExportTarget(raw, sessionId, config) {
1591
1839
  */
1592
1840
  export async function runExport(ctx, config, args, signal) {
1593
1841
  const sp = ctx.get('sessionPersistence')
1594
- if (!sp || typeof sp.readFrom !== 'function') {
1842
+ if (!sp || (typeof sp.readFrom !== 'function' && typeof sp.open !== 'function')) {
1595
1843
  throw new Error('会话持久化服务(sessionPersistence)不可用:claude-move 回迁导出需要该服务')
1596
1844
  }
1597
1845
  const sessionId = typeof args?.sessionId === 'string' ? args.sessionId.trim() : ''
@@ -1599,7 +1847,10 @@ export async function runExport(ctx, config, args, signal) {
1599
1847
  signal?.throwIfAborted()
1600
1848
  let read
1601
1849
  try {
1602
- read = await sp.readFrom(sessionId, 0)
1850
+ read = await readStoredEvents(sp, sessionId)
1851
+ if (read === null) {
1852
+ throw new Error('sessionPersistence 基线无法判定(既无 readFrom 也无 open 形状)')
1853
+ }
1603
1854
  } catch (err) {
1604
1855
  throw new Error(`读取会话 ${sessionId} 失败(不存在或不可读):${String((err && err.message) || err)}`)
1605
1856
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-claude-move",
3
3
  "description": "Four-source migration wizard: copy Claude Code, Codex, OpenCode and Hermes sessions, memories, skills, instructions and slash commands into DeepSeek Harness as resumable sessions 鈥?copy-only, approval-gated, idempotent",
4
- "version": "0.3.3",
4
+ "version": "0.4.0",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",
7
7
  "exports": {
@@ -110,8 +110,8 @@
110
110
  "license": "Apache-2.0",
111
111
  "devDependencies": {
112
112
  "@deepseek-ai/cordis": "^4.0.2",
113
- "@deepseek-ai/cordis-plugin-include": "^1.0.6",
114
- "@deepseek-ai/cordis-plugin-loader": "^1.0.2",
113
+ "@deepseek-ai/cordis-plugin-include": "^1.0.7",
114
+ "@deepseek-ai/cordis-plugin-loader": "^1.0.3",
115
115
  "@deepseek-ai/dsh-agent": "0.1.2-alpha.5",
116
116
  "@deepseek-ai/dsh-attachment": "0.1.2-alpha.5",
117
117
  "@deepseek-ai/dsh-brand": "0.1.2-alpha.5",
@@ -121,6 +121,7 @@
121
121
  "@deepseek-ai/dsh-llm": "0.1.2-alpha.5",
122
122
  "@deepseek-ai/dsh-scope": "0.1.2-alpha.5",
123
123
  "@deepseek-ai/dsh-session": "0.1.2-alpha.5",
124
+ "@deepseek-ai/dsh-session-projection": "0.1.2-alpha.5",
124
125
  "@deepseek-ai/dsh-system-prompt": "0.1.2-alpha.5",
125
126
  "@deepseek-ai/dsh-timeout": "0.1.2-alpha.5",
126
127
  "@deepseek-ai/dsh-tools": "0.1.2-alpha.5",