# dsbot-session-create 中文文档

规范地址：https://npmcn.com/package/dsbot-session-create/

版本：0\.5\.0
npm 元数据更新时间：2026-11-03T12:16:40.492Z
中文译文更新时间：2026-11-03T13:23:59.122Z
软件包许可：MIT

本文由上游 README 机器翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/dsbot-session-create
原文对照：https://npmcn.com/package/dsbot-session-create/?lang=original

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：94.1 kB，5 个文件
- 模块格式：ES Module
- TypeScript 类型：未声明
- 运行依赖：0 个
- peer 依赖：@deepseek-ai/cordis \*，@deepseek-ai/dsh-tools \*
- 首次发布：2026-10-29T16:31:12.739Z
- 最新版本发布：2026-11-03T12:16:40.315Z
- 已发布版本数：6（近 12 个月 6 个）

## 中文简介

一个DSH插件，为会话提供了四个工具：create\_session（在指定的工作区或现有文件夹中打开一个新会话，将其模型固定到调度会话并向其传递一个提示），session\_message(将消息剪切到一个会话中，该会话是st

## 安装

```sh
npm install dsbot-session-create
```

也可以使用：`pnpm add dsbot-session-create`、`yarn add dsbot-session-create`、`bun add dsbot-session-create`

## README

# dsbot-session-create

给 DSH 加四个工具：

-   **`create_session`** —— 在指定的 DSH 工作区、或任意一个已存在的文件夹里开一个新会话，并把一段提示词投进去。开之前先把**模型**和**权限档**定下来。
-   **`session_message`** —— 给一个**还在跑**的会话插一条消息（插队投放，它下一步就读到）。
-   **`read_session_transcript`** —— 按会话 id 把它说过的话和当前状态读回来。
-   **`list_dispatched_sessions`** —— 看这个插件派过哪些会话、各自此刻什么状态。

它是一个 DSH 插件（dsh-plugin），宿主半边，没有界面。

三条与普通「开个会话」不同的硬规则，先看这里：

1.  **派出去的会话不落到宿主的全局默认模型上**。它钉死到派活那个 Bot 自己正在用的模型；读不到派活人的模型就**不建这一单**。
2.  **缺省权限档是全权限**。不传 `permission` 时一律开在 `danger-full-access`，并把这一条写进结果；要 `auto` 就显式传。
3.  **补话只服务还在跑的会话**。目标会话已经停下，就拒投并让你新开一个，不往历史里一层一层堆活。

## 为什么需要它

DS Bot 的 Chief 能把活派给别的 Bot（`message_bot`），但派不进「某个项目的那个会话」。 宿主本身早就有这个能力（`ctx.sessionController.create({ cwd | workspaceId })`），只是没有对外开放成工具。

这个插件就是那层薄壳：**把宿主已有的原语开成工具**。 参考实现是 DS Bot 自己的 `src/host/roster.js`（帮 Bot 建会话时用的同一批调用）。

## 工具

```
create_session(title, prompt, workspace? | folder?, preset?, permission?, model?)
session_message(sessionId, message)
read_session_transcript(sessionId?, last?)
list_dispatched_sessions(limit?)
```

### `create_session`

| 参数 | 必填 | 说明 |
| --- | --- | --- |
| `title` | 是 | 新会话的名字，显示在会话列表里 |
| `prompt` | 是 | 投进新会话的那段话（排队投放，`mode: 'queue'`） |
| `workspace` | 二选一 | 工作区 id，或它的完整文件夹路径 |
| `folder` | 二选一 | 任意一个存在的文件夹的完整路径 |
| `preset` | 否 | agent 预设（`standard` / `minimal` / `ptc` / `creator`），建会话时写进 header，开跑后不能换；名字错就整单不建 |
| `permission` | 否 | 权限档，见下面「权限档」 |
| `model` | 否 | 写成 `provider/model`；不写就与派活的那个 Bot 一致 |

`workspace` 和 `folder` **必须给且只能给一个**。

两者的区别：

-   `workspace` —— 会话挂在那个 DSH 工作区下，会出现在用户的对应项目里。
-   `folder` —— 会话按 `cwd` 创建，**不加入任何 DSH 工作区**，用户的项目列表保持原样。

两者是同一套宿主原语的两种落位方式，没有别的差别。

#### 模型（fail-closed）

宿主 `sessionController.create` **不转传 model**，所以不钉就是「宿主的全局默认模型」；2026-11-01 那个默认一度是按量付费的模型，派出去的会话因此替用户烧过真钱。所以：

-   不写 `model` 时，取**派活那个 Bot 自己会话正在用的模型**（`agent.session.requestHeader().config` 的 provider / model / reasoningEffort）。
-   **不写任何模型名做兜底**：写死一个名字，provider 或组一改名，这条绑定就静默失效。
-   显式写 `model` 必须是 `provider/model`——按第一个斜杠切一刀、两边都要有字；`provider` 空或 `model` 空就整单失败（`model-invalid`），model 本身允许再带斜杠（`b/c`）。
-   派活人的模型**读不到就不建这一单**（`dispatcher-model-unknown`）。宁可失败，也不留一条会落到全局默认模型上的会话。
-   建会话之后用 `sessionController.selectModel` 钉死。这一步失败**不**中断派送，但结果里带一句 `modelNote`，明说「没钉上、可能落到宿主默认、花额度之前先查它的模型」。

#### 权限档

档名由宿主判定（`/permission` 的合法值会随宿主升级漂移），本插件不硬编码白名单，只做三件事：

-   **显式传 `read-only` / `workspace-write` 会被改写成 `danger-full-access`**，并在结果里如实标出 `permissionOverriddenFrom`。理由：这两档带 `approval: ask`，派单会话无人应答就整轮卡死，而宿主没有「给已存在会话提权」的入口。要放行只读探针，在宿主环境里设 `DSBOT_ALLOW_RESTRICTIVE_PERMISSION=1`（值必须严格等于 `1`）。
-   **没传 `permission` 时一律开在 `danger-full-access`**，并在结果里带 `permissionNote` 说明。理由：`auto` 档下宿主 auto-review 审查每一次工具调用，而派出去的会话无人值守——审查器没按约定给出判决时，宿主直接按拒绝处理、不重试也不降级，会话就卡在原地。要 `auto` 就**显式传**。
-   **没有 `commands.execute` 时**设不了档：保持不传，吃宿主默认（本机默认就是全权限），并在结果里说明。
-   **显式要 `auto` 时**：宿主名单明确没有就整单失败、不建会话（`host-missing`）；名单读不到就照旧下发，让宿主的 `/permission` 自己去判认不认这个名字。
-   `auto` 档的第二步是把审批策略写成 `never`：`auto` 自带 `ask`，无人值守时 reviewer 判 deny 会变成整晚卡死。这一步走 `session.append('approval/policy', { policy: 'never' })`，不调 `ApprovalService.setPolicy`（后者会往会话里注入一条假消息）；写不进去就**归档这个会话并整单失败**——让会话带着 `ask` 活下去，等于埋一颗整晚卡死的雷。
-   有一处宿主机制产物要知道：`/permission auto` 那一步自己会写一条 `ask`，并往会话里注入一句「changed from "never" to "ask"」的 user 消息；本插件紧跟其后写 `never`，最终生效的是 `never`，但读 transcript 时会看到那条消息。

### 派送台账

每次 `create_session` 成功，都会往**派送台账**追加一行：

```
$DSH_HOME/bot/session-create/dispatched-sessions.jsonl      # 可用 DSH_SESSION_CREATE_LEDGER 覆盖
```

每行记住：会话 id、标题、落位、提示词开头、派送时刻，以及这一单实际下发的 `preset` / `permission` / `permissionOverriddenFrom` / `approvalPolicy`，和**派活人的会话 id**（回执按它投回去）。建了会话、但**权限档**这一步失败的单会补写一行 `failed`（`src/core.ts:679`），否则事后谁都在会话列表里找不到那个空会话；提示词投递失败走另一条路——归档会话并报 `prompt-failed`，不写台账。

台账写失败**不会**让整次派送算失败（会话已经建出来了），但结果里会带 `ledgerError`，并写明「自己手动记下这个 session id」。

落点为什么在 `bot/` 下：这份账是 **Bot ↔ 人的交接记录**，属于 DS Bot 自己的数据，所以跟在 DS Bot 的 home（`<DSH_HOME>/bot`）里。若 ds-bot 的 home 被它自己的 profile 配置改到别处，用 `DSH_SESSION_CREATE_LEDGER` 对齐。

### `session_message`

给一个**已经存在、且还在跑**的会话插一条消息，不新建会话：

| 参数 | 必填 | 说明 |
| --- | --- | --- |
| `sessionId` | 是 | 目标会话的 id（`create_session` 或 `list_dispatched_sessions` 给的） |
| `message` | 是 | 插进去的那段话 |

-   走 `controller.prompt(mode: 'steer')`（宿主 `agent.steer`）：**插进正在跑的那一轮**，会话下一步就读到，不等这一轮结束。
-   目标会话的 turn 已经结束（宿主直报 `idle`）时**拒投**（`session-idle`），并指向 `create_session`。判据只用宿主直给的状态，不替调用方判断「这条消息算新任务还是纠偏」——那是语义判断。这条是 2026-11-01 那单事故的防线：同一条会话被补了 16 轮裁决，历史涨到约 76 万 token，第二天宿主把它切到 512,000 窗口的模型后立刻放不下。
-   宿主没报状态时按「还在跑」处理，结果里如实标 `targetState: unreported`。
-   只说「已插进正在跑的那一轮」，不说「它看过了」——后者只有读回才能证明。
-   单次投递超时 10 秒（`MESSAGE_TIMEOUT_MS`）。

### `read_session_transcript`

按 id 取回一个会话说了什么、现在什么状态（最新若干条消息 + 当前这一轮结束没有）。**不给 `sessionId` 就取台账里最近一条派送**，所以「刚派完那单跑完没」不需要手动找 id。

`last` 给取回多少条（1..200，默认 40），更早的用更大的值再读。历史读回超时 20 秒（`HISTORY_TIMEOUT_MS`）。

### `list_dispatched_sessions`

倒序列出台账里的派送，每条带**此刻**的状态：还在跑 / 上一轮已结束 / 还没起跑（`not started`）/ 读失败。用来挑「哪些可以收了」。

### 状态是怎么来的

会话「跑完没有」有两条来源，插件**优先问宿主**，拿不到才从会话自己的事件推：

-   宿主直给（`agent.status` 是 `running` / `idle`）→ 输出写 `the harness reports this Session as running`；
-   事件推断（只有 `turn/start` 没有 `turn/end`）→ 输出写 `read from the Session's own events`。

两种来源的措辞**故意不同**：推断不许冒充宿主结论。

读回走宿主自带的 `ctx.sessionController.page({ address, throughSeq, maxMessages }, signal)`，参数形状逐字照抄 DS Bot 自己的三处生产调用（`src/host/parts.js:1287`、`chat-index.js:58`、`usage.js:351`）——不自己造一套读事件的机制。宿主内置的 `session_event_read` / `session_search` 工具**对普通会话不可用**（返回 `No session matched "…"`），故不走。

### 派完自动回执

派送时把派活人的会话 id 记进台账。目标会话**正常收尾**（`turn/end.reason.kind === 'completed'`）、安静满 **120 秒**（`QUIET_WINDOW_MS`）、宿主复核为 `idle`，并且**它自己派出去的活都已经停下来**，插件才用 `controller.prompt(mode: 'queue')` 投一条短回执回派活人（`src/core.ts` 的 `createDispatchNotifier`，`src/index.ts:299` 的 `installDispatchNotify` 挂到 ctx 上——没人调用工具时也要有东西替我们盯着）。

投递面是收窄过的：把**假回执**（关停、重启掐断、报错、卡住）和**分段派工**（还在等自己派出去的活）这两大类挡掉。但要说清楚——`turn/end` 这一层看不出「这一单」的边界，**收窄不等于只剩整单完成**。2026-11-03 拿本机派单台账 126 单逐条回溯（含静默窗口与三道门）：旧口径会投 160 条回执，新口径 154 条（少 3.75%，其中真正被门三拦下的是 0 条）；120 单仍会收到至少一条，11 单会收到多条，最多的一单 11 条。想再降只能改协议（例如一次派单只投一条回执），不在这一版里。

-   **只认正常收尾**：`aborted`（关停/叫停）、`interrupted`（重启掐断）、`error`、`blocked`、`max-tokens`，以及认不出来的取值，一律不进静默窗口、一个回执都不发（`COMPLETED_REASON`，门一）。取值来自 2026-11-03 全扫本机 `<DSH_HOME>/data/sessions/**`（当日 1,740 份文件、7,851 条 `turn/end`）的统计：`completed` 6,914 / `aborted` 464 / `error` 171 / `blocked` 153 / `interrupted` 121 / `max-tokens` 29。
-   **还在等自己派的活就不算收口**：台账里 `dispatcherSessionId` 指向它、宿主又报着没停的子会话，说明它是在等它们（`firstBusyChild`，门三）。等的时候不投；子会话停下、它被叫醒再跑一轮、这一轮没有在跑的子会话，才投。
-   **机械区分不了的那一类**：自己没派人、也没出错，就是这一轮干完了停下——这与「整单完成」在宿主事件面上同形，插件不替派活方猜，照旧投一条；由派活方自己按纪律决定要不要理它。

回执走 `queue` 不走 `steer`：它是给派活人的旁路通报，排到下一轮，不去打断他手头正在跑的那一轮。

### 宿主启动自检（重启后自动接续）

宿主重启时在跑的 Bot 会话一并被杀，醒来后没有东西会主动说「刚重启过、盘上有接手指针」。这一段补上它（`src/autoresume.ts`，`src/index.ts:303` 的 `installAutoResume`）：

-   **只认两个文件**：指针 `restart-pending.json`，已确认记录 `restart-pending.ack.json`。目录**运行时算**，不写死：`DSH_AUTORESUME_STATE_DIR` > `<DSH_HOME>/bot/session-create`（`DSH_HOME` 缺省 `~/.dsh`，与派送台账同一目录；`src/autoresume.ts` 的 `resolveAutoresumeStateDir`）。要接到别处就设那个 env，不用改代码。
-   **目标只能是指针里的 `notify_session_id`**：缺失或不是字符串就跳过。不猜主会话、不遍历会话列表。
-   **投三次**：间隔 6s / 12s / 20s（`src/autoresume.ts:50`，`AUTORESUME_RETRY_DELAYS_MS`；38s 是三次间隔之和，不是总超时——单次投递上限 10s）。宿主刚起来那几秒恰恰是最可能投不进去的时刻，所以单跳不够；只有 `send-failed` 才排下一次，送达、已确认、没指针、没目标都是终态。
-   **幂等只看 `written_at`**：已确认记录里的 `written_at` 不严格新于指针的 `written_at` 就跳过。失败也写一份 ack，但**故意不写 `written_at`**——它只是盘上的证据，不参与幂等判断，所以下次启动照常重试。
-   **只投 `prompt(mode: 'queue')`**，不建会话、不改会话状态。唤醒正文只给指针的绝对路径和固定一句「上一个宿主 pid」，让收到消息的一方自己去读盘，不抄正文。
-   **关掉它**：`DSH_AUTORESUME_DISABLE=1`（在读任何文件之前就判断，连定时器都不注册）。
-   **全程 fail-open**：读不到盘、JSON 坏、没有目标，都只记一行日志就安静退出；定时器 `unref`，不阻塞宿主启动也不拖住宿主退出。

## 失败时的行为（fail-closed）

位置不成立就**什么都不建**，直接报错：

| 情况 | 结果 |
| --- | --- |
| 文件夹不存在 | 报错，不建会话 |
| 工作区认不出 | 报错，**不回落**到默认工作区 |
| `workspace`/`folder` 两个都给或都不给 | 报错 |
| `preset` 名字宿主不认 | 报错，不建会话 |
| `model` 不是 `provider/model` | 报错（`model-invalid`），不建会话 |
| 派活人的模型读不到 | 报错（`dispatcher-model-unknown`），不建会话 |
| 显式要 `auto`、宿主名单里没有 | 报错（`host-missing`），不建会话 |
| 宿主缺所需服务 | 报错，附上缺了哪几个 |
| 权限档设不上 | 归档这个会话，整单报错 |
| `auto` 的审批策略写不进 | 归档这个会话，整单报错 |
| 提示词投递失败 | 归档这个会话，如实报错，不对外说成功 |
| 补话给已经停下的会话 | 拒投（`session-idle`），什么都不发 |

那张「宿主缺服务」的报错要能真的报出来，靠的是**服务不写进 `inject` 硬声明**：插件只 `inject: ['tools']`，其余服务都走 `ctx.get()` 宽容路径（`src/core.ts` 的 `resolveServices`）。写成硬 inject 的话，宿主缺服务时整棵插件树会加载失败，`preflight` 那句可读报错根本没有机会执行——用户只会看到「工具不存在」。

这里和 DS Bot 的 `roster.js` 有一处**有意不同**：它会「工作目录没了就悄悄换到团队工作区」，本插件不这么做——悄然换位置会让人以为任务落在 A 项目，实际跑在别处。

归档失败时，报错里会改成说「列表里可能还留着一条空会话」——不谎称已归档。

## 装

推荐以链路方式装进 profile：

```
# 在 profile 目录里（<DSH_HOME>/profiles/<profile>）
# 把 "dsbot-session-create": "link:<本插件目录的绝对路径>" 加进 package.json 的 dependencies
```

或直接把本目录复制进 `node_modules/`。装完 **必须重启 DSH**（重启只能由用户执行）。

`cordis.patch.yml` 负责把它挂上：

```yaml
- insert:
    - id: session-create
      name: dsbot-session-create
```

## 开发

```bash
bun run build   # src/*.ts → index.js（@deepseek-ai/dsh-tools 保持 external）
bun test        # 165 个测试、5 个文件；plugin.test.ts 测的是构建产物，跑前先 build
```

类型检查（本仓不带 `typescript` 与 `@types/node` 依赖，用现成的那份就行）：

```bash
node <某个 node_modules>/typescript/bin/tsc --noEmit -p . --typeRoots <同一个 node_modules>/@types
```

`--typeRoots` 显式指过去，是因为 `tsconfig.json` 里**没有**写任何本机路径：它只声明 `"types": ["node"]`，落在哪由你调用时决定。

## 几个必须知道的事实

-   **`@deepseek-ai/dsh-tools` 不在 profile 的 `node_modules` 里**（在宿主 `app.asar` 内，加载插件时注入）。所以 `test/stubs/dsh-tools.ts` 是它的替身，`tsconfig.json` 的 `paths` 只为本机开发把它指过去——那**不是**权威类型定义，是按 DS Bot 的实际用法反推的最小形状。
-   宿主 API 的形状（`create` 返回 `{ sessionId }`、`prompt` 要 `requestId` + `mode` + `content:[{type:'text',text}]`）是从 DS Bot 的源码**读出来**的，不是从官方文档抄的。上游没有这层文档。
-   **代码与文档里不许出现本机路径**（2026-11-02 用户定）：源码、测试、注释、配置一律不许写盘符路径、用户名或本机目录名。唯一例外是单元测试里验证「Windows 路径怎么处理」用的**虚构样本**（`test/read.test.ts`、`test/core.test.ts` 与 `test/autoresume.test.ts` 里那几个），它们跟这台机器的实际目录无关。默认落点由 `DSH_HOME` / `homedir()` 在运行时算，要换就加一个 env 覆盖；`test/autoresume.test.ts` 有一条用例扫 `src/` 下全部 `.ts`（不递归子目录），命中盘符形式的路径就判红。
-   启动自检的落点跟随上面的规则：换机器、换 `DSH_HOME` 都不用改代码；要把指针接到某个固定目录，设 `DSH_AUTORESUME_STATE_DIR`。
-   派出去的**提示词**走 `queue`，**补话**走 `steer`，**回执**走 `queue`——三个通道口径不同，改的时候别顺手统一。

## 已验证 / 尚未验证

**已验证（2026-11-03 实测）**

-   `bun run build` 之后 `git status` 只见预期改动：`index.js` 与 `src/` 同步（64,495 字节，sha256 `040124bb…989`）。
-   `bun test`：176 pass / 0 fail / 654 次断言，5 个文件（bun 1.3.13）。
-   `tsc --noEmit -p . --typeRoots <现成的 node_modules>/@types`（借一份现成的 `typescript` 与 `@types/node`，见「开发」）：0 个错误。**不带 `--typeRoots` 会报 `error TS2688: Cannot find type definition file for 'node'.` 并退出码 2**——本仓没有自己的 `node_modules`。
-   0.2.0 那份构建的**描述文本确实在真实宿主里加载过**：宿主会话的工具表里 `create_session` 带上了 `model` 参数与 `auto` 权限段（`never the host's global default`、`DSBOT_ALLOW_RESTRICTIVE_PERMISSION`），说明 profile 挂的就是本仓的 `index.js`。0.4.0 与 0.5.0 的改动都要重启宿主之后才生效（重启只能由用户执行）。

**尚未验证**

1.  `ctx.sessionController.page` 在真宿主里是否真按 `{ records, hasMore }` 返回（参数形状抄的是 DS Bot 生产代码，不是官方文档）。
2.  `steer` 插队以后，目标会话是否真在下一步就读到（只验到「宿主受理」这一层）。
3.  `auto` 档写 `approval/policy: never` 在真宿主里是否被认（单测覆盖的是「写不进去就归档并报错」这条路）。
4.  启动自检的**真重启投递**：注册、延迟序列、幂等判据都有单测，但没在真重启里收过那条唤醒消息。
5.  **提示词的 30 秒超时上限**没有「真触发」用例——bun 1.3.13 的 `AbortSignal.timeout` 在进程没有其它待办定时器时不触发（宿主是 Node，不受影响）。
6.  `last` 夹到 1..200、`limit` 夹到 1..20 是本插件自定的范围，宿主有没有更低的天花板未验。

## 许可

MIT

## 常见问题

### dsbot-session-create 是什么？

一个DSH插件，为会话提供了四个工具：create\_session（在指定的工作区或现有文件夹中打开一个新会话，将其模型固定到调度会话并向其传递一个提示），session\_message\(将消息剪切到一个会话中，该会话是st

### 如何安装 dsbot-session-create？

在项目目录运行 npm install dsbot-session-create，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：@deepseek-ai/cordis（\*）、@deepseek-ai/dsh-tools（\*）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### dsbot-session-create 支持 TypeScript 吗？

该包的 package\.json 没有声明类型入口。需要类型提示时，可以在 npm 上查找社区维护的 @types/dsbot-session-create 是否存在。

### dsbot-session-create 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### dsbot-session-create 可以商用吗？

dsbot-session-create 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### dsbot-session-create 还在维护吗？

最新版本 0\.5\.0 发布于 2026/11/03（今天）；至今共发布 6 个版本，其中过去 12 个月发布了 6 个；首次发布于 2026/10/30。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

