# dsh-plugin-loom-chat Loom Chat 是一个 DSH Web 客户端插件,把线性的普通会话转换为可平移、缩放和并行操作的 Loom 风格无限画布。 [![npm version](https://img.shields.io/npm/v/dsh-loom-chat.svg)](https://www.npmjs.com/package/dsh-loom-chat) [![CI](https://github.com/onenameneo/dsh-plugin-loom-chat/actions/workflows/ci.yml/badge.svg)](https://github.com/onenameneo/dsh-plugin-loom-chat/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) ![DSH Web Plugin](https://img.shields.io/badge/DSH-Web%20Plugin-0ea5e9.svg) [English README](README.md) · [GitHub 仓库](https://github.com/onenameneo/dsh-plugin-loom-chat) ## Loom 项目 [Loom](https://github.com/onenameneo/Loom) 是一个把 AI 对话组织成可探索思考空间的本地 Agent 工作台。它将流式对话、分支画布、基于项目的文件与会话、精细的上下文管理、Agent 工具、MCP Server、长期记忆和本地 Agent 活动观察结合在一起。用户可以把一个问题拆分为多条探索路径,将它们整理成思考图,并在每个分支中携带恰当的上下文继续推进。Loom 适合研究、学习、写作、编程,以及任何需要持续思考和并行探索的工作;本插件则以轻量的方式,将 Loom 的分支画布体验带到 DSH Web 中。 ## 安装 Loom Chat 已通过 npm 分发,npm 包包含预构建的浏览器代码。不带标签安装即可获取当前 `latest` 版本: ### 最新版 ```sh dsh plugin --profile web add dsh-loom-chat dsh web ``` ### 从 GitHub 安装 需要最新的、尚未发布的提交时,可以直接安装仓库源码: ```sh dsh plugin --profile web add github:onenameneo/dsh-plugin-loom-chat dsh web ``` 从 GitHub 安装时会通过 `prepare` 脚本在本地构建。如果 pnpm 要求确认构建权限,请将它提示的准确包名添加到 profile 的 `pnpm-workspace.yaml` 中的 `allowBuilds`,然后重新执行安装命令。日常使用建议从 npm 安装,因为 npm 包直接提供预构建产物。 更新或卸载插件: ```sh dsh plugin --profile web update dsh-loom-chat dsh plugin --profile web remove dsh-loom-chat ``` 插件只装配到 `web` profile,不会修改 DSH 原生侧边栏,也不会替换原生会话渲染器。 ## 功能介绍 它适合需要围绕一个问题同时探索多个方向的场景:保留主会话作为上下文起点,把新的问题分支到独立会话中,并在画布上同时查看各条探索路径。 - 画布中的每个会话窗口都可以直接阅读历史、编辑输入、发送消息和停止生成。 - Canvas 的 transcript 和消息渲染完全由本插件实现,支持文本/Markdown、代码、思考、工具和状态摘要、命令、上下文、附件引用;遇到未知的数据结构时也会显示可读的 fallback。 - 从消息或选中文字发起分支时,子会话继承 DSH 分岔边界前的上下文;选中文字会以引用内容保留在子会话顶部。 - 子会话之间互相独立,支持继续创建更深层级的分支,不复制渲染后的消息,也不自动同步后续内容。 - 需要完整附件、斜杠命令、模型选择或 Plan 能力时,可以从窗口进入 DSH 原生单会话模式。 ## 使用演示 从 DSH 原生会话头部进入 Loom 后,可以在画布中平移和缩放视图;点击任意会话窗口的分支按钮,就能从对应的上下文继续探索新的方向。每条分支都有独立的历史、输入和运行状态,可以同时推进多个思路。 ![从 DSH 会话进入 Loom 的操作演示](assets/intro_0.gif) _从原生会话打开 Loom 画布。_ ![Loom 画布中的多会话并行探索](assets/intro_1.png) _在同一张画布上查看多个相互关联、又彼此独立的会话。_ ## 为什么叫 Loom Loom 的英文原意是“织布机”。一次对话像一根思路线:从主会话出发,随着问题分支成不同方向,再在同一张画布上并行展开。Loom Chat 希望像织布机一样,把这些分散的思路编织成一张可观察、可继续延展的对话网络,而不是把探索过程压缩成一条线性的记录。 ## 交互模型 - **Canvas 模式**:默认展示当前会话所在的完整父子树(父会话、兄弟分支和后代);支持平移、缩放、窗口选择和从任意窗口继续岔出。 - **窗口交互**:每个窗口都有自己的会话历史、草稿、发送、停止、运行中和错误状态;多个会话可以同时在画布中并行运行,互不覆盖,也不会共享输入内容。删除窗口需要确认,并会一并归档它的所有子节点。 - **单会话模式**:打开某个节点后进入 DSH 原生会话界面,继续使用原生输入框、工具、附件、Plan、模型选择和会话投影。 - **模式切换**:点击窗口只会选中它;只有点击窗口右上角的“聊天”按钮才进入原生单会话模式。单会话头部提供 Loom 入口;回到 Canvas 时恢复当前选中节点和本次页面运行中的视口。 - **无限层级**:Canvas 根据每个会话的 `parentId` 递归构建关系,不限制分支深度。 - **导航与性能**:进入画布固定展示当前会话相关的父子树并定位当前会话,工具栏提供定位当前会话和重置视图。分支可以折叠,已有节点在消息、标题和状态变化时保持位置;历史会话按视口和选中状态按需加载,屏幕外节点先显示轻量卡片。 ## 上下文继承 岔出话题使用 DSH 的 `sessions.fork({ sessionId, atSeq, increaseTitle })`。子会话继承分岔边界之前的持久化会话历史,随后与父会话、兄弟会话独立。对于选中文字的分岔,首次发送的 prompt 还会以结构化引用块携带选中片段,使模型同时收到继承的历史和准确的选中文本。插件不会复制渲染后的消息,也不会自动同步后续消息。 分岔位置必须是已完成轮次的稳定边界。正在运行的会话不会被截断分叉。选中文字分岔的展示状态按子会话 ID 恢复,页面重载后引用卡仍然保留,分岔边界之前的继承消息继续隐藏。带有 `origin: 'subagent'` 的会话不作为 Loom 普通话题节点展示。 Loom 子会话第一次发送非空的继续提问后,会使用归一化后的提问生成标题,最多保留 30 个 Unicode 字符,超出时追加省略号。无论消息来自插件回退输入框还是宿主完整 Composer,插件都会观察分岔边界后的第一条用户消息完成改名。这个过程调用宿主公开的会话改名操作,不额外请求大模型,后续提问也不会重复改名。 Canvas 窗口使用 DSH 公开的 session face 和每会话 input face。它是用于并行探索的紧凑交互面;需要附件、斜杠命令、模型选择、Plan 等完整输入能力时,进入单会话模式使用宿主原生界面。 画布的完整关系图与当前展示范围分离:折叠和离屏卸载只改变展示与挂载,不改变真实父子关系或递归归档范围。运行中、获得焦点、输入法组合中或仍有未提交草稿的窗口会受到保护,恢复后继续使用同一会话输入。 ## DSH 插件装配 插件宿主入口导出 Cordis `apply`,浏览器入口通过 `dsh.client` 声明 Web 平台、依赖注入和 `exports["./client"]`。`dsh.bundle.patch` 指向顶层数组格式的 `cordis.patch.yml`,由 profile 将插件作为可选 bundle 装载。 插件只使用 DSH 的公开 session、runtime、conversation、workspace 和 UI slot 能力;需要开发新的宿主能力时,应先在 DSH 中提供公开 slot 或 service,再由插件消费。插件自身不依赖宿主私有组件。 开发入口和 Cordis 插件的最小结构可参考 [DSH 插件入门](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/index.zh.md),Web 客户端 bundle 的装配规则见 [Client Modules](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/client-modules.zh.md)。 ## 宿主边界 插件不修改 DSH 原生侧边栏、`ui-workspace` 或主会话渲染器。Canvas 使用公开的 session/runtime 与 `shell.overlay` Slot;脱离当前选择且尚未加载历史的窗口,会由插件串行调用 `ctx.sessions.open()` 临时完成公开历史 hydration,然后恢复用户之前的当前会话。Canvas 不调用宿主私有 renderer,也不依赖窗口级 `session.open()`。单会话模式通过 `ctx.sessions.open()` 交回宿主会话界面。 Canvas 不承诺在每个窗口内复刻宿主的完整 Composer。附件、斜杠命令、模型选择、Plan 等宿主专属控件,请打开窗口进入原生单会话模式使用。 ## 隐私与权限 基于当前运行时代码,Loom Chat: - 不会直接向第三方服务器发送数据; - 不读取 API key 或环境变量; - 不执行 shell 命令; - 使用 DSH 公开的 session、runtime、conversation、workspace 和 UI API; - 使用浏览器 `localStorage` 保存 Loom 分支展示元数据,包括派生标题和分支边界; - 用户通过插件界面请求时,可以打开、fork、改名、取消和归档 DSH 会话。 ## 兼容性 - 需要使用 DSH Web profile,以及公开的 session、runtime、conversation、workspace 和 UI slot 能力。 - DSH 包版本线为 `0.1.x`,当前使用并验证的是一致的 `0.1.0-rc.7` 公开包集合;peer dependency 上限为 `<0.2.0`。 - 插件依赖 DSH 当前的开发预览 API;随着 DSH 演进,兼容性可能发生变化。 - 本地开发和构建需要 Node.js `^22.19.0` 或 `>=24.0.0`,以及 pnpm `11.7.0`。 ## 本地开发 ```sh pnpm install pnpm test pnpm typecheck pnpm build pnpm pack --pack-destination ./.artifacts ``` 使用临时 profile 验证打包插件: ```sh DSH_HOME=/tmp/dsh-loom-chat-profile \ dsh plugin --profile web add "$PWD/.artifacts/dsh-loom-chat-0.1.0-rc.4.tgz" ``` ## 发布 npm 维护者将发布版本直接发布到 `latest`: ```sh npm login npm whoami pnpm test pnpm typecheck pnpm build pnpm run pack:verify pnpm publish --tag latest ``` `prepare` 脚本会在发布前构建 `lib/`;`files` 字段会限制最终包只包含运行时代码、类型声明、DSH patch、文档和许可证。