diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bcb900a..12ce48e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -131,10 +131,10 @@ jobs: # Pin the DSH CLI the lane mounts into; the profile's bundle stack # (dsh-base / dsh-web-app) resolves from this installation. Keep in # step with the plugin's @deepseek-ai/* peer range when bumping. - # DSH 0.1.2-rc.1 is published under the `next` dist-tag; the lanes + # DSH 0.1.5-alpha.1 is published under the `alpha` dist-tag; the lanes # speak the slash-only RPC dialect (tests/e2e/host-protocol.ts). - name: Install dsh CLI (pinned) - run: npm install -g @deepseek-ai/dsh@0.1.2-rc.1 + run: npm install -g @deepseek-ai/dsh@0.1.5-alpha.1 # lib/ is gitignored; the tarball is produced from this build. - name: Build and pack the npm tarball diff --git a/AGENTS.md b/AGENTS.md index 0f8656b..65f5e1f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,27 +19,27 @@ 「npm 打包 → 真实挂载 → 无头渲染」门禁(证明打包产物在真实 DSH 挂载后不 crash):`pnpm build && pnpm pack` 产 tarball → `scripts/e2e-mount.sh` 装进全新 scratch profile(`dsh plugin --profile web add file:`)并启动真实 `dsh web`(keyless,`--port 0`)→ `tests/e2e/mount.e2e.ts`(Playwright)断言 `[data-dsh-better-sidebar]` 挂载、无错误条/pageerror/console 错误,经「+ 菜单」逐个打开内置 tab(含终端懒加载 chunk),再经文件树打开 seed 文件强制加载 editor chunk(`client-editor.js`)。 -本地:`pnpm build && pnpm pack && pnpm exec playwright install chromium && pnpm test:mount`。CI 钉 `@deepseek-ai/dsh@0.1.2-rc.1`(`next` dist-tag;peer 下限 `^0.1.2-rc.1`)。e2e spec 命名 `*.e2e.ts` + vitest `exclude` 双保险;**改 `exclude` 必须保留默认排除项**(exclude 整体替换默认值)。 +本地:`pnpm build && pnpm pack && pnpm exec playwright install chromium && pnpm test:mount`。CI 钉 `@deepseek-ai/dsh@0.1.5-alpha.1`(`alpha` dist-tag;peer 下限 `^0.1.5-alpha.1`)。e2e spec 命名 `*.e2e.ts` + vitest `exclude` 双保险;**改 `exclude` 必须保留默认排除项**(exclude 整体替换默认值)。 --- -## 3. DSH 0.1.2 适配要点(0.1.2-rc.1+ 基线) +## 3. DSH 0.1.5 适配要点(0.1.5-alpha.1+ 基线) -**v0.18.0 起插件毕业正式版(npm `latest`),仅支持 DSH 0.1.2-rc.1+**(peer 下限 `^0.1.2-rc.1`,CI 钉 `@deepseek-ai/dsh@0.1.2-rc.1`,DSH rc.x 发在 `next` dist-tag)。v0.18.0-alpha.0 进入 alpha 通道时已删除对 0.1.0-rc.8 ~ 0.1.1-rc.2 的兼容层——双方言 RPC(点分回退)、MarkdownText 双形状 labels、`dsh-client-runtime` externals/inject 残留行;stable DSH 用户请固定装 v0.17.1(npm 上的旧 latest,0.1.2-alpha.x 宿主继续用 npm `alpha` 的 v0.18.0-alpha.0)。发版:release.yml 按版本号是否含 `-` 自动选 `alpha`/`latest` dist-tag。 +**v0.19.0-alpha.0 起仅支持 DSH 0.1.5-alpha.1+**(peer 下限 `^0.1.5-alpha.1`,CI 钉 `@deepseek-ai/dsh@0.1.5-alpha.1`,DSH alpha 线发在 `alpha` dist-tag)。0.1.2-rc.1 稳定线用户继续用 v0.18.x(npm `latest`)——0.1.5 的会话事件模型与文件打开漏斗都变了,插件不再对 0.1.2 做运行时兼容。发版:release.yml 按版本号是否含 `-` 自动选 `alpha`/`latest` dist-tag。 宿主契约(均经真机挂载冒烟 14/14 验证): 1. **一次性 token 鉴权**:就绪行 `dsh web: http://127.0.0.1:/?token=<43字符>`(导航换签名 cookie,干净 URL 401)。`e2e-mount.sh` 的 URL grep 必须延伸到空白(`[^ ]*`,在 `/` 截断丢 token);e2e 统一走 `tests/e2e/host.ts`(token 必选:`parseLaunchUrl` 对裸 origin 直接抛错),带 stamps 导航走 `gotoPage()`(先 addCookies 再直达——token 换 cookie 的 303 会丢弃同 URL 其它 query 参数)。插件 `/sidebar/*` 路由不受影响,同源 fetch 照旧。 2. **Remote gateway 斜杠 RPC(唯一方言)**:`POST /api/workspace/create`,payload 恰为 `{args: {...}}`,**args 按控制器 TS 参数名包装**(`workspace/create`、`session/create` → `{args:{request:{...}}}`;`session/list` 参数名 `_request` **不可省略**——`{}` 也被拒 `args fields do not match the descriptor`);envelope `method` 与路径一致,点分路径 404。请求由 `tests/e2e/host-protocol.ts` 的 `rpcAttempt` 构造、`tests/e2e-host-protocol.spec.ts` 锁定;要调新方法先在真机验参数名再进 `RPC_ARGS_KEY`。 3. **`MarkdownText` labels 嵌套契约**:必填 `labels: { code: { copyLabel, copiedLabel }, footnotes }`(漏传回退硬编码中文)。四个渲染点(mermaid.tsx / MarkdownHtml.tsx / TextEditor.tsx / SideChatView.tsx)统一走 `src/client/markdown-labels.tsx` 的 `markdownTextProps()`。 -4. **侧边对话转录走自有路由,不碰客户端宿主 RPC**:`ctx.connection.api`(含 `sessions.history`)在 alpha.1 整体移除,继任 `session/follow|page` 又对 `origin:'subagent'` 会话强制 subagent 地址(普通 `{kind:'session'}` 被 `agent-busy` 拒)且分页 `throughSeq` 不得超当前游标。转录因此由 **`sidechat.events`** 插件路由供给(`src/sidechat-routes.ts`:live 读 `agent.session.snapshotEvents()`(0.1.2-alpha.4 起的按需读 API,前身 `Session.events` 属性已删)、冷读 `sessionPersistence.inspect`,服务端 `session/end-seed` 切割 + `afterSeq` 增量);`ctx.connection` 镜像与 inject 已删。设计见 [docs/plans/2026-08-20-sidechat-tab-design.md](docs/plans/2026-08-20-sidechat-tab-design.md) §10。 +4. **侧边对话转录走自有路由,不碰客户端宿主 RPC**:`ctx.connection.api`(含 `sessions.history`)在 alpha.1 整体移除,继任 `session/follow|page` 又对 `origin:'subagent'` 会话强制 subagent 地址(普通 `{kind:'session'}` 被 `agent-busy` 拒)且分页 `throughSeq` 不得超当前游标。转录因此由 **`sidechat.events`** 插件路由供给(`src/sidechat-routes.ts`:live 读 `agent.session.snapshotEvents()`、冷读 `sessionPersistence.inspect`,服务端 `session/end-seed` 切割 + `afterSeq` 增量);`ctx.connection` 镜像与 inject 已删。**0.1.5 起实时增量不在日志里**:`assistant/chunk` 事件被删除,进行中的模型增量改由 `agent/assistant-stream` 瞬时帧发布(`start` / `chunk` / `end`,不入会话日志),结算时才落 `assistant/message`(内嵌 `stream`)或 `assistant/attempt`(失败尝试,内嵌 `stream`)。插件在宿主侧 `src/assistant-live.ts` 折叠这些帧成有界缓冲,`sidechat.events` 的 `live` 字段按「当前 attempt 全量、每次轮询替换」下发(不是增量),转录映射与继承快照都读它。设计见 [docs/plans/2026-08-20-sidechat-tab-design.md](docs/plans/2026-08-20-sidechat-tab-design.md) §10。 5. **`dsh-settings` 无运行时 `settingsNamespace`**:命名空间合法性校验转为编译期模板字面量 `SettingsNamespaceInput`(小写字母开头 + `[a-z0-9-]` 尾部),`'dsh-better-sidebar'` 字面量直接过——宿主侧直接传常量(`src/index.ts` 的 settings inject)。 6. **`dsh-subagent` 的 `SUBAGENT_DESCRIPTOR_VERSION` 2 → 3**:sidechat 种子的 `subagent/descriptor` 版本由宿主包盖章,插件不硬编码;测试断言跟随常量(`tests/sidechat-routes.spec.ts`),勿钉字面量。 7. **`@deepseek-ai/dsh-client-runtime` 包已消亡**(继任 seed 是裸名 `dsh-client-store`,无 `/client` 子路径):peerDependencies、devDependencies、`dsh.client.inject`、chunk externals 白名单(`src/client/chunk-loader.ts` / `tsdown.config.ts` / `tests/chunk-loader.spec.ts` / `tests/manifest-consistency.spec.ts` 四处同步)均已无该条目。 8. **e2e scratch profile 的 `minimumReleaseAgeExclude` 含 `'@deepseek-ai/*'`**(`scripts/e2e-mount.sh` / `e2e-aggregate-mount.sh`,与仓库根 `pnpm-workspace.yaml` 同策):alpha 版本常在发布后 24h 内跑 lane,pnpm 11 的 `minimumReleaseAge` 默认会拒装新鲜包。 9. **插件开发树的 dsh-* 传递 peer 需提升为 devDependencies**(alpha.3 首见):`dsh-subagent` 等 npm 包把 `dsh-attachment` 等 dsh-* 姊妹包全部声明为 peerDependencies(由宿主 bundle 树统一提供,宿主侧无此问题),插件仓库若只直接依赖其中一部分,其余 peer 在 pnpm 下会解析到树上残留的旧版——如 `dsh-attachment@0.1.1-rc.1` 缺 `admitPromptContent` 导出、`dsh-subagent` 产物 import 它时测试加载即崩。因此 devDependencies 需涵盖 dev 树实际触达的全部 peer(attachment / code-runtime / scope / session-projection / system-prompt / user-approval / util-time 七个即为此提升,与直接依赖同款精确钉版);`@deepseek-ai/cordis` peer 自 alpha.3 起要求 `^4.0.2`(上游全线 peer 已升)。适配新 alpha 版本时先跑 `pnpm peers check`,把新失配的传递 peer 一并提升进 devDependencies。例外:**`dsh-client-locale` 的 peer/devDep 允许落后于基线**(alpha.5 时上游停在 0.1.2-alpha.3 未发新版)——peer 下限 `^0.1.2-alpha.3` 天然容纳同 tuple 的 alpha.5 运行时,此时保持旧钉版并在 `pnpm-workspace.yaml` 注明,待上游发版再追平;rc.1 起上游恢复发版,peer/devDep 已与基线一并钉 `0.1.2-rc.1`,该例外消除。 -10. **聊天文件打开漏斗是 `remote.session.openWorkspacePath`**(`ctx.workspaces.openPath` 已随 alpha 删除,`IWorkspaces` 无此方法):「聊天区文件在侧边栏打开」拦截在 `ctx.inject(['remote.session'], …)` 内以 **defineProperty 数据属性遮蔽**该命名空间方法(gateway client 的方法是 accessor 属性、异步挂载、contribution 重挂载时服务重建——inject 回调重跑即自愈);实现见 `src/client/openpath-intercept.ts`,设计见 [docs/plans/2026-08-31-openpath-intercept-alpha-design.md](docs/plans/2026-08-31-openpath-intercept-alpha-design.md)。 -11. **自定义种子必须带 fork 标记对(`meta.isSeeded: true` + `inheritedEventCount`)**:dsh-session 契约「只给 seed 不标 isSeeded,种子算重放历史而非继承前缀」——缺标记时 `Session.ownEvents()` 含整个种子,子会话 `Inbox` 构造器重放种子里的 `agent/inbox/spliced`,**继承父会话切割时刻未领取的 inbox 输入**(排队用户消息 next-turn、长回合中工具结果上下文/steering next-step——后者即「上下文很长时侧边对话先把之前的 User msg 发出去」的根因,幽灵消息排在 boundary 之前最先发给模型)。宿主 `session.fork`(api-session-controller)的调用即规范形态;回归由 `tests/sidechat-seed-validation.spec.ts`(真实 `Session.create` + `Inbox`)守护。0.1.2-rc.1 复评:无更优雅的 sidechat 宿主 API(`ContinuableStartSpec` 仍无 seed 字段),`AgentRegistry.create` 接缝补齐标记即为规范用法。 +10. **聊天文件打开漏斗已改为原生右侧栏**:0.1.5 客户端不再有任何 `remote.session.openWorkspacePath` 调用——聊天里一切文件打开(工具行 / 产物行 / 正文提及 / 行内代码路径)统一走 `ctx.sidebarRight.openResource(fileAddressFor(sessionId, cwd, path))`(`packages/client/ui-chat/src/client/apply.ts` 是唯一调用点)。插件此前的 `openpath-intercept`(defineProperty 遮蔽 `remote.session` 方法)随之删除:0.1.5 上它是死代码,且会劫持宿主的「在外部应用打开」语义。文件当前落到原生栏的内置 `text` 预览;阶段二由插件注册 `dsh-resource://file/**` 类型(默认 `extension` 带,压过 `text` 的 `fallback`)接管。 +11. **自定义种子必须带 fork 标记对(`meta.isSeeded: true` + `inheritedEventCount`)**:dsh-session 契约「只给 seed 不标 isSeeded,种子算重放历史而非继承前缀」——缺标记时 `Session.ownEvents()` 含整个种子,子会话的 inbox 折(`inboxProjectionDefinition`,0.1.5 起 `@deepseek-ai/dsh-agent` 只导出 `Inbox` 接口,实现是该投影)重放种子里的 `agent/inbox/spliced`,**继承父会话切割时刻未领取的 inbox 输入**(排队用户消息 next-turn、长回合中工具结果上下文/steering next-step——后者即「上下文很长时侧边对话先把之前的 User msg 发出去」的根因,幽灵消息排在 boundary 之前最先发给模型)。宿主 `session.fork`(api-session-controller)的调用即规范形态;回归由 `tests/sidechat-seed-validation.spec.ts`(真实 `Session.create` + 对 `ownEvents()` 跑 inbox 折)守护。0.1.5 复评:无更优雅的 sidechat 宿主 API(`ContinuableStartSpec` 仍无 seed 字段),`AgentRegistry.create` 接缝补齐标记即为规范用法;`Session.create` 的 header `version` 必须用 `SESSION_FORMAT_VERSION`(0.1.5 是 `3` 字面量类型),勿钉 `0`。 --- diff --git a/docs/external-plugin-guide.md b/docs/external-plugin-guide.md index 0dac147..2e373dc 100644 --- a/docs/external-plugin-guide.md +++ b/docs/external-plugin-guide.md @@ -2,7 +2,7 @@ > 面向 **消费插件开发者**:如何让你的插件向 better-sidebar 注册新的侧边栏页面(tab)和文件类型预览器。 > -> 适用版本:**v0.4.0+**(`ctx.betterSidebar` 服务);声明式设置 **v0.4.1+**;text/number 设置行 **v0.11.0+**;badge/生命周期/定向打开/插件设置/版本探测 **v0.12.0+**;select 设置行(`settingSelect`)与外链认领(`urlTarget`)**v0.13.0+**;统一 `@deepseek-ai/cordis` 类型基底 **v0.15.2+**;自由窗口(`floatWindows`)**v0.16.0+**;终端固定(pin)**v0.17.0+**。当前版本 **v0.18.1**(正式版,仅支持 DSH 0.1.2-rc.1+;旧宿主 stable 线为 v0.17.1)。 +> 适用版本:**v0.4.0+**(`ctx.betterSidebar` 服务);声明式设置 **v0.4.1+**;text/number 设置行 **v0.11.0+**;badge/生命周期/定向打开/插件设置/版本探测 **v0.12.0+**;select 设置行(`settingSelect`)与外链认领(`urlTarget`)**v0.13.0+**;统一 `@deepseek-ai/cordis` 类型基底 **v0.15.2+**;自由窗口(`floatWindows`)**v0.16.0+**;终端固定(pin)**v0.17.0+**。当前版本 **v0.19.0-alpha.0**(alpha 通道,仅支持 DSH **0.1.5-alpha.1+**;0.1.2-rc.1 稳定线请用 v0.18.x)。 > 权威代码:`src/client/service.ts`(服务实现)、`src/client/builtins/`(内置 8 tab + 6 viewer 参考实现)、`lib/types/client/service.d.ts`(类型声明)。 > 仓库开发规则(硬约束 / CI / 发版)见 [AGENTS.md](../AGENTS.md)。 @@ -730,7 +730,7 @@ ctx.effect(() => | **i18n 跟随** | 文案跟随 DSH `ctx.locale`(词典在 `betterSidebar` 命名空间;Host-backed `locale.preference` 优先于浏览器语言并实时切换;缺失回退浏览器)。消费插件**不要**依赖内部 `t()`——标题传字符串或 `() => string` | | **第三语言覆盖(ja 等)** | 可选 peer `@huanlin/dsh-plugin-better-locale`(optional)提供 ja/ko 覆盖,**借用 DSH 英文槽位**(仅 DSH=en 时生效,zh 下惰性)。经 `ctx.get('betterLocale')` 注入 `t()`;未安装整段 no-op | | **懒加载 chunk** | 重依赖(xterm/CodeMirror)在独立 bundle(`lib/client-.js`),经 `/sidebar/bundle` 按需下发;factory 赋到 `globalThis.__dshChunks__[]`,由 `src/client/chunk-loader.ts` 物化,**不经** `__ModuleLoader__`。对消费插件透明 | -| **聊天文件打开漏斗(alpha 宿主)** | 聊天里一切文件打开(工具行 / 产物行 / 正文提及 / 行内代码路径)汇入 `ctx.remote.session.openWorkspacePath`(Typert remote 命名空间,cordis 服务 key `remote.session`,方法为 **accessor 属性**、异步挂载)。better-sidebar 的「聊天区文件在侧边栏打开」即在 `ctx.inject(['remote.session'], …)` 内以 defineProperty 遮蔽该方法(`src/client/openpath-intercept.ts`)。你的插件若要观测/旁路聊天文件打开,走同一服务;不要假设 pre-alpha 的 `ctx.workspaces.openPath` 存在(alpha 的 `IWorkspaces` 已无此方法) | +| **聊天文件打开漏斗(0.1.5 宿主)** | 聊天里一切文件打开(工具行 / 产物行 / 正文提及 / 行内代码路径)统一走 DSH 原生右侧栏的 `ctx.sidebarRight.openResource(fileAddressFor(sessionId, cwd, path))`(`packages/client/ui-chat/src/client/apply.ts` 是唯一调用点);`remote.session.openWorkspacePath` 在 0.1.5 客户端**已无调用者**,better-sidebar 的 openpath 拦截随之删除。你的插件若要观测/旁路聊天文件打开,注册原生 tab 类型认领 `dsh-resource://file/**`(见 [AGENTS.md §3](AGENTS.md) 第 10 条);不要假设 `ctx.workspaces.openPath`(已删除)或旧 `remote.session` 漏斗存在 | --- diff --git a/dsh.plugin.json b/dsh.plugin.json index 51bd46c..bfff44d 100644 --- a/dsh.plugin.json +++ b/dsh.plugin.json @@ -1,10 +1,10 @@ { "id": "dsh-external/dsh-better-sidebar", - "version": "0.18.1", + "version": "0.19.0-alpha.0", "main": "./lib/index.js", "description": "VSCode 风格右侧侧边栏:文件资源管理器 / 编辑器 / 终端 / Git / 浏览器,按会话隔离。暴露服务供其他插件注册侧边栏页面和文件预览器", "engines": { - "dsh": ">=0.1.2-rc.1" + "dsh": ">=0.1.5-alpha.1" }, "contributes": { "tools": [], diff --git a/package.json b/package.json index fdcacf7..5f5fc94 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dsh-better-sidebar", - "version": "0.18.1", + "version": "0.19.0-alpha.0", "description": "DSH web plugin: a VSCode-like right sidebar (explorer / editor / terminal / git / browser), isolated per conversation session. Exposes a service for other plugins to register sidebar tabs and file viewers.", "type": "module", "repository": { @@ -49,6 +49,7 @@ "@deepseek-ai/dsh-client-locale", "@deepseek-ai/dsh-client-ui-slots", "@deepseek-ai/dsh-client-ui-conversation", + "@deepseek-ai/dsh-client-ui-sidebar-right", "@deepseek-ai/dsh-client-modules" ], "platform": "web" @@ -88,19 +89,20 @@ "license": "MIT", "peerDependencies": { "@deepseek-ai/cordis": "^4.0.2", - "@deepseek-ai/dsh-agent": "^0.1.2-rc.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-rc.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-rc.1", - "@deepseek-ai/dsh-client-ui-primitives": "^0.1.2-rc.1", - "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-rc.1", - "@deepseek-ai/dsh-client-ui-slots": "^0.1.2-rc.1", - "@deepseek-ai/dsh-host-webserver": "^0.1.2-rc.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-rc.1", - "@deepseek-ai/dsh-llm": "^0.1.2-rc.1", - "@deepseek-ai/dsh-session": "^0.1.2-rc.1", - "@deepseek-ai/dsh-settings": "^0.1.2-rc.1", - "@deepseek-ai/dsh-subagent": "^0.1.2-rc.1", - "@deepseek-ai/dsh-tools": "^0.1.2-rc.1", + "@deepseek-ai/dsh-agent": "^0.1.5-alpha.1", + "@deepseek-ai/dsh-client-locale": "^0.1.5-alpha.1", + "@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-alpha.1", + "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-alpha.1", + "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-alpha.1", + "@deepseek-ai/dsh-client-ui-sidebar-right": "^0.1.5-alpha.1", + "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-alpha.1", + "@deepseek-ai/dsh-host-webserver": "^0.1.5-alpha.1", + "@deepseek-ai/dsh-invariants": "^0.1.5-alpha.1", + "@deepseek-ai/dsh-llm": "^0.1.5-alpha.1", + "@deepseek-ai/dsh-session": "^0.1.5-alpha.1", + "@deepseek-ai/dsh-settings": "^0.1.5-alpha.1", + "@deepseek-ai/dsh-subagent": "^0.1.5-alpha.1", + "@deepseek-ai/dsh-tools": "^0.1.5-alpha.1", "@huanlin/dsh-plugin-better-locale": "^0.1.0", "react": "^18.2.0", "react-dom": "^18.2.0" @@ -108,6 +110,9 @@ "peerDependenciesMeta": { "@huanlin/dsh-plugin-better-locale": { "optional": true + }, + "@deepseek-ai/dsh-client-ui-sidebar-right": { + "optional": true } }, "dependencies": { @@ -144,26 +149,28 @@ }, "devDependencies": { "@cordisjs/plugin-loader": "^1.0.0-rc.5", - "@deepseek-ai/dsh-agent": "0.1.2-rc.1", - "@deepseek-ai/dsh-attachment": "0.1.2-rc.1", - "@deepseek-ai/dsh-client-locale": "0.1.2-rc.1", - "@deepseek-ai/dsh-client-ui-conversation": "0.1.2-rc.1", - "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-rc.1", - "@deepseek-ai/dsh-client-ui-settings": "0.1.2-rc.1", - "@deepseek-ai/dsh-client-ui-slots": "0.1.2-rc.1", - "@deepseek-ai/dsh-code-runtime": "0.1.2-rc.1", - "@deepseek-ai/dsh-host-webserver": "0.1.2-rc.1", - "@deepseek-ai/dsh-invariants": "0.1.2-rc.1", - "@deepseek-ai/dsh-llm": "0.1.2-rc.1", - "@deepseek-ai/dsh-scope": "0.1.2-rc.1", - "@deepseek-ai/dsh-session": "0.1.2-rc.1", - "@deepseek-ai/dsh-session-projection": "0.1.2-rc.1", - "@deepseek-ai/dsh-settings": "0.1.2-rc.1", - "@deepseek-ai/dsh-subagent": "0.1.2-rc.1", - "@deepseek-ai/dsh-system-prompt": "0.1.2-rc.1", - "@deepseek-ai/dsh-tools": "0.1.2-rc.1", - "@deepseek-ai/dsh-user-approval": "0.1.2-rc.1", - "@deepseek-ai/dsh-util-time": "0.1.2-rc.1", + "@deepseek-ai/dsh-agent": "0.1.5-alpha.1", + "@deepseek-ai/dsh-agent-loop": "0.1.5-alpha.1", + "@deepseek-ai/dsh-attachment": "0.1.5-alpha.1", + "@deepseek-ai/dsh-client-locale": "0.1.5-alpha.1", + "@deepseek-ai/dsh-client-ui-conversation": "0.1.5-alpha.1", + "@deepseek-ai/dsh-client-ui-primitives": "0.1.5-alpha.1", + "@deepseek-ai/dsh-client-ui-settings": "0.1.5-alpha.1", + "@deepseek-ai/dsh-client-ui-sidebar-right": "0.1.5-alpha.1", + "@deepseek-ai/dsh-client-ui-slots": "0.1.5-alpha.1", + "@deepseek-ai/dsh-code-runtime": "0.1.5-alpha.1", + "@deepseek-ai/dsh-host-webserver": "0.1.5-alpha.1", + "@deepseek-ai/dsh-invariants": "0.1.5-alpha.1", + "@deepseek-ai/dsh-llm": "0.1.5-alpha.1", + "@deepseek-ai/dsh-scope": "0.1.5-alpha.1", + "@deepseek-ai/dsh-session": "0.1.5-alpha.1", + "@deepseek-ai/dsh-session-projection": "0.1.5-alpha.1", + "@deepseek-ai/dsh-settings": "0.1.5-alpha.1", + "@deepseek-ai/dsh-subagent": "0.1.5-alpha.1", + "@deepseek-ai/dsh-system-prompt": "0.1.5-alpha.1", + "@deepseek-ai/dsh-tools": "0.1.5-alpha.1", + "@deepseek-ai/dsh-user-approval": "0.1.5-alpha.1", + "@deepseek-ai/dsh-util-time": "0.1.5-alpha.1", "@huanlin/dsh-plugin-better-locale": "^0.1.0", "@playwright/test": "^1.62.1", "@types/node": "^24.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f44d498..bfbdac6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -106,71 +106,77 @@ importers: specifier: ^1.0.0-rc.5 version: 1.0.0-rc.5(cordis@4.0.0-rc.8) '@deepseek-ai/dsh-agent': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(6f833cf90b9dd3f0d529840b1aebfb48) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(288d2f4271bc6bef491bb5a5c084f12c) + '@deepseek-ai/dsh-agent-loop': + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(3d62ae7d0bb4adb238d8dfb4da37e073) '@deepseek-ai/dsh-attachment': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-brand@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-brand@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)) '@deepseek-ai/dsh-client-locale': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) '@deepseek-ai/dsh-client-ui-conversation': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(typescript@5.6.2) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(typescript@5.6.2) '@deepseek-ai/dsh-client-ui-primitives': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) '@deepseek-ai/dsh-client-ui-settings': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-client-ui-sidebar-right': + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) '@deepseek-ai/dsh-client-ui-slots': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) '@deepseek-ai/dsh-code-runtime': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) '@deepseek-ai/dsh-host-webserver': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) '@deepseek-ai/dsh-invariants': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) '@deepseek-ai/dsh-llm': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) '@deepseek-ai/dsh-scope': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)) '@deepseek-ai/dsh-session': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))) '@deepseek-ai/dsh-session-projection': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-session@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)))) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-session@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)))) '@deepseek-ai/dsh-settings': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-brand@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-session@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))))(@deepseek-ai/schemastery@3.18.2) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-brand@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-session@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))))(@deepseek-ai/schemastery@3.18.2) '@deepseek-ai/dsh-subagent': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(6241f70df87ac98c48965b657a1f7a60) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(0904ccd91d8f9ab5907c34d0b1c82b43) '@deepseek-ai/dsh-system-prompt': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))) '@deepseek-ai/dsh-tools': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(de5e1ff2d608fbe22667925341ad1dfc) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(239eb910b4462b7c15b6b44661457a15) '@deepseek-ai/dsh-user-approval': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-agent@0.1.2-rc.1(6f833cf90b9dd3f0d529840b1aebfb48))(@deepseek-ai/dsh-brand@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)))(@deepseek-ai/dsh-session@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))))(@deepseek-ai/dsh-system-prompt@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)))) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-agent@0.1.5-alpha.1(288d2f4271bc6bef491bb5a5c084f12c))(@deepseek-ai/dsh-brand@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)))(@deepseek-ai/dsh-session@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))))(@deepseek-ai/dsh-system-prompt@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)))) '@deepseek-ai/dsh-util-time': - specifier: 0.1.2-rc.1 - version: 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + specifier: 0.1.5-alpha.1 + version: 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) '@eslint/js': specifier: ^10.0.1 version: 10.0.1(eslint@10.9.1) '@huanlin/dsh-plugin-better-locale': specifier: ^0.1.0 - version: 0.1.0(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-client-locale@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-client-ui-primitives@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-client-ui-settings@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-client-ui-slots@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + version: 0.1.0(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-client-locale@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-client-ui-primitives@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-client-ui-settings@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-client-ui-slots@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@playwright/test': specifier: ^1.62.1 version: 1.62.1 @@ -458,124 +464,153 @@ packages: '@deepseek-ai/cosmokit@1.8.3': resolution: {integrity: sha512-qBo+ronVM6Eu2WNVJXi8JcMiqZ19T9BRIpV+5qJUFPXjGH/Z0QKcQMC/IZJ7L394YTOtJgcovbk9qP0w2GsBXQ==} - '@deepseek-ai/dsh-agent@0.1.2-rc.1': - resolution: {integrity: sha512-lfaqN34vUCWvbn1kJVHrhfJ6Dvt1HDHCm33ZCpmKkl07/5q6FxWqVv6rOdVX5QnM/9xz/uYiN0nQwUtBg4+Skg==} + '@deepseek-ai/dsh-agent-loop@0.1.5-alpha.1': + resolution: {integrity: sha512-FpfSUfeyONRfVbmIC82dYGPL4G5T+9sJDd0OIqMqv8gH2M7smbILE/HOnKo/eHC29ce/YDSvL3etWr6g0PGc8w==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-invariants': ^0.1.2-rc.1 - '@deepseek-ai/dsh-llm': ^0.1.2-rc.1 - '@deepseek-ai/dsh-scope': ^0.1.2-rc.1 - '@deepseek-ai/dsh-session': ^0.1.2-rc.1 - '@deepseek-ai/dsh-session-projection': ^0.1.2-rc.1 - '@deepseek-ai/dsh-system-prompt': ^0.1.2-rc.1 - '@deepseek-ai/dsh-typert-protocol': ^0.1.2-rc.1 + '@deepseek-ai/dsh-agent': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-invariants': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-llm': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-scope': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-session': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-session-persistence': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-session-projection': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-settings': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-system-prompt': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-tools': ^0.1.5-alpha.1 - '@deepseek-ai/dsh-attachment@0.1.2-rc.1': - resolution: {integrity: sha512-QISKUjEITusLvAkqPLRn70xDUt+GjjY41pISjtPKYcZ4EGmayzhgZ3VJNYEcC+rbaF44hs2o/0VhOwHA7lyxjw==} + '@deepseek-ai/dsh-agent@0.1.5-alpha.1': + resolution: {integrity: sha512-Klimoo970OFP2/5sk6MaycY5l4VHeK8dN5jEIL3l9EBerr4QnoDOZMmLVkrx3cSk9OCm4KfQWrv8Gkm1HJ3fUg==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-brand': ^0.1.2-rc.1 + '@deepseek-ai/dsh-invariants': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-llm': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-scope': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-session': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-session-projection': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-system-prompt': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-typert-protocol': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-util-values': ^0.1.5-alpha.1 - '@deepseek-ai/dsh-brand@0.1.2-rc.1': - resolution: {integrity: sha512-y2RHIag3kzLdY8kTBuSUSTsKzWPscJrysNObXTiNM7stXLpa3jXpshsB1caqGzNx50vYcmHeN1gFVECgx3NZHg==} + '@deepseek-ai/dsh-attachment@0.1.5-alpha.1': + resolution: {integrity: sha512-2XBsE+J4zSFEC09OhCXVD1BPWR3KIdVlM7lKcaHBEuUHwqewS/g16DbM/yAmFUaigjXS9zuQFThCAv7QYb1JhA==} + peerDependencies: + '@deepseek-ai/cordis': ^4.0.2 + '@deepseek-ai/dsh-brand': ^0.1.5-alpha.1 + + '@deepseek-ai/dsh-brand@0.1.5-alpha.1': + resolution: {integrity: sha512-R7CADJl81Q5PMeIKAQfzP9ZRauLw7niiSZDUVqZfxAkNMlGbKIqdXtUIMuxOv1TQUW8fuqqqPVSDo7m+QtKQxA==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-client-locale@0.1.2-rc.1': - resolution: {integrity: sha512-p34i5WcZ0+b77RKEatGLc6Rh4OgJJmc15kWbBqtCtSI1BxpwZxjDzoNIMTTS3jDGUQqfEd9X7Js9D4uNJDUewg==} + '@deepseek-ai/dsh-client-locale@0.1.5-alpha.1': + resolution: {integrity: sha512-Dz7VSN8nr59xLR+rvIlYXlP1A47l1XOgFFAHpNXnxIXupI8VdXb1GIByI5WdEdrZ+J4DmoBJZggNkIrVbjXqRg==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-client-ui-conversation@0.1.2-rc.1': - resolution: {integrity: sha512-PDrV7twPfmZE5TjdvdVVZMHOla36wCojRC8GJBUp1cdXsWUP+Omk10gxYbH1g5TQPdAWgSL+GktdjondSXMSNQ==} + '@deepseek-ai/dsh-client-ui-conversation@0.1.5-alpha.1': + resolution: {integrity: sha512-0UiN8TEuVKrnr10lIz8T8pkp9wK/gfMap6HEnP040KB50mceTTO3y00umCsz3CeS61gYWS6DwkaNBY9th3wq8Q==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-client-ui-primitives@0.1.2-rc.1': - resolution: {integrity: sha512-4gZS+tXAHbSxAjP91+lENpxKb49XVKcO80m7ATNNgD6lJHOwbuWVGv9IwEnmQ5ebZvI8Yrp8C1J+jDe1pB9jXw==} + '@deepseek-ai/dsh-client-ui-primitives@0.1.5-alpha.1': + resolution: {integrity: sha512-vF+hNd/Mxrhpbm5qcFVUFb7Db0JqkLnSdu+V2USu8dtQwcFIb0C5Es6v1+EB6Pq42YFs/lIg90JUTuXvF0OxBg==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-client-ui-settings@0.1.2-rc.1': - resolution: {integrity: sha512-/S16tlS84fggURMqO94HjYbE0xIH82aRJ2E4UupgM9acD3yJ6SCVam17tMcWKnqA6UWJK9llEHhl+KN9JyI/0Q==} + '@deepseek-ai/dsh-client-ui-settings@0.1.5-alpha.1': + resolution: {integrity: sha512-+3TVwum4UVY10jzEG2Pt7LfcJAjs1PlYb6kr49v3Yq6ZCvs3bsrEJ/yAN7EDJ7jtcUsgooSSneUlvBMXVdOCrQ==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-client-ui-slots@0.1.2-rc.1': - resolution: {integrity: sha512-wcqn8xwb87RjurSQ0zPoI4K9eYhDyoBjQqg4bByDq8CNynvayHoBPnNWfiAQ8ngyOPE20teL3DMS0gbcfCTqbA==} + '@deepseek-ai/dsh-client-ui-sidebar-right@0.1.5-alpha.1': + resolution: {integrity: sha512-3msAiVyymMsaBFKKSTsPO4q/x5isVcIYJvhQtuTCH1rEI11FFjm7/FUkOLGlBJsmIrxlubyTQ5lxhZKv+d1X5w==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-code-runtime@0.1.2-rc.1': - resolution: {integrity: sha512-dv5UszPJb/AFcarZc2c6h5z/X80bsrSd3bfIOugwNVclSI6P+L3ixseoZKgyks7T0Ap0h6ngXGk2WeoUkmYxnQ==} + '@deepseek-ai/dsh-client-ui-slots@0.1.5-alpha.1': + resolution: {integrity: sha512-Z7GXSKUeKlYrV0yRMU5auRSsTI0ORQ5zsr/FSojzdCI6+qQi27wMwRHvy5enIYMq/R/PfvSnH7igefzxu7OL5w==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-host-webserver@0.1.2-rc.1': - resolution: {integrity: sha512-QVcaf4qnIa1t215Y8TRiRhqkEz4Lu5/F+KqvWT+4sHOiyWmZr8g1JndqlwP6MBnMQEPmp0I/EKYJ7PWMV32gkA==} + '@deepseek-ai/dsh-code-runtime@0.1.5-alpha.1': + resolution: {integrity: sha512-WsHQyfhZlhy6E/E5FOg0O1nKguGTdxTQUOcE9Ixu0u9LD6kjnuNtwbJasKNCAVSRGRIMJ/LQ22OorCOVTbP+Ag==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-invariants@0.1.2-rc.1': - resolution: {integrity: sha512-zSjuFJYcEB94kPwn7SicqAV1dfVWZkjt1I8luDboxtjfp5CN6ziwJhLv3KaB+Dh9tgkyGY4mvo886W/HhE9L9g==} + '@deepseek-ai/dsh-host-webserver@0.1.5-alpha.1': + resolution: {integrity: sha512-0jjj2skOsThDukKE48WVE/GUtKXza4yTUrfZ7B0MwVrNbVhQPlrbMVlIyjR60OQCwNwDI8PC7Tbd6BIBAfetGw==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-llm@0.1.2-rc.1': - resolution: {integrity: sha512-7VYsha5AXsVLnsAwYJffWXz9bwUbElw8i5N8tlTSdai9Bupk3sMbsotzPf8ZbsuGAxQYErahMwQwgGEu4qZO6g==} + '@deepseek-ai/dsh-invariants@0.1.5-alpha.1': + resolution: {integrity: sha512-8Ote6DMq12u/X7eu5OfjlXPI1jxbEFUEm1jLIu6X03XZJT37O6S+wHvf5XCF0Kq43Ch7r5sibT3xcgTZpWRkNQ==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-scope@0.1.2-rc.1': - resolution: {integrity: sha512-GHwbzWrGbmE0ZGzAZsXBudpyCM9ENCpXxlKODIaPFbIWhRePh6nH2jW47TAOqCIsPPedWC2p3w8Tx5WAa3I/2A==} + '@deepseek-ai/dsh-llm@0.1.5-alpha.1': + resolution: {integrity: sha512-ncbGiWdvOG9v3K4b/LluSnrrG1w0lHKGkDsjztz1rfekvp2meLeQwbf59VCQRG68N9hdqNcr8dm0GFLEpGTE4w==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-invariants': ^0.1.2-rc.1 - '@deepseek-ai/dsh-session-projection@0.1.2-rc.1': - resolution: {integrity: sha512-qVWlIzPm4u9vzQ5G/QtE6jeSJ3b3b42hWVJj664BurEZlHbAfdqkhYc1QsoBU+5k5kUEJFHh07z8HxuXA9kTnQ==} + '@deepseek-ai/dsh-scope@0.1.5-alpha.1': + resolution: {integrity: sha512-rY/5BQAiP0hPBArwPB2uf5kXdWfmSHcOXprB7hrDeyZ6EwciYfUgOX8n5CY/FT7e2ypd3Lwqqe90fibUxU3xGg==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-session': ^0.1.2-rc.1 + '@deepseek-ai/dsh-invariants': ^0.1.5-alpha.1 - '@deepseek-ai/dsh-session@0.1.2-rc.1': - resolution: {integrity: sha512-jRGNPTbQcvIx1F2MVmmkoiHLgpC3Btqo1wkl/3JDLlOWRVWKOuaC+5fQiMrFPOYfno6YiX/c2UvmA0td8qB92w==} + '@deepseek-ai/dsh-session-persistence@0.1.5-alpha.1': + resolution: {integrity: sha512-9bWMlbGsY/IEeAr0fKHdIxC9X/FMVmFGjJFn1HzqGsLKYDtrWmU3F5jug3ZN+baIfcjhiIzyP6FVbNvkFrVQag==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-scope': ^0.1.2-rc.1 + '@deepseek-ai/dsh-brand': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-session': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-timeout': ^0.1.5-alpha.1 - '@deepseek-ai/dsh-settings@0.1.2-rc.1': - resolution: {integrity: sha512-wMmJ2w5S6I7hgmgPppuAVemltEcTmAa8gvii7fk4T2KGVCuBYgig8xPwQ8Lp2ukLZSzzE6ZVCQxhdYZyJX/USA==} + '@deepseek-ai/dsh-session-projection@0.1.5-alpha.1': + resolution: {integrity: sha512-V68REQFndPMxtbwsX+MOPkrBF6nvSWC5L5cbFheX5EcgIqe2gJthRH4j0aQyTt3HOHeu3VRlNDXeE/Ys6pEejg==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-brand': ^0.1.2-rc.1 - '@deepseek-ai/dsh-invariants': ^0.1.2-rc.1 - '@deepseek-ai/dsh-session': ^0.1.2-rc.1 + '@deepseek-ai/dsh-session': ^0.1.5-alpha.1 + + '@deepseek-ai/dsh-session@0.1.5-alpha.1': + resolution: {integrity: sha512-15+FxQFoVDDG1Lh0wA8dqVpVz7O4iUiXtVYM+ETg/ce6+gITPafXTEDd5i8HTIMnPRjZxEwX0BUlDDsChsjjNw==} + peerDependencies: + '@deepseek-ai/cordis': ^4.0.2 + '@deepseek-ai/dsh-scope': ^0.1.5-alpha.1 + + '@deepseek-ai/dsh-settings@0.1.5-alpha.1': + resolution: {integrity: sha512-nO2fAvijWIHyNvAQsWPpIVuFnz03q1adB94l7HRbfWfMi0fJwi1u96f28SM8BJuOOqLLOka7kRNmljoqc/1bDw==} + peerDependencies: + '@deepseek-ai/cordis': ^4.0.2 + '@deepseek-ai/dsh-brand': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-invariants': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-session': ^0.1.5-alpha.1 '@deepseek-ai/schemastery': ^3.18.2 - '@deepseek-ai/dsh-subagent@0.1.2-rc.1': - resolution: {integrity: sha512-GhRsymKb1uYbc7OwHh8WTdSXqAcx/KHFJwYX4BYJk90dsOkVx9ccBq6QoJfDYja34s1KCyv2UO/8MUtrzBsITw==} + '@deepseek-ai/dsh-subagent@0.1.5-alpha.1': + resolution: {integrity: sha512-wXjiTD9t230lI2AywTjEBfa6B5Q22eVkZqrAE8z1pUrd7/CPoyMSLmySEobfb9ugX1LcjLn8lGTngO8k+Gj8DQ==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-agent': ^0.1.2-rc.1 - '@deepseek-ai/dsh-agent-presets': ^0.1.2-rc.1 - '@deepseek-ai/dsh-attachment': ^0.1.2-rc.1 - '@deepseek-ai/dsh-invariants': ^0.1.2-rc.1 - '@deepseek-ai/dsh-jobs': ^0.1.2-rc.1 - '@deepseek-ai/dsh-llm': ^0.1.2-rc.1 - '@deepseek-ai/dsh-sandbox': ^0.1.2-rc.1 - '@deepseek-ai/dsh-sandbox-policy': ^0.1.2-rc.1 - '@deepseek-ai/dsh-scope': ^0.1.2-rc.1 - '@deepseek-ai/dsh-session': ^0.1.2-rc.1 - '@deepseek-ai/dsh-session-persistence': ^0.1.2-rc.1 - '@deepseek-ai/dsh-session-projection': ^0.1.2-rc.1 - '@deepseek-ai/dsh-session-projection-cache': ^0.1.2-rc.1 - '@deepseek-ai/dsh-session-query': ^0.1.2-rc.1 - '@deepseek-ai/dsh-system-prompt': ^0.1.2-rc.1 - '@deepseek-ai/dsh-tools': ^0.1.2-rc.1 - '@deepseek-ai/dsh-typert-protocol': ^0.1.2-rc.1 - '@deepseek-ai/dsh-user-approval': ^0.1.2-rc.1 - '@deepseek-ai/dsh-util-time': ^0.1.2-rc.1 + '@deepseek-ai/dsh-agent': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-agent-presets': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-attachment': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-invariants': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-jobs': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-llm': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-sandbox': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-sandbox-policy': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-scope': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-session': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-session-persistence': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-session-projection': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-session-projection-cache': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-session-query': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-system-prompt': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-tools': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-typert-protocol': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-user-approval': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-util-time': ^0.1.5-alpha.1 peerDependenciesMeta: '@deepseek-ai/dsh-agent-presets': optional: true @@ -596,61 +631,61 @@ packages: '@deepseek-ai/dsh-user-approval': optional: true - '@deepseek-ai/dsh-system-prompt@0.1.2-rc.1': - resolution: {integrity: sha512-7W93PZKIk4CHvjZGgLIxrrEKpk+6t8nQXje1vKR5vG73dfXH0t1MJ5WWH/fngadYc7c6KzM84ihQ66tJqJqyNQ==} + '@deepseek-ai/dsh-system-prompt@0.1.5-alpha.1': + resolution: {integrity: sha512-sQk/K05ZOFn2xbDbbCTfDN1Jr5lMBz+dPVreqkVsXbt72QxzTGM4yw8JmDJEvUMVnOKBsKyWpLrvBpcYN8Unbw==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-invariants': ^0.1.2-rc.1 - '@deepseek-ai/dsh-llm': ^0.1.2-rc.1 - '@deepseek-ai/dsh-scope': ^0.1.2-rc.1 + '@deepseek-ai/dsh-invariants': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-llm': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-scope': ^0.1.5-alpha.1 - '@deepseek-ai/dsh-timeout@0.1.2-rc.1': - resolution: {integrity: sha512-llz11yxWeJB8suKCa6hRLjNBWVaPn3Fbd0XoBS0bkZLhPFzO1mDorx/XMca2GhI7XF9A86CURQcDt+aGjjvqJA==} + '@deepseek-ai/dsh-timeout@0.1.5-alpha.1': + resolution: {integrity: sha512-QTcHWuKD1/LuHyXTvwX85LzWhgcqUeIza0FjajekIDYG7+jW1K6T6gzRYaOger0pAzJMylUz8iERU16cHR1c8A==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-tools@0.1.2-rc.1': - resolution: {integrity: sha512-W9kUio00s7WbM8kEwniyd4hfb3CeUxVczsjXbOcKtakfiTywNLeyRWkpx2fvwRAH2rBfg3qCgczVfS0DOw1csg==} + '@deepseek-ai/dsh-tools@0.1.5-alpha.1': + resolution: {integrity: sha512-TYvlnJlFZtenuoqe/SvTSrscfo6Y8NgwwV2zgaF+1rsOZBBiGPZNsUn0WG53JMs8M8wpkq7HIzIUUs2TOdThMw==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-agent': ^0.1.2-rc.1 - '@deepseek-ai/dsh-code-runtime': ^0.1.2-rc.1 - '@deepseek-ai/dsh-invariants': ^0.1.2-rc.1 - '@deepseek-ai/dsh-llm': ^0.1.2-rc.1 - '@deepseek-ai/dsh-scope': ^0.1.2-rc.1 - '@deepseek-ai/dsh-session': ^0.1.2-rc.1 - '@deepseek-ai/dsh-system-prompt': ^0.1.2-rc.1 - '@deepseek-ai/dsh-user-approval': ^0.1.2-rc.1 + '@deepseek-ai/dsh-agent': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-code-runtime': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-invariants': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-llm': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-scope': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-session': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-system-prompt': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-user-approval': ^0.1.5-alpha.1 - '@deepseek-ai/dsh-typert-protocol@0.1.2-rc.1': - resolution: {integrity: sha512-vBWD0NpriPd96ltbWaAw+iKeWFIEjiAcCWYtmqEcV1Ms+FaV02usjjm7LCmhSQhMZpBSBc7EZUX0spGlpjI1bA==} + '@deepseek-ai/dsh-typert-protocol@0.1.5-alpha.1': + resolution: {integrity: sha512-TYUDD047W5uxXK+8QX8lR8z/cAUqQBBy2mzU2kX7i7STtjzzeIqf+29JzANDFDFIXjwKmszk6VjeX7pwyjinIw==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-user-approval@0.1.2-rc.1': - resolution: {integrity: sha512-oYkLE4a/TwqVNi299pUf/QP1Yku6KScdUCTUyYbjaRBcN+/pXPpHikl2aoE2thH9D0uT/Kj6T2kz+wDDcFd9Xg==} + '@deepseek-ai/dsh-user-approval@0.1.5-alpha.1': + resolution: {integrity: sha512-xCuH17yYnuCpDMJyrNzHfL/wYs9iEGPGFOqnc2NWyFhNMsja+UGe3cDjqhCRvpYsXImM9AKxdAtjM18bDda0Lw==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-agent': ^0.1.2-rc.1 - '@deepseek-ai/dsh-brand': ^0.1.2-rc.1 - '@deepseek-ai/dsh-invariants': ^0.1.2-rc.1 - '@deepseek-ai/dsh-llm': ^0.1.2-rc.1 - '@deepseek-ai/dsh-scope': ^0.1.2-rc.1 - '@deepseek-ai/dsh-session': ^0.1.2-rc.1 - '@deepseek-ai/dsh-system-prompt': ^0.1.2-rc.1 + '@deepseek-ai/dsh-agent': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-brand': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-invariants': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-llm': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-scope': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-session': ^0.1.5-alpha.1 + '@deepseek-ai/dsh-system-prompt': ^0.1.5-alpha.1 - '@deepseek-ai/dsh-util-crypto@0.1.2-rc.1': - resolution: {integrity: sha512-gE8FznQ1hkknw9QMrHNSlm3UEyRYuj3MLVvlJ6NyH1kmOsdjnbrVbSs+Pn72k2oIV4Fiyu3Umg0sLznUEcsiXw==} + '@deepseek-ai/dsh-util-crypto@0.1.5-alpha.1': + resolution: {integrity: sha512-lTTpyRKowbzlvrwHJr7leMDt/Iul8cLmlrwn+JLyKid5YzX1NDK7MgkrEYivqGplX5Jd64GRcXU1bGpz9phULA==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-util-time@0.1.2-rc.1': - resolution: {integrity: sha512-OoudDLoYothbZKDbjO6kFu3NLgAnglIS8NFkg5fjnBtz8LGwplN8w7kRURLIrZtH38HknYhpDsdJh8LZai3U6A==} + '@deepseek-ai/dsh-util-time@0.1.5-alpha.1': + resolution: {integrity: sha512-Fry6C7D9d4pwaDzAMc0Mg7PUjHitvLZ6UxzLfHP4sm7/43aps9RWeKAdRz+2OMf2Ml51zn8jUMRrhfaQoB3Ghg==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 - '@deepseek-ai/dsh-util-values@0.1.2-rc.1': - resolution: {integrity: sha512-FQdgoK+KYVR0pBQb9fTsajvGb3gzUxEzJQEYnFOt+Qdwc4nkxJOPwJJdIvXuqvZaQ33DaOoqpnpcr2p5HpLu4g==} + '@deepseek-ai/dsh-util-values@0.1.5-alpha.1': + resolution: {integrity: sha512-slx5c7zAg1VAwvfkCNe24+NTu93FlQuI5vHLx0rb/S7HIVeCqI5Lm5158WCKjPTvHxXXOpACy7qY9L17lwCsFw==} peerDependencies: '@deepseek-ai/cordis': ^4.0.2 @@ -3506,32 +3541,51 @@ snapshots: '@deepseek-ai/cosmokit@1.8.3': {} - '@deepseek-ai/dsh-agent@0.1.2-rc.1(6f833cf90b9dd3f0d529840b1aebfb48)': + '@deepseek-ai/dsh-agent-loop@0.1.5-alpha.1(3d62ae7d0bb4adb238d8dfb4da37e073)': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-invariants': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-llm': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-scope': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)) - '@deepseek-ai/dsh-session': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))) - '@deepseek-ai/dsh-session-projection': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-session@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)))) - '@deepseek-ai/dsh-system-prompt': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))) - '@deepseek-ai/dsh-typert-protocol': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-agent': 0.1.5-alpha.1(288d2f4271bc6bef491bb5a5c084f12c) + '@deepseek-ai/dsh-brand': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-invariants': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-llm': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-scope': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)) + '@deepseek-ai/dsh-session': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))) + '@deepseek-ai/dsh-session-persistence': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-brand@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-session@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))))(@deepseek-ai/dsh-timeout@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)) + '@deepseek-ai/dsh-session-projection': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-session@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)))) + '@deepseek-ai/dsh-settings': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-brand@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-session@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))))(@deepseek-ai/schemastery@3.18.2) + '@deepseek-ai/dsh-system-prompt': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))) + '@deepseek-ai/dsh-tools': 0.1.5-alpha.1(239eb910b4462b7c15b6b44661457a15) + '@deepseek-ai/dsh-util-values': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/schemastery': 3.18.2 + zod: 4.4.3 - '@deepseek-ai/dsh-attachment@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-brand@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))': + '@deepseek-ai/dsh-agent@0.1.5-alpha.1(288d2f4271bc6bef491bb5a5c084f12c)': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-brand': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-invariants': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-llm': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-scope': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)) + '@deepseek-ai/dsh-session': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))) + '@deepseek-ai/dsh-session-projection': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-session@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)))) + '@deepseek-ai/dsh-system-prompt': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))) + '@deepseek-ai/dsh-typert-protocol': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-util-values': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-brand@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)': + '@deepseek-ai/dsh-attachment@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-brand@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))': + dependencies: + '@deepseek-ai/cordis': 4.0.2 + '@deepseek-ai/dsh-brand': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + + '@deepseek-ai/dsh-brand@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-client-locale@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)': + '@deepseek-ai/dsh-client-locale@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)': dependencies: '@deepseek-ai/cordis': 4.0.2 '@deepseek-ai/schemastery': 3.18.2 - '@deepseek-ai/dsh-client-ui-conversation@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(typescript@5.6.2)': + '@deepseek-ai/dsh-client-ui-conversation@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(typescript@5.6.2)': dependencies: '@deepseek-ai/cordis': 4.0.2 '@deepseek-ai/schemastery': 3.18.2 @@ -3544,7 +3598,7 @@ snapshots: transitivePeerDependencies: - typescript - '@deepseek-ai/dsh-client-ui-primitives@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)': + '@deepseek-ai/dsh-client-ui-primitives@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)': dependencies: '@deepseek-ai/cordis': 4.0.2 '@shikijs/langs': 4.4.3 @@ -3570,19 +3624,27 @@ snapshots: transitivePeerDependencies: - supports-color - '@deepseek-ai/dsh-client-ui-settings@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)': + '@deepseek-ai/dsh-client-ui-settings@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-client-ui-slots@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)': + '@deepseek-ai/dsh-client-ui-sidebar-right@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)': + dependencies: + '@deepseek-ai/cordis': 4.0.2 + clsx: 2.1.1 + picomatch: 4.0.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + + '@deepseek-ai/dsh-client-ui-slots@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-code-runtime@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)': + '@deepseek-ai/dsh-code-runtime@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-host-webserver@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)': + '@deepseek-ai/dsh-host-webserver@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)': dependencies: '@deepseek-ai/cordis': 4.0.2 '@deepseek-ai/schemastery': 3.18.2 @@ -3591,122 +3653,131 @@ snapshots: transitivePeerDependencies: - supports-color - '@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)': + '@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)': dependencies: '@deepseek-ai/cordis': 4.0.2 '@deepseek-ai/schemastery': 3.18.2 - '@deepseek-ai/dsh-llm@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)': + '@deepseek-ai/dsh-llm@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-brand': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-timeout': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-typert-protocol': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-util-crypto': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-util-values': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-brand': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-timeout': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-typert-protocol': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-util-crypto': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-util-values': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) '@deepseek-ai/schemastery': 3.18.2 zod: 4.4.3 - '@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))': + '@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-invariants': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-invariants': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-session-projection@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-session@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))))': + '@deepseek-ai/dsh-session-persistence@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-brand@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-session@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))))(@deepseek-ai/dsh-timeout@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-session': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))) + '@deepseek-ai/dsh-brand': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-session': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))) + '@deepseek-ai/dsh-timeout': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-util-values': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + + '@deepseek-ai/dsh-session-projection@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-session@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))))': + dependencies: + '@deepseek-ai/cordis': 4.0.2 + '@deepseek-ai/dsh-session': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))) zod: 4.4.3 - '@deepseek-ai/dsh-session@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)))': + '@deepseek-ai/dsh-session@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)))': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-brand': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-llm': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-scope': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)) - '@deepseek-ai/dsh-util-values': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-brand': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-llm': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-scope': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)) + '@deepseek-ai/dsh-util-values': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-settings@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-brand@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-session@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))))(@deepseek-ai/schemastery@3.18.2)': + '@deepseek-ai/dsh-settings@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-brand@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-session@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))))(@deepseek-ai/schemastery@3.18.2)': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-brand': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-invariants': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-session': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))) - '@deepseek-ai/dsh-util-values': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-brand': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-invariants': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-session': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))) + '@deepseek-ai/dsh-util-values': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) '@deepseek-ai/schemastery': 3.18.2 - '@deepseek-ai/dsh-subagent@0.1.2-rc.1(6241f70df87ac98c48965b657a1f7a60)': + '@deepseek-ai/dsh-subagent@0.1.5-alpha.1(0904ccd91d8f9ab5907c34d0b1c82b43)': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-agent': 0.1.2-rc.1(6f833cf90b9dd3f0d529840b1aebfb48) - '@deepseek-ai/dsh-attachment': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-brand@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)) - '@deepseek-ai/dsh-brand': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-invariants': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-llm': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-scope': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)) - '@deepseek-ai/dsh-session': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))) - '@deepseek-ai/dsh-system-prompt': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))) - '@deepseek-ai/dsh-tools': 0.1.2-rc.1(de5e1ff2d608fbe22667925341ad1dfc) - '@deepseek-ai/dsh-typert-protocol': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-util-time': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-util-values': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-agent': 0.1.5-alpha.1(288d2f4271bc6bef491bb5a5c084f12c) + '@deepseek-ai/dsh-attachment': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-brand@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)) + '@deepseek-ai/dsh-brand': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-invariants': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-llm': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-scope': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)) + '@deepseek-ai/dsh-session': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))) + '@deepseek-ai/dsh-system-prompt': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))) + '@deepseek-ai/dsh-tools': 0.1.5-alpha.1(239eb910b4462b7c15b6b44661457a15) + '@deepseek-ai/dsh-typert-protocol': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-util-time': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-util-values': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) zod: 4.4.3 optionalDependencies: - '@deepseek-ai/dsh-session-projection': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-session@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)))) - '@deepseek-ai/dsh-user-approval': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-agent@0.1.2-rc.1(6f833cf90b9dd3f0d529840b1aebfb48))(@deepseek-ai/dsh-brand@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)))(@deepseek-ai/dsh-session@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))))(@deepseek-ai/dsh-system-prompt@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)))) + '@deepseek-ai/dsh-session-persistence': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-brand@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-session@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))))(@deepseek-ai/dsh-timeout@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)) + '@deepseek-ai/dsh-session-projection': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-session@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)))) + '@deepseek-ai/dsh-user-approval': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-agent@0.1.5-alpha.1(288d2f4271bc6bef491bb5a5c084f12c))(@deepseek-ai/dsh-brand@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)))(@deepseek-ai/dsh-session@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))))(@deepseek-ai/dsh-system-prompt@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)))) - '@deepseek-ai/dsh-system-prompt@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)))': + '@deepseek-ai/dsh-system-prompt@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)))': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-invariants': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-llm': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-scope': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)) + '@deepseek-ai/dsh-invariants': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-llm': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-scope': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)) '@deepseek-ai/schemastery': 3.18.2 - '@deepseek-ai/dsh-timeout@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)': + '@deepseek-ai/dsh-timeout@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-tools@0.1.2-rc.1(de5e1ff2d608fbe22667925341ad1dfc)': + '@deepseek-ai/dsh-tools@0.1.5-alpha.1(239eb910b4462b7c15b6b44661457a15)': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-agent': 0.1.2-rc.1(6f833cf90b9dd3f0d529840b1aebfb48) - '@deepseek-ai/dsh-brand': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-code-runtime': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-invariants': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-llm': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-scope': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)) - '@deepseek-ai/dsh-session': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))) - '@deepseek-ai/dsh-system-prompt': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))) - '@deepseek-ai/dsh-user-approval': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-agent@0.1.2-rc.1(6f833cf90b9dd3f0d529840b1aebfb48))(@deepseek-ai/dsh-brand@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)))(@deepseek-ai/dsh-session@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))))(@deepseek-ai/dsh-system-prompt@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)))) - '@deepseek-ai/dsh-util-values': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-agent': 0.1.5-alpha.1(288d2f4271bc6bef491bb5a5c084f12c) + '@deepseek-ai/dsh-brand': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-code-runtime': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-invariants': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-llm': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-scope': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)) + '@deepseek-ai/dsh-session': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))) + '@deepseek-ai/dsh-system-prompt': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))) + '@deepseek-ai/dsh-user-approval': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-agent@0.1.5-alpha.1(288d2f4271bc6bef491bb5a5c084f12c))(@deepseek-ai/dsh-brand@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)))(@deepseek-ai/dsh-session@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))))(@deepseek-ai/dsh-system-prompt@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)))) + '@deepseek-ai/dsh-util-values': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) '@deepseek-ai/schemastery': 3.18.2 - '@deepseek-ai/dsh-typert-protocol@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)': + '@deepseek-ai/dsh-typert-protocol@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-user-approval@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-agent@0.1.2-rc.1(6f833cf90b9dd3f0d529840b1aebfb48))(@deepseek-ai/dsh-brand@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)))(@deepseek-ai/dsh-session@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))))(@deepseek-ai/dsh-system-prompt@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))))': + '@deepseek-ai/dsh-user-approval@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-agent@0.1.5-alpha.1(288d2f4271bc6bef491bb5a5c084f12c))(@deepseek-ai/dsh-brand@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)))(@deepseek-ai/dsh-session@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))))(@deepseek-ai/dsh-system-prompt@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))))': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-agent': 0.1.2-rc.1(6f833cf90b9dd3f0d529840b1aebfb48) - '@deepseek-ai/dsh-brand': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-invariants': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-llm': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-scope': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)) - '@deepseek-ai/dsh-session': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))) - '@deepseek-ai/dsh-system-prompt': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))) + '@deepseek-ai/dsh-agent': 0.1.5-alpha.1(288d2f4271bc6bef491bb5a5c084f12c) + '@deepseek-ai/dsh-brand': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-invariants': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-llm': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-scope': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)) + '@deepseek-ai/dsh-session': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))) + '@deepseek-ai/dsh-system-prompt': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-llm@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-scope@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))) '@deepseek-ai/schemastery': 3.18.2 - '@deepseek-ai/dsh-util-crypto@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)': + '@deepseek-ai/dsh-util-crypto@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-util-time@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)': + '@deepseek-ai/dsh-util-time@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)': dependencies: '@deepseek-ai/cordis': 4.0.2 - '@deepseek-ai/dsh-util-values@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2)': + '@deepseek-ai/dsh-util-values@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2)': dependencies: '@deepseek-ai/cordis': 4.0.2 @@ -3767,15 +3838,15 @@ snapshots: '@exodus/bytes@1.15.1': {} - '@huanlin/dsh-plugin-better-locale@0.1.0(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-client-locale@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-client-ui-primitives@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-client-ui-settings@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-client-ui-slots@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-invariants@0.1.2-rc.1(@deepseek-ai/cordis@4.0.2))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + '@huanlin/dsh-plugin-better-locale@0.1.0(@deepseek-ai/cordis@4.0.2)(@deepseek-ai/dsh-client-locale@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-client-ui-primitives@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-client-ui-settings@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-client-ui-slots@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(@deepseek-ai/dsh-invariants@0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@deepseek-ai/cordis': 4.0.2 optionalDependencies: - '@deepseek-ai/dsh-client-locale': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-client-ui-primitives': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-client-ui-settings': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-client-ui-slots': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) - '@deepseek-ai/dsh-invariants': 0.1.2-rc.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-client-locale': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-client-ui-primitives': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-client-ui-settings': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-client-ui-slots': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) + '@deepseek-ai/dsh-invariants': 0.1.5-alpha.1(@deepseek-ai/cordis@4.0.2) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) diff --git a/src/assistant-live.ts b/src/assistant-live.ts new file mode 100644 index 0000000..e2a25e2 --- /dev/null +++ b/src/assistant-live.ts @@ -0,0 +1,140 @@ +/** + * Process-local live assistant stream buffer (DSH 0.1.5+). + * + * 0.1.2 appended a durable `assistant/chunk` session event for every model + * delta, so the side-chat transcript and the inherited in-progress snapshot + * could read streaming text straight out of the session log. 0.1.5 removed + * that event: an in-flight attempt now publishes `agent/assistant-stream` + * frames (start / chunk / end) that are NOT part of the log, and the durable + * record lands only at settlement — `assistant/message` (with the exact + * `stream` embedded) or `assistant/attempt` (a failed attempt that committed + * no message, also with its `stream`). + * + * This module folds those frames into a bounded per-session buffer of + * normalized chunks — the same information the old `assistant/chunk` events + * carried — so the plugin keeps a live transcript and an honest in-progress + * snapshot. The buffer is cleared when an attempt ends, so a settled step + * never duplicates its durable message. + */ +import type { Context } from './context-types.ts' + +/** One normalized live delta, keyed by its attempt and dense position. */ +export interface AssistantLiveChunk { + /** The attempt these chunks belong to (DSH `LlmAttemptId`). */ + readonly attemptId: string + /** The turn the attempt belongs to. */ + readonly turn: number + /** The step the attempt belongs to. */ + readonly step: number + /** Dense zero-based position within the attempt (DSH's own index). */ + readonly index: number + /** Safe-integer timestamp of the frame (reused by the durable stream). */ + readonly time: number + /** The raw model stream chunk (`text-delta` / `reasoning-delta` / …). */ + readonly chunk: Record +} + +/** The plugin's read face over the live frames of every session. */ +export interface AssistantLiveBuffer { + /** + * The live chunks of one session's active attempt, in index order. + * @param sessionId - the session whose attempt is streaming. + * @returns the chunks; empty when nothing is streaming for that session. + */ + chunksFor(sessionId: string): readonly AssistantLiveChunk[] + /** Stop observing the agent stream. */ + dispose(): void +} + +/** Per-attempt buffer ceiling; beyond it the oldest deltas are dropped. */ +export const LIVE_CHUNK_CAP = 4000 + +/** How many sessions may hold a live buffer at once (the oldest is dropped). */ +export const LIVE_SESSION_CAP = 64 + +/** One session's active attempt as it accumulates. */ +interface Attempt { + readonly attemptId: string + readonly turn: number + readonly step: number + readonly chunks: AssistantLiveChunk[] + /** The next dense frame index this attempt accepts (independent of the + * window below, which drops the oldest chunks once the cap is reached). */ + nextIndex: number +} + +/** A frame's `chunk` payload must be a JSON record to be worth buffering. */ +function chunkOf(value: unknown): Record | undefined { + return typeof value === 'object' && value !== null && !Array.isArray(value) + ? value as Record + : undefined +} + +/** A finite non-negative integer, or undefined. */ +function countOf(value: unknown): number | undefined { + return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : undefined +} + +/** + * Fold `agent/assistant-stream` frames into per-session live buffers. + * + * Frames arrive for every attached agent, so the buffer is keyed by the + * emitting session and ignores anything it cannot identify. An out-of-order + * or mismatched chunk drops the attempt rather than splicing a gap: a + * transcript with a hole is worse than one that settles at the next durable + * event. + * @param ctx - host plugin context (its `on` subscribes the session feed). + * @param cap - per-attempt chunk ceiling. + * @returns the read face and a disposer unbinding the listener. + */ +export function createAssistantLiveBuffer(ctx: Context, cap: number = LIVE_CHUNK_CAP): AssistantLiveBuffer { + const attempts = new Map() + const off = ctx.on('agent/assistant-stream', (payload) => { + const record = payload as { agent?: unknown; frame?: unknown } | null + const frame = record?.frame as Record | undefined + if (frame === undefined) return + const session = (record?.agent as { session?: { id?: unknown } } | undefined)?.session + const sessionId = typeof session?.id === 'string' ? session.id : undefined + if (sessionId === undefined) return + const type = frame['type'] + if (type === 'end') { + attempts.delete(sessionId) + return + } + const attemptId = typeof frame['attemptId'] === 'string' ? frame.attemptId : undefined + if (attemptId === undefined) return + if (type === 'start') { + const turn = countOf(frame['turn']) + const step = countOf(frame['step']) + if (turn === undefined || step === undefined) return + const known = attempts.has(sessionId) + attempts.delete(sessionId) + // Only a NEW session may evict the oldest tracked one. + if (!known && attempts.size >= LIVE_SESSION_CAP) { + const oldest = attempts.keys().next().value + if (oldest !== undefined) attempts.delete(oldest) + } + attempts.set(sessionId, { attemptId, turn, step, chunks: [], nextIndex: 0 }) + return + } + if (type !== 'chunk') return + const attempt = attempts.get(sessionId) + if (attempt === undefined || attempt.attemptId !== attemptId) return + const index = countOf(frame['index']) + const chunk = chunkOf(frame['chunk']) + // Dense positions only: a gap means the buffer missed frames (a + // replacement attempt, a reconnect) and its text would be wrong. + if (index === undefined || chunk === undefined || index !== attempt.nextIndex) { + attempts.delete(sessionId) + return + } + attempt.nextIndex = index + 1 + const time = countOf(frame['time']) ?? 0 + attempt.chunks.push({ attemptId, turn: attempt.turn, step: attempt.step, index, time, chunk }) + if (attempt.chunks.length > cap) attempt.chunks.splice(0, attempt.chunks.length - cap) + }) + return { + chunksFor: sessionId => attempts.get(sessionId)?.chunks ?? [], + dispose: () => { off() }, + } +} diff --git a/src/client/SideCardSection.tsx b/src/client/SideCardSection.tsx index 6a36a7b..dfdb632 100644 --- a/src/client/SideCardSection.tsx +++ b/src/client/SideCardSection.tsx @@ -901,17 +901,6 @@ export function SideCardSection({ store, service }: SideCardSectionProps) { {t('settingsWidthSuffix')} -
- - {t('settingsOpenPathTitle')} - {t('settingsOpenPathDesc')} - - { applyPref({ interceptOpenPath: next }) }} - /> -
{t('settingsOpenToolsTitle')} diff --git a/src/client/SideChatView.tsx b/src/client/SideChatView.tsx index 367ee88..ed78619 100644 --- a/src/client/SideChatView.tsx +++ b/src/client/SideChatView.tsx @@ -58,6 +58,7 @@ import { sideThreadRows, threadHasCompletedTurn, threadTrailingPending, + type SidechatLiveEvent, type SidechatThreadInfo, } from '../sidechat-core.ts' import { @@ -112,6 +113,8 @@ const inFlightStarts = new Set() * the afterSeq delta and never re-download what they already hold). */ interface ThreadCache { entries: SidebarHistoryEntry[] + /** The CURRENT attempt's live deltas (replaced every pull, never merged). */ + live: SidechatLiveEvent[] } /** Row-render labels (locale-dependent, memoized once per mount). */ @@ -383,7 +386,7 @@ export function SideChatView(props: { const [info, setInfo] = useState(null) const [menuOpen, setMenuOpen] = useState(false) - const cacheRef = useRef({ entries: [] }) + const cacheRef = useRef({ entries: [], live: [] }) // The previous poll's rows (see the mapping's reuse pass below). const prevRowsRef = useRef([]) const controllerRef = useRef(null) @@ -457,14 +460,18 @@ export function SideChatView(props: { try { const cache = cacheRef.current const afterSeq = cache.entries.at(-1)?.event.seq - const { events } = await api.sidechatEvents(childId, afterSeq, controller.signal) + const { events, live } = await api.sidechatEvents(childId, afterSeq, controller.signal) + // The live set is the CURRENT attempt, so it replaces whatever the + // previous pull held; a settled step drops out of the host buffer and + // its durable assistant/message takes over in the mapping. + cache.live = live ?? [] if (events.length > 0) { // Wire events arrive as parsed JSON; the mirror narrows data to the // record the mapping reads. const incoming = events.map(event => ({ event: event as SidebarSessionEvent })) cache.entries = mergeBySeq(cache.entries, incoming) - setRevision(value => value + 1) } + setRevision(value => value + 1) } catch { // Aborted by a newer pull or a wire failure: keep the last rows. } @@ -482,7 +489,7 @@ export function SideChatView(props: { // Reset the transcript cache whenever the binding changes, then focus // the composer — it owns the first message of a fresh thread. useEffect(() => { - cacheRef.current = { entries: [] } + cacheRef.current = { entries: [], live: [] } prevRowsRef.current = [] controllerRef.current?.abort() setError(null) @@ -532,8 +539,24 @@ export function SideChatView(props: { // The previous poll's rows ride into the mapping so unchanged rows keep // their object identity (see reuseRows): the 2s poll re-renders only the // changed tail instead of re-parsing markdown for the whole transcript. + // The live deltas are appended AFTER the durable events (they are the + // in-flight tail), and a delta whose step already settled durably is + // dropped: the durable message is authoritative and would otherwise render + // twice. const rows = useMemo(() => { - const next = threadId === undefined ? [] : transcriptRows(cacheRef.current.entries, prevRowsRef.current) + const cache = cacheRef.current + const settled = new Set( + cache.entries + .filter(entry => entry.event.type === 'assistant/message') + .map(entry => { + const data = entry.event.data as { turn?: unknown; step?: unknown } + return `${String(data.turn)}:${String(data.step)}` + }), + ) + const live = cache.live + .filter(event => !settled.has(`${String(event.data.turn)}:${String(event.data.step)}`)) + .map(event => ({ event: event as SidebarSessionEvent })) + const next = threadId === undefined ? [] : transcriptRows([...cache.entries, ...live], prevRowsRef.current) prevRowsRef.current = next return next }, diff --git a/src/client/api.ts b/src/client/api.ts index cf0cacb..2c40f36 100644 --- a/src/client/api.ts +++ b/src/client/api.ts @@ -8,7 +8,7 @@ */ import { encodeHtmlUrl } from '../html-route.ts' import type { LastActivity } from '../subagent-activity.ts' -import type { SidechatLogEvent, SidechatThreadInfo } from '../sidechat-core.ts' +import type { SidechatLiveEvent, SidechatLogEvent, SidechatThreadInfo } from '../sidechat-core.ts' import type { SidebarSessionEvent } from '../context-types.ts' import type { BrowserProbeResult } from './browser.ts' @@ -390,9 +390,12 @@ export const api = { call('sidechat.info', { childId }), /** One transcript pull of a Side Chat thread: the thread's OWN events * (the inherited seed is cut host-side and never crosses the wire). - * `afterSeq` narrows the response to the delta beyond it (poll tail). */ + * `afterSeq` narrows the response to the delta beyond it (poll tail). + * `live` is the thread's in-flight model deltas (DSH 0.1.5 publishes them + * outside the session log) — the CURRENT attempt on every pull, never a + * delta, so the caller replaces its live set instead of appending. */ sidechatEvents: (childId: string, afterSeq?: number, signal?: AbortSignal) => - call<{ events: SidechatLogEvent[] }>('sidechat.events', { + call<{ events: SidechatLogEvent[]; live: SidechatLiveEvent[] }>('sidechat.events', { childId, ...(afterSeq !== undefined ? { afterSeq } : {}), }, signal), diff --git a/src/client/index.tsx b/src/client/index.tsx index aed2ee8..9f016ab 100644 --- a/src/client/index.tsx +++ b/src/client/index.tsx @@ -17,7 +17,7 @@ import { revalidateChunksOnReactivate, setChunkModuleSystem } from './chunk-load import { registerBuiltins } from './builtins/index.ts' import { Sidebar } from './Sidebar.tsx' import { RenderBoundary } from './RenderBoundary.tsx' -import { registerOpenPathInterception, registerTurnTailInterception } from './intercept.tsx' +import { registerTurnTailInterception } from './intercept.tsx' import { registerLinkInterception } from './link-intercept.ts' import { registerImeGuard } from './ime-guard.ts' import { registerSettingsNavIcon } from './settings-nav-icon.ts' @@ -342,18 +342,6 @@ export function apply(ctx: Context): void { 'dsh-better-sidebar: turn-tail interception', ) - ctx.effect( - () => { - try { - return registerOpenPathInterception(ctx, sidebarStore) - } catch (error) { - fail('interception', error) - return () => {} - } - }, - 'dsh-better-sidebar: open-path interception', - ) - ctx.effect( () => { try { diff --git a/src/client/intercept.tsx b/src/client/intercept.tsx index 8630a8b..7f04b00 100644 --- a/src/client/intercept.tsx +++ b/src/client/intercept.tsx @@ -11,7 +11,6 @@ import type { Context } from '../context-types.ts' import { firstLeaf, revealPaths, togglePanel, type SidebarStore } from './state.ts' import { t } from './locales.ts' import { resolveSidebarPath, selectProducedFiles } from './produced-files.ts' -import { wrapOpenWorkspacePath, type OpenWorkspacePathService } from './openpath-intercept.ts' import css from './sidebar.module.css' /** Open a file in the sidebar's editor (used by the intercepted row and the explorer). */ @@ -25,13 +24,6 @@ export function openSidebarFile(ctx: Context, store: SidebarStore, sessionId: st ctx.get('betterSidebar')?.openTab({ type: 'editor', title, path: absolute, id: `editor:${absolute}` }) } -/** - * The produced files the turn-tail selector last matched for the visible - * session. The "Show in folder" gesture carries no file path of its own - * (`'.'`), so the reveal highlights exactly these rows when available. - */ -let lastProduced: readonly string[] = [] - /** * Reveal the produced files in the sidebar explorer: expand their parent * directories, highlight the rows, and focus the explorer tab (expanding the @@ -137,9 +129,7 @@ export function registerTurnTailInterception(ctx: Context, store: SidebarStore): select: (owner) => { if (store.getSuspended()) return null if (store.getPrefs().tabsEnabled['editor'] === false) return null - const matched = selectProducedFiles(owner) - if (matched !== null) lastProduced = matched - return matched + return selectProducedFiles(owner) }, priority: -1, registrant: 'dsh-better-sidebar', @@ -149,38 +139,3 @@ export function registerTurnTailInterception(ctx: Context, store: SidebarStore): }), }, SidebarProducedFiles)) } - -/** - * Register the chat file-open interception: shadows - * `remote.session.openWorkspacePath` — the single funnel every chat-side - * file open goes through on alpha hosts (tool-row path links, the - * produced-files row, prose mentions, inline-code paths) — so opens land in - * the sidebar editor instead of the Host OS. The folder-reveal gesture - * ("Show in folder" passes `'.'`) is the one exception: it is routed to the - * explorer. Gated by BOTH the `interceptOpenPath` pref and the editor tab's - * enable switch; declined opens fall through to the original remote call. - * - * The `remote.session` namespace service mounts asynchronously (the gateway - * client creates it when the session-controller contribution arrives) and - * is recreated on contribution remounts, so the wrapper installs through - * `ctx.inject`: the callback runs once the service exists and re-runs after - * every remount, re-applying the shadow on the fresh instance. Returns the - * disposer (disposes the inject fiber, which restores the original method - * descriptor — HMR-safe). - */ -export function registerOpenPathInterception(ctx: Context, store: SidebarStore): () => void { - const fiber = ctx.inject(['remote.session'], (fctx) => { - fctx.effect(() => { - const service = fctx.get('remote.session') as OpenWorkspacePathService - return wrapOpenWorkspacePath(service, { - takeoverEnabled: () => !store.getSuspended() - && store.getPrefs().interceptOpenPath !== false - && store.getPrefs().tabsEnabled['editor'] !== false, - currentSessionId: () => ctx.sessions.list.getSnapshot().current, - openInSidebar: (path, sessionId) => { openSidebarFile(ctx, store, sessionId, path) }, - revealInExplorer: (_path, sessionId) => { revealInExplorer(ctx, store, sessionId, lastProduced) }, - }) - }, 'dsh-better-sidebar: open-path interception wrap') - }) - return () => { void fiber.dispose() } -} diff --git a/src/client/locales-ar.ts b/src/client/locales-ar.ts index cc66eec..ffa4f95 100644 --- a/src/client/locales-ar.ts +++ b/src/client/locales-ar.ts @@ -238,8 +238,6 @@ export const ar: Record = { settingsWidthTitle: 'الحصة الافتراضية للعرض', settingsWidthDesc: 'الحصة الافتراضية للبطاقة الجانبية من عرض النافذة للمحادثات الجديدة (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: 'فتح ملفات المحادثة في الشريط الجانبي', - settingsOpenPathDesc: 'فتح روابط الملفات في المحادثة (صفوف الأدوات، الملفات الناتجة، الإشارات) في محرّر الشريط الجانبي بدلاً من التطبيق الافتراضي للنظام', settingsOpenToolsTitle: 'حقن أداة فتح الشريط الجانبي للنموذج', settingsOpenToolsDesc: 'عند التفعيل، يمكن للنموذج فتح الملفات والمجلدات وصفحات HTTP(S) في الشريط الجانبي عبر أداة sidebar_open (معطّل افتراضياً)', settingsTitleBarTitle: 'وضع توافق الموضع', diff --git a/src/client/locales-de.ts b/src/client/locales-de.ts index d34b382..1baa471 100644 --- a/src/client/locales-de.ts +++ b/src/client/locales-de.ts @@ -223,8 +223,6 @@ export const de: Record = { settingsWidthTitle: 'Standardbreite', settingsWidthDesc: 'Standardanteil der Seitenkarte an der Fensterbreite für neue Unterhaltungen (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: 'Chat-Dateien in der Seitenleiste öffnen', - settingsOpenPathDesc: 'Dateilinks im Chat (Werkzeugzeilen, erstellte Dateien, Erwähnungen) werden im Seitenleisten-Editor geöffnet statt in der System-Standardanwendung', settingsOpenToolsTitle: 'Seitenleisten-Öffnungswerkzeug für das Modell bereitstellen', settingsOpenToolsDesc: 'Wenn aktiviert, kann das Modell über das sidebar_open-Werkzeug Dateien, Ordner und HTTP(S)-Seiten in der Seitenleiste öffnen (standardmäßig deaktiviert)', settingsTitleBarTitle: 'Kompatibilitätsmodus der Position', diff --git a/src/client/locales-fr.ts b/src/client/locales-fr.ts index 567fffd..d47ab59 100644 --- a/src/client/locales-fr.ts +++ b/src/client/locales-fr.ts @@ -230,8 +230,6 @@ export const fr: Record = { settingsWidthTitle: 'Part de largeur par défaut', settingsWidthDesc: 'Pourcentage de la largeur de la fenêtre occupé par la carte latérale pour les nouvelles sessions (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: 'Ouvrir les fichiers de discussion dans la barre latérale', - settingsOpenPathDesc: 'À la place de l’application par défaut du système, ouvrir dans l’éditeur de la barre latérale les liens vers les fichiers du chat (lignes d’outils, listes de produits, mentions de fichiers)', settingsOpenToolsTitle: 'Injecter l\'outil d\'ouverture latérale pour le modèle', settingsOpenToolsDesc: 'Une fois activé, le modèle peut ouvrir des fichiers, dossiers et pages HTTP(S) dans la barre latérale via l\'outil sidebar_open (désactivé par défaut)', settingsTitleBarTitle: 'Mode de compatibilité de position', diff --git a/src/client/locales-hi.ts b/src/client/locales-hi.ts index 3c0ee0b..ce19341 100644 --- a/src/client/locales-hi.ts +++ b/src/client/locales-hi.ts @@ -237,8 +237,6 @@ export const hi: Record = { settingsWidthTitle: 'डिफ़ॉल्ट चौड़ाई हिस्सा', settingsWidthDesc: 'नए वार्तालाप के लिए साइड कार्ड की विंडो चौड़ाई का डिफ़ॉल्ट हिस्सा (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: 'चैट फ़ाइलें साइडबार में खोलें', - settingsOpenPathDesc: 'चैट में फ़ाइल लिंक (टूल पंक्ति, उत्पादित फ़ाइलें, उल्लेख) क्लिक करने पर सिस्टम डिफ़ॉल्ट ऐप के बजाय साइडबार एडिटर में खोलें', settingsOpenToolsTitle: 'मॉडल के लिए साइडबार ओपन टूल इंजेक्ट करें', settingsOpenToolsDesc: 'चालू होने पर, मॉडल sidebar_open टूल से साइडबार में फ़ाइलें, फ़ोल्डर और HTTP(S) पेज खोल सकता है (डिफ़ॉल्ट रूप से बंद)', settingsTitleBarTitle: 'स्थिति संगतता मोड', diff --git a/src/client/locales-id.ts b/src/client/locales-id.ts index 45a1002..952f8ba 100644 --- a/src/client/locales-id.ts +++ b/src/client/locales-id.ts @@ -235,8 +235,6 @@ export const id: Record = { settingsWidthTitle: 'Porsi lebar default', settingsWidthDesc: 'Porsi lebar jendela default kartu samping untuk obrolan baru (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: 'Buka berkas obrolan di sidebar', - settingsOpenPathDesc: 'Buka tautan berkas di obrolan (baris alat, berkas yang dihasilkan, mention) di editor sidebar alih-alih aplikasi default sistem', settingsOpenToolsTitle: 'Suntik alat buka sidebar untuk model', settingsOpenToolsDesc: 'Saat diaktifkan, model dapat membuka file, folder, dan halaman HTTP(S) di sidebar melalui alat sidebar_open (nonaktif secara default)', settingsTitleBarTitle: 'Mode kompatibilitas posisi', diff --git a/src/client/locales-it.ts b/src/client/locales-it.ts index a7ab143..9d6052a 100644 --- a/src/client/locales-it.ts +++ b/src/client/locales-it.ts @@ -228,8 +228,6 @@ export const it: Record = { settingsWidthTitle: 'Quota di larghezza predefinita', settingsWidthDesc: 'La quota predefinita della scheda laterale sulla larghezza della finestra per le nuove conversazioni (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: 'Apri i file della chat nella barra laterale', - settingsOpenPathDesc: 'Apre i collegamenti ai file nella chat (righe di strumenti, file prodotti, menzioni) nell’editor della barra laterale invece dell’app predefinita di sistema', settingsOpenToolsTitle: 'Iniettare lo strumento di apertura della barra laterale per il modello', settingsOpenToolsDesc: 'Se attivato, il modello può aprire file, cartelle e pagine HTTP(S) nella barra laterale tramite lo strumento sidebar_open (disattivato per impostazione predefinita)', settingsTitleBarTitle: 'Modalità di compatibilità della posizione', diff --git a/src/client/locales-ja.ts b/src/client/locales-ja.ts index a967059..1b780a2 100644 --- a/src/client/locales-ja.ts +++ b/src/client/locales-ja.ts @@ -237,8 +237,6 @@ export const ja: Record = { settingsWidthTitle: 'デフォルト幅の比率', settingsWidthDesc: '新規会話時のサイドカードがウィンドウ幅に占める割合 (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: 'チャット内ファイルをサイドバーで開く', - settingsOpenPathDesc: 'チャット内のファイルリンク(ツール行、産物リスト、ファイル言及)クリック時に、システム既定アプリではなくサイドバーエディターで開く', settingsOpenToolsTitle: 'モデルにサイドバー開くツールを注入', settingsOpenToolsDesc: 'オンにすると、モデルは sidebar_open ツールでサイドバーにファイル・フォルダー・HTTP(S) ページを開ける(デフォルトオフ)', settingsTitleBarTitle: '位置互換モード', diff --git a/src/client/locales-ko.ts b/src/client/locales-ko.ts index 4e8b4f1..82a4938 100644 --- a/src/client/locales-ko.ts +++ b/src/client/locales-ko.ts @@ -229,8 +229,6 @@ export const ko: Record = { settingsWidthTitle: '기본 너비 비율', settingsWidthDesc: '새 대화에서 사이드 카드가 창 너비에서 차지하는 비율(%) (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: '채팅 영역 파일을 사이드바에서 열기', - settingsOpenPathDesc: '채팅에서 파일 링크(도구 행, 산출물 목록, 파일 언급)를 클릭하면 사이드바 편집기에서 열고, 시스템 기본 앱은 호출하지 않습니다', settingsOpenToolsTitle: '모델에 사이드바 열기 도구 주입', settingsOpenToolsDesc: '켜면 모델이 sidebar_open 도구로 사이드바에서 파일·폴더·HTTP(S) 페이지를 열 수 있습니다(기본 꺼짐)', settingsTitleBarTitle: '위치 호환 모드', diff --git a/src/client/locales-nl.ts b/src/client/locales-nl.ts index 8106e22..a3da95c 100644 --- a/src/client/locales-nl.ts +++ b/src/client/locales-nl.ts @@ -235,8 +235,6 @@ export const nl: Record = { settingsWidthTitle: 'Standaard breedte-aandeel', settingsWidthDesc: 'Het standaard breedte-aandeel van de zijkaart voor nieuwe conversaties (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: 'Chatbestanden in de zijbalk openen', - settingsOpenPathDesc: 'Bestandslinks in de chat (toolrijen, geproduceerde bestanden, vermeldingen) openen in de zijbalk-editor in plaats van de standaard systeemapp', settingsOpenToolsTitle: 'Zijbalk-openen-tool voor het model injecteren', settingsOpenToolsDesc: 'Indien ingeschakeld kan het model bestanden, mappen en HTTP(S)-pagina\'s in de zijbalk openen via de sidebar_open-tool (standaard uit)', settingsTitleBarTitle: 'Positiecompatibiliteitsmodus', diff --git a/src/client/locales-pl.ts b/src/client/locales-pl.ts index dd62423..855ea69 100644 --- a/src/client/locales-pl.ts +++ b/src/client/locales-pl.ts @@ -239,8 +239,6 @@ export const pl: Record = { settingsWidthTitle: 'Domyślny udział szerokości', settingsWidthDesc: 'Domyślny udział szerokości okna dla karty bocznej w nowych rozmowach (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: 'Otwieraj pliki czatu w panelu bocznym', - settingsOpenPathDesc: 'Otwieraj linki plików na czacie (wiersze narzędzi, wyprodukowane pliki, wzmianki) w edytorze panelu bocznego zamiast w domyślnej aplikacji systemu', settingsOpenToolsTitle: 'Wstrzyknij narzędzie otwierania panelu bocznego dla modelu', settingsOpenToolsDesc: 'Po włączeniu model może otwierać pliki, foldery i strony HTTP(S) w panelu bocznym za pomocą narzędzia sidebar_open (domyślnie wyłączone)', settingsTitleBarTitle: 'Tryb zgodności pozycji', diff --git a/src/client/locales-pt.ts b/src/client/locales-pt.ts index 08c0d25..7146bff 100644 --- a/src/client/locales-pt.ts +++ b/src/client/locales-pt.ts @@ -220,8 +220,6 @@ export const pt: Record = { settingsWidthTitle: 'Participação padrão na largura', settingsWidthDesc: 'Participação padrão do cartão lateral na largura da janela em novas conversas (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: 'Abrir arquivos do chat na barra lateral', - settingsOpenPathDesc: 'Abrir links de arquivos no chat (linhas de ferramentas, arquivos produzidos, menções) no editor da barra lateral em vez do aplicativo padrão do sistema', settingsOpenToolsTitle: 'Injetar a ferramenta de abertura na barra lateral para o modelo', settingsOpenToolsDesc: 'Quando ativado, o modelo pode abrir arquivos, pastas e páginas HTTP(S) na barra lateral pela ferramenta sidebar_open (desativado por padrão)', settingsTitleBarTitle: 'Modo de compatibilidade de posição', diff --git a/src/client/locales-ru.ts b/src/client/locales-ru.ts index 10711b8..5a6155b 100644 --- a/src/client/locales-ru.ts +++ b/src/client/locales-ru.ts @@ -234,8 +234,6 @@ export const ru: Record = { settingsWidthTitle: 'Доля ширины по умолчанию', settingsWidthDesc: 'Доля боковой карточки в ширине окна для новых сессий, в процентах (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: 'Открывать файлы чата в боковой панели', - settingsOpenPathDesc: 'При щелчке по ссылке на файл в чате (строки инструментов, списки результатов, упоминания) файл открывается в редакторе боковой панели, а не в системном приложении по умолчанию', settingsOpenToolsTitle: 'Внедрить инструмент открытия в боковой панели для модели', settingsOpenToolsDesc: 'Если включено, модель может открывать файлы, папки и HTTP(S)-страницы в боковой панели с помощью инструмента sidebar_open (по умолчанию выключено)', settingsTitleBarTitle: 'Режим совместимости заголовка', diff --git a/src/client/locales-sv.ts b/src/client/locales-sv.ts index 8c1262f..0bdc0d0 100644 --- a/src/client/locales-sv.ts +++ b/src/client/locales-sv.ts @@ -220,8 +220,6 @@ export const sv: Record = { settingsWidthTitle: 'Standardbreddandel', settingsWidthDesc: 'Sidokortets standardandel av fönsterbredden för nya konversationer (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: 'Öppna chattfiler i sidopanelen', - settingsOpenPathDesc: 'Öppna fillänkar i chatten (verktygsrader, producerade filer, omnämnanden) i sidopanelens editor i stället för systemets standardapp', settingsOpenToolsTitle: 'Injicera sidopanel-öppningsverktyg för modellen', settingsOpenToolsDesc: 'När aktiverat kan modellen öppna filer, mappar och HTTP(S)-sidor i sidopanelen via sidebar_open-verktyget (av som standard)', settingsTitleBarTitle: 'Positions kompatibilitetsläge', diff --git a/src/client/locales-th.ts b/src/client/locales-th.ts index fcdfa5d..094f961 100644 --- a/src/client/locales-th.ts +++ b/src/client/locales-th.ts @@ -237,8 +237,6 @@ export const th: Record = { settingsWidthTitle: 'สัดส่วนความกว้างเริ่มต้น', settingsWidthDesc: 'สัดส่วนความกว้างเริ่มต้นของการ์ดด้านข้างจากความกว้างหน้าต่างสำหรับแชทใหม่ (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: 'เปิดไฟล์แชทในแถบด้านข้าง', - settingsOpenPathDesc: 'เปิดลิงก์ไฟล์ในแชท (แถวเครื่องมือ, ไฟล์ที่สร้าง, การกล่าวถึง) ในตัวแก้ไขแถบด้านข้างแทนแอปเริ่มต้นของระบบ', settingsOpenToolsTitle: 'ฉีดเครื่องมือเปิดแถบด้านข้างสำหรับโมเดล', settingsOpenToolsDesc: 'เมื่อเปิดใช้ โมเดลสามารถเปิดไฟล์ โฟลเดอร์ และหน้า HTTP(S) ในแถบด้านข้างผ่านเครื่องมือ sidebar_open (ปิดเป็นค่าเริ่มต้น)', settingsTitleBarTitle: 'โหมดความเข้ากันได้ของตำแหน่ง', diff --git a/src/client/locales-tr.ts b/src/client/locales-tr.ts index f7d015e..df40c75 100644 --- a/src/client/locales-tr.ts +++ b/src/client/locales-tr.ts @@ -237,8 +237,6 @@ export const tr: Record = { settingsWidthTitle: 'Varsayılan genişlik payı', settingsWidthDesc: 'Yeni sohbetler için yan kartın pencere genişliğindeki varsayılan payı (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: 'Sohbet dosyalarını kenar çubuğunda aç', - settingsOpenPathDesc: 'Sohbetteki dosya bağlantılarını (araç satırları, üretilen dosyalar, anılmalar) sistem varsayılan uygulaması yerine kenar çubuğu düzenleyicisinde aç', settingsOpenToolsTitle: 'Model için kenar çubuğu açma aracı enjekte et', settingsOpenToolsDesc: 'Etkinleştirildiğinde model, sidebar_open aracıyla kenar çubuğunda dosyaları, klasörleri ve HTTP(S) sayfalarını açabilir (varsayılan kapalı)', settingsTitleBarTitle: 'Konum uyumluluk modu', diff --git a/src/client/locales-vi.ts b/src/client/locales-vi.ts index 11044a8..35ec66d 100644 --- a/src/client/locales-vi.ts +++ b/src/client/locales-vi.ts @@ -237,8 +237,6 @@ export const vi: Record = { settingsWidthTitle: 'Tỷ lệ chiều rộng mặc định', settingsWidthDesc: 'Tỷ lệ phần trăm chiều rộng cửa sổ mà thẻ bên chiếm cho phiên mới (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: 'Mở tệp chat trong thanh bên', - settingsOpenPathDesc: 'Khi nhấp link tệp trong chat (dòng công cụ, danh sách sản phẩm, nhắc tệp), mở trong trình soạn thảo thanh bên thay vì ứng dụng mặc định hệ thống', settingsOpenToolsTitle: 'Tiêm công cụ mở thanh bên cho mô hình', settingsOpenToolsDesc: 'Khi bật, mô hình có thể mở tệp, thư mục và trang HTTP(S) trong thanh bên qua công cụ sidebar_open (mặc định tắt)', settingsTitleBarTitle: 'Chế độ tương thích thanh tiêu đề', diff --git a/src/client/locales-zh-HK.ts b/src/client/locales-zh-HK.ts index b898c5a..248016f 100644 --- a/src/client/locales-zh-HK.ts +++ b/src/client/locales-zh-HK.ts @@ -252,8 +252,6 @@ export const zhHK: Record = { settingsWidthTitle: '預設寬度佔比', settingsWidthDesc: '新增工作階段時側邊卡片佔視窗寬度的百分比 (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: '聊天區檔案在側邊欄開啟', - settingsOpenPathDesc: '在聊天裡點擊檔案連結(工具行、產物列表、檔案提及)時,在側邊欄編輯器中開啟,不再呼叫系統預設應用程式', settingsOpenToolsTitle: '為模型注入側邊欄開啟工具', settingsOpenToolsDesc: '開啟後,模型可透過 sidebar_open 工具在側邊欄主動開啟檔案、資料夾和 HTTP(S) 網頁(預設關閉)', settingsTitleBarTitle: '位置相容模式', diff --git a/src/client/locales-zh-MO.ts b/src/client/locales-zh-MO.ts index 52d3e8d..d67b91c 100644 --- a/src/client/locales-zh-MO.ts +++ b/src/client/locales-zh-MO.ts @@ -252,8 +252,6 @@ export const zhMO: Record = { settingsWidthTitle: '預設寬度佔比', settingsWidthDesc: '新增工作階段時側邊卡片佔視窗寬度的百分比 (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: '聊天區檔案在側邊欄開啟', - settingsOpenPathDesc: '在聊天裡點擊檔案連結(工具行、產物列表、檔案提及)時,在側邊欄編輯器中開啟,不再呼叫系統預設應用程式', settingsOpenToolsTitle: '為模型注入側邊欄開啟工具', settingsOpenToolsDesc: '開啟後,模型可透過 sidebar_open 工具在側邊欄主動開啟檔案、資料夾和 HTTP(S) 網頁(預設關閉)', settingsTitleBarTitle: '位置相容模式', diff --git a/src/client/locales-zh-TW.ts b/src/client/locales-zh-TW.ts index 0601580..0fb0422 100644 --- a/src/client/locales-zh-TW.ts +++ b/src/client/locales-zh-TW.ts @@ -252,8 +252,6 @@ export const zhTW: Record = { settingsWidthTitle: '預設寬度佔比', settingsWidthDesc: '新增工作階段時側邊卡片佔視窗寬度的百分比 (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: '聊天區檔案在側邊欄開啟', - settingsOpenPathDesc: '在聊天裡點擊檔案連結(工具行、產物列表、檔案提及)時,在側邊欄編輯器中開啟,不再呼叫系統預設應用程式', settingsOpenToolsTitle: '為模型注入側邊欄開啟工具', settingsOpenToolsDesc: '開啟後,模型可透過 sidebar_open 工具在側邊欄主動開啟檔案、資料夾和 HTTP(S) 網頁(預設關閉)', settingsTitleBarTitle: '位置相容模式', diff --git a/src/client/locales.ts b/src/client/locales.ts index 2c0083e..ef3a2ee 100644 --- a/src/client/locales.ts +++ b/src/client/locales.ts @@ -249,8 +249,6 @@ export const zh = { settingsWidthTitle: '默认宽度占比', settingsWidthDesc: '新建会话时侧边卡片占窗口宽度的百分比 (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: '聊天区文件在侧边栏打开', - settingsOpenPathDesc: '在聊天里点击文件链接(工具行、产物列表、文件提及)时,在侧边栏编辑器中打开,不再调用系统默认应用', settingsOpenToolsTitle: '为模型注入侧边栏打开工具', settingsOpenToolsDesc: '开启后,模型可通过 sidebar_open 工具在侧边栏主动打开文件、文件夹和 HTTP(S) 网页(默认关闭)', settingsTitleBarTitle: '位置兼容模式', @@ -699,8 +697,6 @@ export const en: Record = { settingsWidthTitle: 'Default width share', settingsWidthDesc: 'The side card\'s default share of the window width for new conversations (20–60)', settingsWidthSuffix: '%', - settingsOpenPathTitle: 'Open chat files in the sidebar', - settingsOpenPathDesc: 'Open file links in the chat (tool rows, produced files, mentions) in the sidebar editor instead of the system default app', settingsOpenToolsTitle: 'Inject the sidebar-open tool for the model', settingsOpenToolsDesc: 'When enabled, the model can actively open files, folders, and HTTP(S) pages in the sidebar through the sidebar_open tool (off by default)', settingsTitleBarTitle: 'Position compatibility mode', diff --git a/src/client/openpath-intercept.ts b/src/client/openpath-intercept.ts deleted file mode 100644 index f87e05d..0000000 --- a/src/client/openpath-intercept.ts +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Interception of the chat's file-open funnel (DSH 0.1.2-alpha hosts). - * - * The alpha funnel is the Typert remote namespace `remote.session`: every - * chat-side file open (tool-row path links, the produced-files row, prose - * file mentions, inline-code paths) funnels through ChatView's injected - * `openFile`, which calls `ctx.remote.session.openWorkspacePath({ path })` - * with the path ALREADY resolved against the session cwd (verified against - * the DSH source: `packages/client/ui-chat/src/client/apply.ts` is the only - * production caller). The host side (`api/session-controller`) hands the - * path to the OS's default application (`openNativePath` → xdg-open on - * Linux). The pre-alpha funnel `ctx.workspaces.openPath` is gone — the - * alpha `IWorkspaces` has no openPath at all. - * - * `remote.session` is a cordis service (the gateway client's - * RemoteNamespaceService, key `remote.session`) whose methods are installed - * as ACCESSOR properties (`Object.defineProperty(service, method, { - * configurable: true, get })`) — the getter reads the method table at - * ACCESS time and returns the invocation closure. A plain assignment would - * not stick (no setter), so the wrapper shadows the method with a data - * property and restores the captured accessor descriptor on dispose. - * - * The namespace service appears asynchronously (the session-controller - * contribution mounts after the connection is up) and is disposed/recreated - * on contribution remounts; the caller therefore enters through - * `ctx.inject(['remote.session'], …)`, which re-fires on every remount. - * - * The wrapper is dependency-free by design (no React / ui-primitives), so - * the takeover logic is unit-testable and the file stays importable from - * the test runtime. - */ - -/** The one request shape the funnel method takes (mirror of the host's - * SessionOpenWorkspacePathRequest). */ -export interface OpenWorkspacePathRequest { - /** Absolute path (the chat caller already resolved it against the cwd). */ - path: string -} - -/** - * What the funnel method resolves to (mirror of the typert `RemoteResult` - * envelope): EVERY remote method folds its business value into - * `{ ok: true, value }` / `{ ok: false, error }` — the chat caller branches - * on `result.ok`, so a bare business value would be misread as a failure - * (`result.error.message` reads `message` off undefined). - */ -export type OpenWorkspacePathResult = - | { readonly ok: true; readonly value: { opened: boolean } } - | { readonly ok: false; readonly error: { readonly code: string; readonly message: string; readonly details: object } } - -/** The namespace slice the wrapper shadows (mirror of the gateway client's - * RemoteNamespaceService for the `session` namespace). */ -export interface OpenWorkspacePathService { - openWorkspacePath(request: OpenWorkspacePathRequest, signal?: AbortSignal): Promise -} - -/** Per-call decisions the wrapper needs (wired to the store + ctx in the client half). */ -export interface OpenPathInterceptDeps { - /** - * Whether to take over this call: the `interceptOpenPath` pref AND the - * editor tab's own enable switch must both be on (an editor that cannot - * open must not swallow opens — they fall through to the Host). - */ - takeoverEnabled(): boolean - /** The session whose scope the sidebar editor loads the file in (current session). */ - currentSessionId(): string | undefined - /** Route the open into the sidebar editor (the established openSidebarFile). */ - openInSidebar(path: string, sessionId: string): void - /** Route a folder-reveal gesture ("Show in folder" passes '.') into the sidebar explorer. */ - revealInExplorer(path: string, sessionId: string): void -} - -/** - * Whether a path is the "Show in folder" folder-reveal gesture. The stock - * ui-deliverables row passes `'.'` (the session workspace root, resolved by - * the chat view to `"/."`); any path whose final segment is `.` is the - * same gesture. A directory has no editor content, so these opens must reach - * the explorer instead of an editor tab. - */ -export function isFolderRevealPath(path: string): boolean { - if (path === '.' || path === './') return true - const trimmed = path.replace(/[\\/]+$/, '') - return trimmed === '.' || /[\\/]\.$/.test(trimmed) -} - -/** - * Shadow `remote.session.openWorkspacePath`: intercepted calls open the file - * in the sidebar editor and resolve with the remote SUCCESS ENVELOPE - * (`{ ok: true, value: { opened: true } }`, so ChatView shows no error - * dialog); anything that declines falls through to the captured original - * closure (the host OS's default application) untouched. - * The one exception is the folder-reveal gesture, which is routed to - * {@link OpenPathInterceptDeps.revealInExplorer} instead. - * - * The original closure is captured by ACCESSING the accessor once at wrap - * time — it invokes whatever method records are mounted at that moment. If - * the contribution remounts its methods while the shadow is installed, the - * captured closure points at the old records; the session namespace is - * effectively permanent in practice, so this is accepted and the shadow is - * re-applied anyway when the remount recreates the service (the caller's - * `ctx.inject` re-fires). - * - * @param service - the `remote.session` namespace service. - * @param deps - per-call takeover decisions. - * @returns the disposer restoring the original accessor descriptor (HMR-safe). - */ -export function wrapOpenWorkspacePath( - service: OpenWorkspacePathService, - deps: OpenPathInterceptDeps, -): () => void { - const KEY = 'openWorkspacePath' - const target = service as object - const descriptor = Object.getOwnPropertyDescriptor(target, KEY) - // Invoke the accessor once to capture the CURRENT invocation closure. - const original = service.openWorkspacePath - // The gateway installs the namespace's whole method group in the same - // synchronous window as the service registration, so an inject-observed - // service always has the method — but a hand-rolled composition might not; - // wrapping nothing would turn fall-throughs into crashes, so decline. - if (typeof original !== 'function') return () => {} - const wrapped = (request: OpenWorkspacePathRequest, signal?: AbortSignal): Promise => { - if (deps.takeoverEnabled()) { - const sessionId = deps.currentSessionId() - if (sessionId !== undefined) { - if (isFolderRevealPath(request.path)) deps.revealInExplorer(request.path, sessionId) - else deps.openInSidebar(request.path, sessionId) - return Promise.resolve({ ok: true, value: { opened: true } }) - } - } - return original.call(service, request, signal) - } - Object.defineProperty(target, KEY, { - configurable: true, - enumerable: true, - writable: true, - value: wrapped, - }) - return () => { - // Restore the exact original property (the gateway's accessor) so a - // chain of wrappers keeps working across disposals in any order. - if (descriptor !== undefined) Object.defineProperty(target, KEY, descriptor) - else Reflect.deleteProperty(target, KEY) - } -} diff --git a/src/client/prefs.ts b/src/client/prefs.ts index 082523a..5eae009 100644 --- a/src/client/prefs.ts +++ b/src/client/prefs.ts @@ -76,9 +76,6 @@ export function parsePrefs(value: unknown): SidebarPrefs { terminalFontSize: typeof record.terminalFontSize === 'number' && Number.isFinite(record.terminalFontSize) ? clampTerminalFontSize(record.terminalFontSize) : SIDEBAR_PREFS_DEFAULTS.terminalFontSize, - interceptOpenPath: typeof record.interceptOpenPath === 'boolean' - ? record.interceptOpenPath - : SIDEBAR_PREFS_DEFAULTS.interceptOpenPath, editorExplorer: typeof record.editorExplorer === 'boolean' ? record.editorExplorer : SIDEBAR_PREFS_DEFAULTS.editorExplorer, diff --git a/src/client/service.ts b/src/client/service.ts index 8897e7d..140c5c2 100644 --- a/src/client/service.ts +++ b/src/client/service.ts @@ -465,7 +465,7 @@ export function matchUrlTarget(tabs: readonly TabDescriptor[], url: URL): TabDes * The plugin version this service instance reports. Keep in lockstep with * `package.json`'s version — `tests/service.spec.ts` asserts the pair. */ -export const SIDEBAR_SERVICE_VERSION = '0.18.1' +export const SIDEBAR_SERVICE_VERSION = '0.19.0-alpha.0' /** * Monotonic capability list consumers use to gate new API usage (features diff --git a/src/client/sidechat-transcript.ts b/src/client/sidechat-transcript.ts index 0c9e400..56f5ae7 100644 --- a/src/client/sidechat-transcript.ts +++ b/src/client/sidechat-transcript.ts @@ -13,9 +13,10 @@ * shows only the thread's own conversation. * * Live streaming: `assistant/message` events only land when a step - * completes, but `assistant/chunk` events stream token-level text and - * reasoning deltas. The mapping accumulates both per block and supersedes - * them with the assembled message once it lands (settled rows). + * completes, but the host's `assistant/live-chunk` rows stream token-level + * text and reasoning deltas (DSH 0.1.5 publishes them outside the session + * log — see assistant-live.ts). The mapping accumulates both per block and + * supersedes them with the assembled message once it lands (settled rows). */ import type { DiffHunk, ReadBlockLine } from '@deepseek-ai/dsh-client-ui-primitives' import type { SidebarHistoryEntry } from '../context-types.ts' @@ -299,12 +300,13 @@ function resultCard( /** * Map a thread child's history rows onto compact transcript rows: the * inherited fork seed is cut at the last `session/end-seed`, context - * injections map onto a collapsible injection row, `assistant/chunk` + * injections map onto a collapsible injection row, `assistant/live-chunk` * deltas accumulate into streaming rows per (turn, step, block) and are * superseded by the assembled `assistant/message`, and tool invocations * render one expandable line each (arguments, paired result text, failure * marker; a still-executing call is marked until its result lands). - * @param entries - history rows (event + host-computed view) in seq order. + * @param entries - history rows (event + host-computed view) in seq order, + * the live deltas of the in-flight attempt appended after the durable tail. * @returns display rows in log order. */ export function transcriptRows(entries: readonly SidebarHistoryEntry[], prev?: readonly SidechatTranscriptRow[]): SidechatTranscriptRow[] { @@ -374,7 +376,7 @@ export function transcriptRows(entries: readonly SidebarHistoryEntry[], prev?: r rows.push({ kind: 'user', seq: event.seq, text }) break } - case 'assistant/chunk': { + case 'assistant/live-chunk': { const chunk = data.chunk as { type?: unknown; text?: unknown } | undefined if (chunk === null || typeof chunk !== 'object') break const kind = chunk.type === 'text-delta' ? 'assistant' : chunk.type === 'reasoning-delta' ? 'reasoning' : null diff --git a/src/config.ts b/src/config.ts index 3d055cb..48c3c02 100644 --- a/src/config.ts +++ b/src/config.ts @@ -124,7 +124,6 @@ export const PrefsSchema: z = z.object({ bottomPanelAutoTerminal: z.boolean().default(true), terminalFontFamily: z.string().default(''), terminalFontSize: z.number().step(1).min(TERMINAL_FONT_SIZE_MIN).max(TERMINAL_FONT_SIZE_MAX).default(TERMINAL_FONT_SIZE_DEFAULT), - interceptOpenPath: z.boolean().default(true), editorExplorer: z.boolean().default(false), changesDiffFloat: z.boolean().default(true), workspaceFence: z.boolean().default(true), diff --git a/src/context-types.ts b/src/context-types.ts index 3730749..9f97b7d 100644 --- a/src/context-types.ts +++ b/src/context-types.ts @@ -296,14 +296,34 @@ export interface SidebarSessionTitleService { rename(session: unknown, title: string): { title: string; eventSeq: number } } -/** The host session-persistence face (mirror of the sessionPersistence - * service): detached inspection of a persisted session, used to compose the - * recorded preset when a Side Chat thread cold-resumes. */ +/** + * The host session-persistence face (mirror of the `sessionPersistence` + * service): durable, handle-addressed session storage. + * + * DSH 0.1.5 replaced the detached `inspect(id)` call with an explicit read + * handle: `open(id, 'read')` never takes write ownership and works while + * another process owns the session, `handle.read()` returns one contiguous + * slice of the log, and `close()` releases it. Every cold read in this plugin + * goes through {@link readPersistedSession} so the handle is always closed. + */ export interface SidebarSessionPersistenceService { - inspect(sessionId: string): Promise<{ - meta: { cwd?: string; agentPreset?: string } - events: readonly SidebarSessionEvent[] - }> + open(sessionId: string, access: 'read' | 'write'): Promise +} + +/** One open channel onto a stored session (the fields this plugin reads). */ +export interface SidebarSessionHandle { + /** Immutable stored header (cwd / agentPreset live here). */ + readonly header: { cwd?: string; agentPreset?: string } & Record + /** Exact fork-inherited prefix length stored with the log. */ + readonly inheritedEventCount?: number + /** + * Read a slice of the valid contiguous log. + * @param offset - first logical seq to include (defaults to 0). + * @param length - maximum events (defaults to the rest of the log). + */ + read(offset?: number, length?: number): Promise<{ events: readonly SidebarSessionEvent[] }> + /** Release the handle (idempotent). */ + close(): Promise } /** The client session list snapshot the sidebar subscribes to. */ @@ -408,30 +428,6 @@ export interface SidebarConversation { } } -/** - * The client `remote.session` namespace face (mirror of the gateway client's - * RemoteNamespaceService for the session-controller contribution on alpha - * hosts). The chat's file-open funnel is `openWorkspacePath`: the caller - * resolves the path against the session cwd, and the host hands it to the - * OS's default application. Namespace methods are accessor properties (see - * `client/openpath-intercept.ts` for how the interception shadows them). - */ -export interface SidebarRemoteSessionService { - /** - * Open an absolute path with the Host operating system's default - * application. Resolves with the typert `RemoteResult` envelope - * (`{ ok: true, value: { opened } }` / `{ ok: false, error }`), like every - * remote method — callers branch on `result.ok`. - */ - openWorkspacePath( - request: { path: string }, - signal?: AbortSignal, - ): Promise< - | { readonly ok: true; readonly value: { opened: boolean } } - | { readonly ok: false; readonly error: { readonly code: string; readonly message: string; readonly details: object } } - > -} - /** * The invariant service face (mirror of @deepseek-ai/dsh-invariants' * InvariantRegistry). The upstream augmentation does not reach this Context @@ -564,6 +560,13 @@ export interface SidebarContextShape { * LIVE Session instance that appended it. */ on(event: string, listener: (session: unknown, event: SidebarSessionEvent) => void): () => void + /** + * The agent's process-local assistant stream (DSH 0.1.5+): one payload per + * `start` / `chunk` / `end` frame, carrying the emitting agent and the + * frame. These frames are NOT session events — see + * {@link ./assistant-live.ts} for why the plugin needs them. + */ + on(event: 'agent/assistant-stream', listener: (payload: { agent?: unknown; frame?: unknown }) => void): () => void } /** diff --git a/src/index.ts b/src/index.ts index 4638174..8eba7c3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -54,7 +54,9 @@ import { AgentOpenRegistry, registerOpenTool, type AgentOpenRequest } from './ag import { buildJobsApi, type SidebarJobsRoutes } from './jobs-routes.ts' import { buildSubagentLiveApi, type SidebarSubagentLiveRoutes } from './subagent-live-route.ts' import { buildSidechatApi } from './sidechat-routes.ts' +import { createAssistantLiveBuffer, type AssistantLiveBuffer } from './assistant-live.ts' import { readJsonBody, requireString, SidebarError, writeError, writeJson, writeOk } from './wire.ts' +import { readPersistedSession } from './session-store.ts' export { Config } export type { SidebarConfig, ResolvedSidebarConfig } @@ -129,8 +131,8 @@ async function sessionCwdOf(ctx: Context, sessionId: string, clientCwd?: string) } const persistence = ctx.get('sessionPersistence') if (persistence !== undefined) { - const inspected = await persistence.inspect(sessionId) - const metaCwd = inspected.meta.cwd + const persisted = await readPersistedSession(persistence, sessionId) + const metaCwd = persisted.header.cwd if (metaCwd !== undefined && metaCwd !== '') { try { return requireAbsolute(metaCwd) @@ -298,6 +300,7 @@ function buildApi( resolved: ResolvedSidebarConfig, terminalShell: string, getSettings: () => SidebarSettingsFace | undefined, + assistantLive: AssistantLiveBuffer, ): Record { const cwdOf = async (payload: unknown): Promise<{ sessionId: string; cwd: string }> => { const sessionId = requireString(payload, 'sessionId') @@ -507,7 +510,7 @@ function buildApi( const persistence = ctx.get('sessionPersistence') if (persistence !== undefined) { try { - events = (await persistence.inspect(sessionId)).events + events = (await readPersistedSession(persistence, sessionId)).events } catch { // Cold read unavailable (session never persisted): an empty // window is the honest answer, not a wire error. @@ -694,7 +697,7 @@ function buildApi( // identities are fenced from the generic session RPCs (agent-lookup // ownership), and the thread is created with a CUSTOM seed the stock // fork APIs cannot express. - ...buildSidechatApi(ctx), + ...buildSidechatApi(ctx, assistantLive), } } @@ -859,7 +862,14 @@ export function apply(ctx: Context, config?: SidebarConfig): void { }) // ── JSON API ──────────────────────────────────────────────────────────── - const api = buildApi(ctx, ptyManager, agentPtyRegistry, resolved, terminalShell, () => settingsFace) + // The live assistant stream buffer: DSH 0.1.5 publishes in-flight model + // deltas as process-local `agent/assistant-stream` frames instead of the + // durable `assistant/chunk` events 0.1.2 logged, so the side-chat + // transcript and the inherited in-progress snapshot read them here. The + // effect releases the listener on fiber disposal. + const assistantLive = createAssistantLiveBuffer(ctx) + ctx.effect(() => () => { assistantLive.dispose() }, 'dsh-better-sidebar: live assistant stream buffer') + const api = buildApi(ctx, ptyManager, agentPtyRegistry, resolved, terminalShell, () => settingsFace, assistantLive) ctx.effect(() => ctx.webServer.register({ kind: 'prefix', path: '/sidebar/api', diff --git a/src/prefs-shared.ts b/src/prefs-shared.ts index 9bc87fe..e8b2494 100644 --- a/src/prefs-shared.ts +++ b/src/prefs-shared.ts @@ -63,14 +63,6 @@ export interface SidebarPrefs { * row in the Side card settings. */ bottomPanelAutoTerminal: boolean - /** - * Whether chat-side file opens (tool-row path links, the produced-files - * row, prose file mentions — every path that funnels through the client - * runtime's `remote.session.openWorkspacePath`) open in the sidebar editor - * instead of the Host OS's default application. On by default; the editor - * tab's own enable switch gates it too (both must be on for the takeover). - */ - interceptOpenPath: boolean /** * Whether the editor tab runs in merged mode: a path input replaces the * plain header and a toggleable file-tree panel (with a global name @@ -276,7 +268,6 @@ export const SIDEBAR_PREFS_DEFAULTS: SidebarPrefs = { bottomPanelAutoTerminal: true, terminalFontFamily: '', terminalFontSize: TERMINAL_FONT_SIZE_DEFAULT, - interceptOpenPath: true, editorExplorer: false, changesDiffFloat: true, workspaceFence: true, diff --git a/src/session-store.ts b/src/session-store.ts new file mode 100644 index 0000000..1494b96 --- /dev/null +++ b/src/session-store.ts @@ -0,0 +1,65 @@ +/** + * Cold-session reads over the host `sessionPersistence` service. + * + * DSH 0.1.5 removed the detached `inspect(id)` call: a persisted session is + * read through an explicit handle — `open(id, 'read')` never takes write + * ownership (so it works while the session is live in another process), + * `handle.read()` returns one contiguous slice of the log, and `close()` + * releases it. Every cold read in this plugin goes through + * {@link readPersistedSession} so the handle is always released, even when + * the read throws. + */ +import type { Context, SidebarSessionEvent, SidebarSessionPersistenceService } from './context-types.ts' + +/** One persisted session as this plugin reads it. */ +export interface PersistedSession { + /** Immutable stored header (cwd / agentPreset live here). */ + readonly header: { cwd?: string; agentPreset?: string } & Record + /** The full stored event log. */ + readonly events: readonly SidebarSessionEvent[] + /** Exact fork-inherited prefix length (0 when the header is not seeded). */ + readonly inheritedEventCount: number +} + +/** + * Read one persisted session's header and full event log. + * @param persistence - the live `sessionPersistence` service. + * @param sessionId - the stored session to read. + * @returns the header and log; rejects when the session does not exist. + */ +export async function readPersistedSession( + persistence: SidebarSessionPersistenceService, + sessionId: string, +): Promise { + const handle = await persistence.open(sessionId, 'read') + try { + const { events } = await handle.read() + return { + header: handle.header, + events, + inheritedEventCount: handle.inheritedEventCount ?? 0, + } + } finally { + await handle.close() + } +} + +/** + * Resolve the service and read one persisted session, or `undefined` when + * the service is absent or the session cannot be read. + * @param ctx - host plugin context. + * @param sessionId - the stored session to read. + * @returns the persisted session, or `undefined`. + */ +export async function readPersistedSessionOf( + ctx: Context, + sessionId: string, +): Promise { + const persistence = ctx.get('sessionPersistence') + if (persistence === undefined) return undefined + try { + return await readPersistedSession(persistence, sessionId) + } catch { + return undefined + } +} diff --git a/src/sidechat-core.ts b/src/sidechat-core.ts index af18208..67f108d 100644 --- a/src/sidechat-core.ts +++ b/src/sidechat-core.ts @@ -17,6 +17,7 @@ * snapshot inside the boundary prompt. */ import type { SidebarHistoryEntry, SidebarSessionSummary } from './context-types.ts' +import type { AssistantLiveChunk } from './assistant-live.ts' /** The durable thread-label prefix (also the row filter in the client list). */ export const SIDE_LABEL_PREFIX = 'Side: ' @@ -81,6 +82,55 @@ export interface SidechatLogEvent { data: unknown } +/** + * One live assistant delta on the plugin's wire, mirroring DSH 0.1.5's + * client-only `assistant/live-chunk` presentation row. + * + * DSH 0.1.5 no longer logs `assistant/chunk`: an in-flight attempt's deltas + * are process-local frames (`agent/assistant-stream`, folded by + * {@link ./assistant-live.ts}) and reach the transcript through the plugin's + * own `sidechat.events` route as these rows. They are NOT durable — the + * route returns the current attempt's rows on every poll and the client + * replaces its live set each time; the durable `assistant/message` settles + * them by `turn:step`. + */ +export interface SidechatLiveEvent { + type: 'assistant/live-chunk' + /** Ordering key among live rows only; durable seqs stay authoritative. */ + seq: number + time: number + data: { + attemptId: string + turn: number + step: number + /** Dense zero-based position within the attempt. */ + index: number + /** The raw model stream chunk. */ + chunk: Record + } +} + +/** + * Project buffered live chunks into wire rows. + * @param chunks - the session's active-attempt chunks, in index order. + * @param tailSeq - the session's last durable seq (live rows order after it). + * @returns the rows to append to the transcript feed. + */ +export function liveEventsOf(chunks: readonly AssistantLiveChunk[], tailSeq: number): SidechatLiveEvent[] { + return chunks.map((delta, position) => ({ + type: 'assistant/live-chunk', + seq: tailSeq + 1 + position, + time: delta.time, + data: { + attemptId: delta.attemptId, + turn: delta.turn, + step: delta.step, + index: delta.index, + chunk: delta.chunk, + }, + })) +} + /** The result of cutting a parent log into a side-thread inheritance. */ export interface SidechatInheritance { /** The child seed: contiguous from seq 0, ends outside any open turn. */ @@ -210,8 +260,14 @@ const SNAPSHOT_TOTAL_CAP = 8000 /** * Build the side-thread inheritance for one parent log: the full event log * up to the click moment, honestly closed when it ends inside an open turn. + * @param events - the parent's log (live or persisted). + * @param live - the parent's in-flight stream chunks (DSH 0.1.5+ publishes + * them outside the log); used only by the snapshot fallback. */ -export function buildSidechatInheritance(events: readonly SidechatLogEvent[]): SidechatInheritance { +export function buildSidechatInheritance( + events: readonly SidechatLogEvent[], + live: readonly AssistantLiveChunk[] = [], +): SidechatInheritance { if (events.length === 0) return { seed: [], snapshot: null } const boundary = lastTurnBoundary(events) if (boundary < 0 || events[boundary]?.type === 'turn/end') { @@ -225,7 +281,7 @@ export function buildSidechatInheritance(events: readonly SidechatLogEvent[]): S // the structured snapshot to the boundary prompt instead. return { seed: copyEvents(events.slice(0, boundary)), - snapshot: buildOpenTurnSnapshot(events), + snapshot: buildOpenTurnSnapshot(events, live), } } const seed = copyEvents(events) @@ -245,16 +301,79 @@ export function buildSidechatInheritance(events: readonly SidechatLogEvent[]): S return { seed, snapshot: null } } +/** One assistant content block reduced to the text the snapshot shows. */ +function messageTexts(message: unknown): { text: string; reasoning: string } { + const content = (message as { content?: unknown } | undefined)?.content + let text = '' + let reasoning = '' + if (!Array.isArray(content)) return { text, reasoning } + for (const block of content) { + if (block === null || typeof block !== 'object') continue + const candidate = block as { type?: unknown; text?: unknown } + if (typeof candidate.text !== 'string' || candidate.text === '') continue + if (candidate.type === 'text') text += candidate.text + else if (candidate.type === 'reasoning') reasoning += candidate.text + } + return { text, reasoning } +} + +/** + * Expand one attempt's compact `AssistantStreamRecord[]` (the durable stream + * DSH 0.1.5 embeds in `assistant/message.stream` and `assistant/attempt.stream`) + * into the text/reasoning it carried. Tool-call records contribute no text. + */ +function streamTexts(stream: unknown): { text: string; reasoning: string } { + let text = '' + let reasoning = '' + if (!Array.isArray(stream)) return { text, reasoning } + for (const record of stream) { + if (record === null || typeof record !== 'object') continue + const entry = record as { type?: unknown; texts?: unknown; chunk?: unknown } + if (entry.type === 'text-chunks' || entry.type === 'reasoning-chunks') { + if (!Array.isArray(entry.texts)) continue + const joined = entry.texts.filter((part): part is string => typeof part === 'string').join('') + if (entry.type === 'text-chunks') text += joined + else reasoning += joined + continue + } + if (entry.type === 'chunk') { + const chunk = entry.chunk as { type?: unknown; text?: unknown } | undefined + if (chunk === null || typeof chunk !== 'object' || typeof chunk.text !== 'string') continue + if (chunk.type === 'text-delta') text += chunk.text + else if (chunk.type === 'reasoning-delta') reasoning += chunk.text + } + } + return { text, reasoning } +} + +/** One live delta's contribution to the snapshot. */ +function liveTexts(chunk: Record): { text: string; reasoning: string } { + if (typeof chunk.text !== 'string' || chunk.text === '') return { text: '', reasoning: '' } + if (chunk.type === 'text-delta') return { text: chunk.text, reasoning: '' } + if (chunk.type === 'reasoning-delta') return { text: '', reasoning: chunk.text } + return { text: '', reasoning: '' } +} + /** * Structured text snapshot of the parent's OPEN turn (from its `turn/start` - * to the log tail): the accumulated assistant/reasoning output verbatim - * (code blocks ride the raw deltas) and the tool activity — executed tools - * with their result text, the still-executing one marked. Returns null when - * there is no open turn or nothing to show. + * to the log tail): the assistant/reasoning output so far and the tool + * activity — executed tools with their result text, the still-executing one + * marked. Returns null when there is no open turn or nothing to show. + * + * The in-flight step's text is NOT in the log on DSH 0.1.5 (the model stream + * is process-local until it settles), so it comes from `live`; settled steps + * read their durable `assistant/message` content, and a failed attempt reads + * its embedded `assistant/attempt.stream`. + * @param events - the parent's log. + * @param live - the parent's in-flight stream chunks, in index order. */ -export function buildOpenTurnSnapshot(events: readonly SidechatLogEvent[]): string | null { +export function buildOpenTurnSnapshot( + events: readonly SidechatLogEvent[], + live: readonly AssistantLiveChunk[] = [], +): string | null { const boundary = lastTurnBoundary(events) if (boundary < 0 || events[boundary]?.type !== 'turn/start') return null + const openTurn = numberAt(dataOf(events[boundary]!), 'turn') let text = '' let reasoning = '' const tools: string[] = [] @@ -267,11 +386,16 @@ export function buildOpenTurnSnapshot(events: readonly SidechatLogEvent[]): stri pendingCalls.clear() continue } - if (event.type === 'assistant/chunk') { - const chunk = data.chunk as { type?: unknown; text?: unknown } | undefined - if (chunk === null || typeof chunk !== 'object') continue - if (chunk.type === 'text-delta' && typeof chunk.text === 'string') text += chunk.text - else if (chunk.type === 'reasoning-delta' && typeof chunk.text === 'string') reasoning += chunk.text + if (event.type === 'assistant/message') { + const settled = messageTexts(data.message) + text += settled.text + reasoning += settled.reasoning + continue + } + if (event.type === 'assistant/attempt') { + const attempt = streamTexts(data.stream) + text += attempt.text + reasoning += attempt.reasoning continue } if (event.type === 'tool/call') { @@ -304,6 +428,13 @@ export function buildOpenTurnSnapshot(events: readonly SidechatLogEvent[]): stri const line = `- \`${call.name}\` (executing) — arguments: \`${call.args}\`` tools.push(line) } + // The in-flight step: its deltas never reached the log. + for (const delta of live) { + if (delta.turn !== openTurn) continue + const contribution = liveTexts(delta.chunk) + text += contribution.text + reasoning += contribution.reasoning + } const sections: string[] = [] if (text.trim() !== '') sections.push(`Assistant output so far:\n\n${text}`) if (reasoning.trim() !== '') sections.push(`Reasoning so far:\n\n${reasoning}`) diff --git a/src/sidechat-routes.ts b/src/sidechat-routes.ts index a0252b3..9d60df4 100644 --- a/src/sidechat-routes.ts +++ b/src/sidechat-routes.ts @@ -37,17 +37,21 @@ import type { import { boundaryDelivered, buildSidechatInheritance, + liveEventsOf, resolvePresetId, SIDE_BOUNDARY_PROMPT, SIDE_INJECTION_PLUGIN, SIDE_NEW_THREAD_TITLE, sideLabel, type SeedEvent, + type SidechatLiveEvent, type SidechatLogEvent, type SidechatThreadInfo, threadOwnLogEvents, } from './sidechat-core.ts' +import type { AssistantLiveBuffer } from './assistant-live.ts' import { requireString, SidebarError } from './wire.ts' +import { readPersistedSession } from './session-store.ts' /** The six Side Chat routes of the sidebar API (wire method names). */ export interface SidechatRoutes { @@ -66,8 +70,10 @@ export interface SidechatRoutes { 'sidechat.info'(payload: unknown): Promise /** The thread's OWN transcript events, seed-cut host-side (the inherited * parent log never crosses the wire); `afterSeq` narrows the response to - * the delta beyond it (poll tail). */ - 'sidechat.events'(payload: unknown): Promise<{ events: SidechatLogEvent[] }> + * the delta beyond it (poll tail). `live` carries the thread's in-flight + * model deltas, which DSH 0.1.5 publishes outside the session log — it is + * the CURRENT attempt's rows on every poll, never a delta. */ + 'sidechat.events'(payload: unknown): Promise<{ events: SidechatLogEvent[]; live: SidechatLiveEvent[] }> } /** Timeout guarding the create call (the registry detaches it before the @@ -117,8 +123,8 @@ async function composePersistedSetup( if (persistence === undefined) { return () => Promise.resolve() } - const inspected = await persistence.inspect(childId) - const presetId = resolvePresetId(inspected.meta, inspected.events) + const inspected = await readPersistedSession(persistence, childId) + const presetId = resolvePresetId(inspected.header, inspected.events) const presets = ctx.get('agentPresets') as SidebarAgentPresetsService | undefined if (presets === undefined || presetId === undefined) { return () => Promise.resolve() @@ -186,7 +192,7 @@ async function threadLogEvents(ctx: Context, childId: string): Promise { const sessionId = requireString(payload, 'sessionId') @@ -213,6 +222,7 @@ export function buildSidechatApi(ctx: Context): SidechatRoutes { const parentSession = parent.session const inheritance = buildSidechatInheritance( parentSession.snapshotEvents() as unknown as readonly SidechatLogEvent[], + live?.chunksFor(sessionId) ?? [], ) const { agentPreset, setup } = await composeChildSetup( ctx, @@ -388,8 +398,8 @@ export function buildSidechatApi(ctx: Context): SidechatRoutes { const persistence = ctx.get('sessionPersistence') as SidebarSessionPersistenceService | undefined if (persistence !== undefined) { try { - const inspected = await persistence.inspect(childId) - const preset = resolvePresetId(inspected.meta, inspected.events) + const inspected = await readPersistedSession(persistence, childId) + const preset = resolvePresetId(inspected.header, inspected.events) return { live: false, ...(preset === undefined ? {} : { preset }) } } catch { // Unknown/gone session: report a bare cold info. @@ -408,7 +418,13 @@ export function buildSidechatApi(ctx: Context): SidechatRoutes { const events = await threadLogEvents(ctx, childId) const own = threadOwnLogEvents(events) const fresh = rawAfter === undefined ? own : own.filter(event => event.seq > rawAfter) - return { events: fresh.length > EVENTS_CAP ? fresh.slice(fresh.length - EVENTS_CAP) : fresh } + const tailSeq = own.at(-1)?.seq ?? -1 + return { + events: fresh.length > EVENTS_CAP ? fresh.slice(fresh.length - EVENTS_CAP) : fresh, + // The live rows ride every poll: they are process-local, so there is + // no durable seq to page them by. An idle thread has none. + live: liveEventsOf(live?.chunksFor(childId) ?? [], tailSeq), + } }, } } diff --git a/src/subagent-activity.ts b/src/subagent-activity.ts index 6a5d582..d44d07d 100644 --- a/src/subagent-activity.ts +++ b/src/subagent-activity.ts @@ -39,9 +39,11 @@ export interface LastActivity { /** * Fold a session event log into the last text output + last tool call (each - * is the LAST occurrence in event order). Lifecycle events and raw - * `assistant/chunk` rows are ignored — the card shows what the subagent is - * doing right now, not its plumbing. The scan runs BACKWARD from the newest + * is the LAST occurrence in event order). Lifecycle events and raw stream + * rows are ignored — the card shows what the subagent is doing right now, + * not its plumbing. (DSH 0.1.5 publishes the raw deltas as process-local + * frames instead of log events, so only the assembled `assistant/message` + * reaches this scan.) The scan runs BACKWARD from the newest * event and stops once both fields are found, so a long history costs only * the recent tail in the common case. * @param events - the session's append-only event log (oldest → newest). diff --git a/tests/assistant-live.spec.ts b/tests/assistant-live.spec.ts new file mode 100644 index 0000000..581ea81 --- /dev/null +++ b/tests/assistant-live.spec.ts @@ -0,0 +1,132 @@ +/** + * Unit tests for the live assistant stream buffer (src/assistant-live.ts): + * the fold that replaces DSH 0.1.2's durable `assistant/chunk` events with + * the process-local `agent/assistant-stream` frames 0.1.5 publishes instead. + */ +import { describe, expect, it, vi } from 'vitest' +import { createAssistantLiveBuffer, LIVE_CHUNK_CAP, LIVE_SESSION_CAP } from '../src/assistant-live.ts' +import type { Context } from '../src/context-types.ts' + +/** A context whose `on` records the stream listener the buffer installs. */ +function harness(): { + ctx: Context + emit: (payload: unknown) => void + off: ReturnType +} { + let listener: ((payload: unknown) => void) | undefined + const off = vi.fn() + const ctx = { + on: (event: string, fn: (payload: unknown) => void) => { + expect(event).toBe('agent/assistant-stream') + listener = fn + return off + }, + } as unknown as Context + return { ctx, emit: payload => { listener?.(payload) }, off } +} + +/** One frame as DSH emits it, for the session named by `sessionId`. */ +function frame(sessionId: string, frame: Record): Record { + return { agent: { session: { id: sessionId } }, frame } +} + +describe('createAssistantLiveBuffer', () => { + it('folds start + chunk frames into ordered per-session chunks', () => { + const { ctx, emit } = harness() + const live = createAssistantLiveBuffer(ctx) + emit(frame('s1', { type: 'start', attemptId: 'a1', revision: 1, turn: 2, step: 1 })) + emit(frame('s1', { type: 'chunk', attemptId: 'a1', revision: 1, index: 0, time: 10, chunk: { type: 'text-delta', index: 0, text: 'he' } })) + emit(frame('s1', { type: 'chunk', attemptId: 'a1', revision: 1, index: 1, time: 11, chunk: { type: 'reasoning-delta', index: 1, text: 'hmm' } })) + const chunks = live.chunksFor('s1') + expect(chunks.map(chunk => [chunk.turn, chunk.step, chunk.index, chunk.time])).toEqual([ + [2, 1, 0, 10], + [2, 1, 1, 11], + ]) + expect(chunks[1]?.chunk).toEqual({ type: 'reasoning-delta', index: 1, text: 'hmm' }) + expect(live.chunksFor('other')).toEqual([]) + }) + + it('ignores payloads it cannot identify', () => { + const { ctx, emit } = harness() + const live = createAssistantLiveBuffer(ctx) + emit(undefined) + emit({}) + emit({ agent: {}, frame: { type: 'chunk', attemptId: 'a1', index: 0, chunk: {} } }) + emit({ agent: { session: { id: 's1' } }, frame: { type: 'chunk', attemptId: 'a1', index: 0, chunk: {} } }) + // A chunk with no start is not an attempt this buffer knows. + expect(live.chunksFor('s1')).toEqual([]) + emit(frame('s1', { type: 'start', attemptId: 'a1', turn: 1, step: 1 })) + emit(frame('s1', { type: 'chunk', attemptId: 'a1', index: 0, chunk: 'not-a-record' })) + expect(live.chunksFor('s1')).toEqual([]) + }) + + it('drops the attempt on a gap rather than splicing incomplete text', () => { + const { ctx, emit } = harness() + const live = createAssistantLiveBuffer(ctx) + emit(frame('s1', { type: 'start', attemptId: 'a1', turn: 1, step: 1 })) + emit(frame('s1', { type: 'chunk', attemptId: 'a1', index: 0, chunk: { type: 'text-delta', text: 'a' } })) + emit(frame('s1', { type: 'chunk', attemptId: 'a1', index: 2, chunk: { type: 'text-delta', text: 'c' } })) + expect(live.chunksFor('s1')).toEqual([]) + }) + + it('drops the attempt when a chunk names another attempt', () => { + const { ctx, emit } = harness() + const live = createAssistantLiveBuffer(ctx) + emit(frame('s1', { type: 'start', attemptId: 'a1', turn: 1, step: 1 })) + emit(frame('s1', { type: 'chunk', attemptId: 'a2', index: 0, chunk: { type: 'text-delta', text: 'a' } })) + expect(live.chunksFor('s1')).toEqual([]) + }) + + it('clears the buffer when the attempt ends', () => { + const { ctx, emit } = harness() + const live = createAssistantLiveBuffer(ctx) + emit(frame('s1', { type: 'start', attemptId: 'a1', turn: 1, step: 1 })) + emit(frame('s1', { type: 'chunk', attemptId: 'a1', index: 0, chunk: { type: 'text-delta', text: 'a' } })) + emit(frame('s1', { type: 'end', attemptId: 'a1', index: 1, outcome: { kind: 'abandoned' } })) + expect(live.chunksFor('s1')).toEqual([]) + }) + + it('a new start replaces the previous attempt of the same session', () => { + const { ctx, emit } = harness() + const live = createAssistantLiveBuffer(ctx) + emit(frame('s1', { type: 'start', attemptId: 'a1', turn: 1, step: 1 })) + emit(frame('s1', { type: 'chunk', attemptId: 'a1', index: 0, chunk: { type: 'text-delta', text: 'old' } })) + emit(frame('s1', { type: 'start', attemptId: 'a2', turn: 1, step: 2 })) + expect(live.chunksFor('s1')).toEqual([]) + emit(frame('s1', { type: 'chunk', attemptId: 'a2', index: 0, chunk: { type: 'text-delta', text: 'new' } })) + expect(live.chunksFor('s1').map(chunk => chunk.chunk['text'])).toEqual(['new']) + }) + + it('caps one attempt at LIVE_CHUNK_CAP chunks (oldest dropped)', () => { + const { ctx, emit } = harness() + const live = createAssistantLiveBuffer(ctx) + emit(frame('s1', { type: 'start', attemptId: 'a1', turn: 1, step: 1 })) + for (let index = 0; index < LIVE_CHUNK_CAP + 3; index++) { + emit(frame('s1', { type: 'chunk', attemptId: 'a1', index, chunk: { type: 'text-delta', text: String(index) } })) + } + const chunks = live.chunksFor('s1') + expect(chunks).toHaveLength(LIVE_CHUNK_CAP) + expect(chunks[0]?.index).toBe(3) + expect(chunks.at(-1)?.index).toBe(LIVE_CHUNK_CAP + 2) + }) + + it('caps tracked sessions at LIVE_SESSION_CAP (oldest dropped)', () => { + const { ctx, emit } = harness() + const live = createAssistantLiveBuffer(ctx) + for (let index = 0; index < LIVE_SESSION_CAP + 1; index++) { + emit(frame(`s${index}`, { type: 'start', attemptId: 'a1', turn: 1, step: 1 })) + } + expect(live.chunksFor('s0')).toEqual([]) + expect(live.chunksFor(`s${LIVE_SESSION_CAP}`)).toEqual([]) + // The most recent session is the only live one, and it has no chunks yet. + emit(frame(`s${LIVE_SESSION_CAP}`, { type: 'chunk', attemptId: 'a1', index: 0, chunk: { type: 'text-delta', text: 'x' } })) + expect(live.chunksFor(`s${LIVE_SESSION_CAP}`)).toHaveLength(1) + }) + + it('dispose unbinds the stream listener', () => { + const { ctx, off } = harness() + const live = createAssistantLiveBuffer(ctx) + live.dispose() + expect(off).toHaveBeenCalledTimes(1) + }) +}) diff --git a/tests/e2e/desktop-layout.e2e.ts b/tests/e2e/desktop-layout.e2e.ts index 0c8d797..87cd006 100644 --- a/tests/e2e/desktop-layout.e2e.ts +++ b/tests/e2e/desktop-layout.e2e.ts @@ -2,14 +2,16 @@ import { mkdirSync } from 'node:fs' import { homedir } from 'node:os' import { join } from 'node:path' -import { expect, test, type Page } from '@playwright/test' -import { PAGE_URL } from './host' +import { expect, test, type APIRequestContext, type Page } from '@playwright/test' +import { PAGE_URL, createHostApi, hostRpc } from './host' const WORKSPACE_PATH = process.env.DSH_E2E_DESKTOP_WORKSPACE ?? (process.env.DSH_E2E_WORKSPACE === undefined ? join(homedir(), 'dsh-e2e-desktop-layout-workspace') : `${process.env.DSH_E2E_WORKSPACE}-desktop-layout`) +let api: APIRequestContext + async function dismissOnboarding(page: Page): Promise { await expect .poll(() => page.getByRole('button', { name: /^(Continue|Configure later|继续|稍后配置)$/ }).count(), { timeout: 60_000 }) @@ -28,8 +30,20 @@ async function dismissOnboarding(page: Page): Promise { throw new Error('onboarding takeovers did not settle after visible dismissal') } -test.beforeAll(() => { +test.beforeAll(async () => { mkdirSync(WORKSPACE_PATH, { recursive: true }) + // Seed one workspace + one session through the host's own RPC surface (the + // calls the UI makes) instead of driving the workspace picker: DSH 0.1.5 + // ships BOTH a browser directory dialog and an OS-native chooser and picks + // one by platform, so a UI-driven flow only works on hosts whose chooser + // renders in the page. + api = await createHostApi() + const workspace = await hostRpc<{ workspace: { workspaceId: string } }>(api, 'workspace.create', { path: WORKSPACE_PATH }) + await hostRpc(api, 'session.create', { workspaceId: workspace.value.workspace.workspaceId }) +}) + +test.afterAll(async () => { + await api?.dispose() }) test('right panel keeps desktop session actions in their header positions', async ({ page }) => { @@ -45,38 +59,37 @@ test('right panel keeps desktop session actions in their header positions', asyn await page.goto(PAGE_URL, { waitUntil: 'domcontentloaded' }) await dismissOnboarding(page) - await page.getByRole('button', { name: /^(Choose workspace|选择工作区)$/ }).click() - const picker = page.getByRole('dialog', { name: /^(Select Workspace Directory|选择工作区目录)$/ }) - await expect(picker).toBeVisible() - await picker.getByRole('button', { name: /^(Edit path|编辑路径)$/ }).click() - const pathInput = picker.getByRole('textbox', { name: /^(Edit path|编辑路径)$/ }) - await pathInput.fill(WORKSPACE_PATH) - await pathInput.press('Enter') - await expect(pathInput).toBeHidden() - await picker.getByRole('button', { name: /^(Open|打开)$/ }).click() - - // Target the ACTIVE composer by its accessible name. DSH 0.1.2-alpha hosts - // keep an INERT `data-composer-input` "Choose workspace" textbox (with - // contenteditable="false") later in DOM order than the real composer — a - // bare `getByRole('textbox').last()` resolved to that one and fill() threw - // ("not an ,