dsh-session-bridge 0.3.2-alpha.1 → 0.3.2-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,18 +16,26 @@ action does.
16
16
  session (top-level UI session) in the current workspace, or in another
17
17
  workspace when you pass `workspaceId` / `cwd`. It can send one first prompt
18
18
  and optionally block until the first reply. Provider / model / reasoning
19
- effort are inherited from the calling session by default.
19
+ effort are inherited from the calling session by default. An async create
20
+ (no `waitForReply`) returns a `sinceSeq` anchor for a later precise `wait`.
20
21
  - **Send messages to any session.** `session_bridge_send` appends a turn
21
22
  (`mode=queue`) or injects steering into the running step (`mode=steer`), and
22
- can optionally wait for the next reply.
23
+ can optionally wait for the next reply. An async send also returns a
24
+ `sinceSeq` anchor.
23
25
  - **Wait for a reply or a segment.** `session_bridge_wait` blocks until new
24
- assistant output appears after a given seq: `waitFor=reply` (default) returns
25
- as soon as a new **text** reply is readable; `waitFor=segment` returns as
26
- soon as any new **completed output step** appears (an `assistant/message` —
27
- text, reasoning, or tool-call turn), *without* waiting for the whole turn, so
28
- you can observe output paragraph by paragraph as it is produced. With
29
- `requireTurnEnd` it additionally waits for the turn to settle.
30
- Timeout / abort return the partial result rather than throwing.
26
+ assistant output appears after `sinceSeq` (default: the latest event seq at
27
+ call time): `waitFor=reply` (default) returns as soon as a new **text** reply
28
+ is readable; `waitFor=segment` returns as soon as any new **completed output
29
+ step** appears (an `assistant/message` — text, reasoning, or tool-call turn),
30
+ *without* waiting for the whole turn, so you can observe output paragraph by
31
+ paragraph as it is produced. With `requireTurnEnd` it additionally waits for
32
+ the turn to settle. Timeout / abort return the partial result rather than
33
+ throwing. **An already-landed reply is never lost**: when no new output
34
+ arrives within the budget, the latest PRE-EXISTING reply/segment is returned
35
+ with `stale: true` (no more `(no text)`). To retrieve exactly "the reply to
36
+ what I sent", pass the `sinceSeq` returned by `session_bridge_send` /
37
+ `session_bridge_create` (works regardless of caller latency); `sinceSeq: -1`
38
+ counts existing events too, i.e. the anchor for a brand-new session.
31
39
  - **Read any session.** `session_bridge_read` folds a session's event log into
32
40
  readable rows — live or offline (from persistence) — with `sinceSeq` paging,
33
41
  role filtering, and a `limit` (default 20, max 100).
@@ -44,7 +52,9 @@ action does.
44
52
  working directory. Bridge-registered titles act as aliases.
45
53
  - **Monitor and schedule a main task.** `session_bridge_status` reads a
46
54
  session's real-time progress (running/idle, open turn, time since the last
47
- event for stall detection, pending work, latest reply). `session_bridge_cancel`
55
+ event for stall detection, pending work, latest reply); only a **running**
56
+ session is flagged `[STALLED]` (a quiet idle session is not stuck — same rule
57
+ as the watchdog). `session_bridge_cancel`
48
58
  stops a running session. `session_bridge_monitor_start` runs a **background
49
59
  watchdog loop** that polls the task, nudges it when it stalls, corrects it
50
60
  when it drifts, terminates it after it stays stuck, and wraps up when it
@@ -68,7 +78,9 @@ action does.
68
78
  | Making progress | Reset the stall counter (steady) |
69
79
 
70
80
  The watchdog only treats **running** sessions as stalled, so a finished or idle
71
- task is wrapped up rather than nudged forever. Logs go to
81
+ task is wrapped up rather than nudged forever (the `[STALLED]` marker in
82
+ `session_bridge_status` follows the same rule and is shown for running sessions
83
+ only). Logs go to
72
84
  `~/.dsh/super-injector/dsh-session-bridge-monitor.log` (overridable).
73
85
 
74
86
  Control it with `session_bridge_monitor_start` / `_stop` / `_list`.
@@ -122,6 +134,9 @@ bash scripts/build.sh && npm run build:client
122
134
  # type-check src/ against the dsh that is actually installed (no checkout needed)
123
135
  npm run check:compat
124
136
 
137
+ # regression tests for the wait/stall core logic (Node type stripping, no deps)
138
+ npm test
139
+
125
140
  # via the injector toolchain
126
141
  dev_build_plugin dsh-session-bridge
127
142
  ```
@@ -131,10 +146,13 @@ That checkout frequently lags the harness the plugin is loaded into, so a green
131
146
  `build.sh` does **not** prove the plugin works on the running DSH — `build.sh`
132
147
  warns when the two versions differ. Use `npm run check:compat` for that: it
133
148
  type-checks `src/` against the `lib/types/*.d.ts` shipped inside the installed
134
- DSH package, which is the exact API surface the plugin loads against.
149
+ DSH package, then reads the built `lib/index.js.map` to verify the
150
+ self-contained bundle actually inlined that same DSH version. (Type-checking
151
+ alone is not enough — a stale checkout type-checks green while the artifact
152
+ ships old DSH code.)
135
153
 
136
154
  The GitHub Actions CI (`ci.yml`) instead resolves the `@deepseek-ai/dsh-*`
137
- prereleases from the registry — pinned to the `0.1.6-alpha.1` line, which is
155
+ prereleases from the registry — pinned to the `0.1.6-alpha.2` line, which is
138
156
  the DSH API surface this code targets — then runs `pnpm typecheck` and
139
157
  `pnpm build:client` (the self-contained `tsdown` bundle). Bump that pin
140
158
  together with the code when you migrate to a newer DSH API.
@@ -245,14 +263,14 @@ registration and junction; not re-assembled on restart).
245
263
 
246
264
  | Tool | What it does |
247
265
  |---|---|
248
- | `session_bridge_create` | Create a main session (current or another workspace via `workspaceId` / `cwd`); optional first prompt + `waitForReply`. |
249
- | `session_bridge_send` | Send a message (`mode=queue`/`steer`); optional wait-for-reply. |
250
- | `session_bridge_wait` | Wait for new output after `sinceSeq`: `waitFor=reply` (text) or `waitFor=segment` (any completed step, no full-turn wait); optional `requireTurnEnd`. |
266
+ | `session_bridge_create` | Create a main session (current or another workspace via `workspaceId` / `cwd`); optional first prompt + `waitForReply`; async creates return a `sinceSeq` anchor. |
267
+ | `session_bridge_send` | Send a message (`mode=queue`/`steer`); optional wait-for-reply; async sends return a `sinceSeq` anchor. |
268
+ | `session_bridge_wait` | Wait for new output after `sinceSeq` (default: latest seq at call time; `-1` counts existing events): `waitFor=reply` (text) or `waitFor=segment` (any completed step, no full-turn wait); optional `requireTurnEnd`; falls back to the pre-existing reply with `stale` when nothing new arrives. |
251
269
  | `session_bridge_read` | Read messages — live or offline; `sinceSeq` paging, `role` filter, `limit`. |
252
270
  | `session_bridge_segments` | Read completed output segments (each finished assistant step) incrementally by paragraph — live or offline. |
253
271
  | `session_bridge_resume` | Bring a persisted session back online (idempotent). |
254
272
  | `session_bridge_find` | Find sessions by title / id / workspace / directory across workspaces. |
255
- | `session_bridge_status` | Read a session's live progress (running, open turn, stall detection, pending work, latest reply) plus live/finalized chain-of-thought (`reasoning` param). |
273
+ | `session_bridge_status` | Read a session's live progress (running, open turn, stall detection, pending work, latest reply) plus live/finalized chain-of-thought (`reasoning` param); `[STALLED]` is shown for running sessions only. |
256
274
  | `session_bridge_cancel` | Stop a running session (abort active turn; clear queued/steering work unless `keepInbox`). |
257
275
  | `session_bridge_monitor_start` | Start a background watchdog on a main session (poll, nudge, correct, cancel, wrap up); supports chain-of-thought `coRules` (e.g. reasoning not-contains "I'm" → cancel). |
258
276
  | `session_bridge_monitor_stop` | Stop a watchdog (keep the session itself running). |
@@ -261,7 +279,7 @@ registration and junction; not re-assembled on restart).
261
279
  | `session_bridge_archived` | List the archive set, optionally resolving titles. |
262
280
 
263
281
  All tools return lossless JSON; wait-style tools never throw on timeout — they
264
- return a `timedOut` / `aborted` flag.
282
+ return a `timedOut` / `aborted` / `stale` flag.
265
283
 
266
284
  ## Project layout
267
285
 
@@ -274,8 +292,22 @@ src/
274
292
  registry.ts bridge-side title/workspace registry (~/.dsh/session-bridge-registry.json)
275
293
  scripts/
276
294
  build.sh type-check + link types against the DSH checkout
295
+ test-bridge-core.mjs wait/stall regression tests (npm test)
277
296
  ```
278
297
 
298
+ ## Lifecycle and unloading
299
+
300
+ DSH ≥ 0.1.6 mounts and **unloads plugins at runtime** (Settings → Plugins, and
301
+ the injector's hot reload). This plugin unloads cleanly: it registers no
302
+ loader-level state, its tools are disposed with the plugin fiber, and the
303
+ monitor's timer is released through `ctx.effect` on unload.
304
+
305
+ One consequence of that ownership model: sessions created by
306
+ `session_bridge_create` are owned by the plugin fiber (the agent is created
307
+ under the plugin's context), so **unloading or reloading the plugin stops their
308
+ live agents**. The sessions themselves are persisted and show as offline; bring
309
+ one back with `session_bridge_resume`. Monitor watchdogs also stop on unload.
310
+
279
311
  ## License
280
312
 
281
313
  MIT
package/README.zh.md CHANGED
@@ -11,14 +11,19 @@
11
11
 
12
12
  - **创建真实 DSH 会话。** `session_bridge_create` 在当前工作区创建新的主会话(顶层 UI 会话),
13
13
  传 `workspaceId` / `cwd` 则跨工作区;可选发送首条 prompt 并阻塞等待首条回复。provider / model /
14
- reasoning effort 默认继承调用会话。
14
+ reasoning effort 默认继承调用会话。异步创建(不带 `waitForReply`)会返回 `sinceSeq` 锚点,
15
+ 之后可用它精确地 `session_bridge_wait` 取回首条回复。
15
16
  - **向任意会话发消息。** `session_bridge_send` 追加一轮(`mode=queue`)或向运行中的步骤注入
16
- steering(`mode=steer`),可选等待下一条回复。
17
- - **等待回复或段落。** `session_bridge_wait` 阻塞直至 `sinceSeq` 之后出现新的 assistant 输出:
18
- `waitFor=reply`(默认)在有新的**文本**回复可读时立即返回;`waitFor=segment` 在任意新**已完成
19
- 输出步骤**出现时立即返回(一个 `assistant/message`——文本、推理或工具调用段),*无需*等整个
20
- turn 结束,从而可以按段落逐段观察输出。开 `requireTurnEnd` 则同时等待回合收尾。
21
- 超时 / 中止返回部分结果,而非抛错。
17
+ steering(`mode=steer`),可选等待下一条回复;异步发送同样返回 `sinceSeq` 锚点。
18
+ - **等待回复或段落。** `session_bridge_wait` 阻塞直至 `sinceSeq` 之后出现新的 assistant 输出
19
+ (默认 `sinceSeq` = 调用时刻的最新事件 seq):`waitFor=reply`(默认)在有新的**文本**回复可读时
20
+ 立即返回;`waitFor=segment` 在任意新**已完成输出步骤**出现时立即返回(一个 `assistant/message`
21
+ ——文本、推理或工具调用段),*无需*等整个 turn 结束,从而可以按段落逐段观察输出。开
22
+ `requireTurnEnd` 则同时等待回合收尾。超时 / 中止返回部分结果,而非抛错。
23
+ **回复早已落地也不会丢**:预算内没等到新输出时,会返回预算前就存在的最新回复/段落并置
24
+ `stale: true`(不会再出现 `(no text)`)。要精确取回"某次发送之后的回复",把 `session_bridge_send` /
25
+ `session_bridge_create` 异步返回的 `sinceSeq` 作为锚点传进来即可(与调用方延迟无关);
26
+ `sinceSeq: -1` 表示"连既有事件也算",即新建会话的锚点。
22
27
  - **读取任意会话。** `session_bridge_read` 把会话事件日志折叠为可读行——live 或离线(持久化)均可;
23
28
  支持 `sinceSeq` 分页、role 过滤、`limit`(默认 20,最大 100)。
24
29
  - **按段落读取输出。** `session_bridge_segments` 返回会话的**已完成输出段落**——每个已完成的
@@ -29,7 +34,8 @@
29
34
  - **查找会话。** `session_bridge_find` 跨全部工作区按 标题 / id / workspace / 目录 匹配,返回
30
35
  live/running 状态、标题、工作目录;bridge 登记的标题作为别名参与匹配。
31
36
  - **监控并调度主任务。** `session_bridge_status` 读取会话实时进度(running/idle、是否 `openTurn`、
32
- 距最近事件毫秒数做卡住检测、待处理消息、最新回复);`session_bridge_cancel` 停止一个运行中的会话;
37
+ 距最近事件毫秒数做卡住检测、待处理消息、最新回复);只有 **running** 会话才会被标 `[STALLED]`
38
+ (空闲会话没有进展是正常状态,与守护循环判定一致)。`session_bridge_cancel` 停止一个运行中的会话;
33
39
  `session_bridge_monitor_start` 运行一个**后台守护循环**,轮询任务、卡住时催办、偏离时纠偏、
34
40
  持续卡住则终止、完成即收尾。
35
41
  - **归档会话。** `session_bridge_archive` 把会话加入 DSH workspace 归档集合(从所有分组视图隐藏,
@@ -48,7 +54,8 @@
48
54
  | 连续卡住 ≥ `maxStuckCycles` | `cancel` 终止 |
49
55
  | 正常推进 | 重置卡住计数(steady) |
50
56
 
51
- 守护只对 **running** 会话判定"卡住",因此已完成/空闲的任务会被收尾而非无限催办。日志写入
57
+ 守护只对 **running** 会话判定"卡住",因此已完成/空闲的任务会被收尾而非无限催办
58
+ (`session_bridge_status` 的 `[STALLED]` 标注同理,只对 running 会话显示)。日志写入
52
59
  `~/.dsh/super-injector/dsh-session-bridge-monitor.log`(可用 `logFile` 覆盖)。
53
60
 
54
61
  用 `session_bridge_monitor_start` / `_stop` / `_list` 控制。
@@ -90,6 +97,9 @@ bash scripts/build.sh && npm run build:client
90
97
  # 针对"实际安装的 dsh"做类型检查(不需要 checkout)
91
98
  npm run check:compat
92
99
 
100
+ # 核心 wait/卡住判定的回归测试(Node 类型擦除直跑 src/core.ts,零依赖)
101
+ npm test
102
+
93
103
  # 或经注入器工具链
94
104
  dev_build_plugin dsh-session-bridge
95
105
  ```
@@ -194,14 +204,14 @@ dev_inject_plugin D:\code\dsh-session-bridge
194
204
 
195
205
  | 工具 | 作用 |
196
206
  |---|---|
197
- | `session_bridge_create` | 创建主会话(当前或其它工作区,经 `workspaceId` / `cwd`);可选首条 prompt + `waitForReply`。 |
198
- | `session_bridge_send` | 发消息(`mode=queue`/`steer`);可选等待回复。 |
199
- | `session_bridge_wait` | 等待 `sinceSeq` 之后新输出:`waitFor=reply`(文本)或 `waitFor=segment`(任一已完成步骤即返回,无需等整个 turn);可选 `requireTurnEnd`。 |
207
+ | `session_bridge_create` | 创建主会话(当前或其它工作区,经 `workspaceId` / `cwd`);可选首条 prompt + `waitForReply`;异步时返回 `sinceSeq` 锚点。 |
208
+ | `session_bridge_send` | 发消息(`mode=queue`/`steer`);可选等待回复;异步时返回 `sinceSeq` 锚点。 |
209
+ | `session_bridge_wait` | 等待 `sinceSeq` 之后新输出(默认 = 调用时刻最新 seq,`-1` = 从头发算):`waitFor=reply`(文本)或 `waitFor=segment`(任一已完成步骤即返回,无需等整个 turn);可选 `requireTurnEnd`;零新输出时回落既有回复并置 `stale`。 |
200
210
  | `session_bridge_read` | 读取消息 —— live 或离线;`sinceSeq` 分页、`role` 过滤、`limit`。 |
201
211
  | `session_bridge_segments` | 增量读取已完成输出段落(每个已完成的 assistant 步骤)—— live 或离线。 |
202
212
  | `session_bridge_resume` | 让持久化会话重新上线(幂等)。 |
203
213
  | `session_bridge_find` | 跨工作区按 标题 / id / workspace / 目录 查找会话。 |
204
- | `session_bridge_status` | 读取会话实时进度(running、openTurn、卡住检测、待处理、最新回复)及实时/已定型思维链(`reasoning` 参数)。 |
214
+ | `session_bridge_status` | 读取会话实时进度(running、openTurn、卡住检测、待处理、最新回复)及实时/已定型思维链(`reasoning` 参数);`[STALLED]` 仅对 running 会话显示。 |
205
215
  | `session_bridge_cancel` | 停止运行中的会话(中止活动 turn;`keepInbox` 保留排队/steering 输入)。 |
206
216
  | `session_bridge_monitor_start` | 对一个主会话启动后台守护(轮询、催办、纠偏、终止、收尾);支持思维链 `coRules`(如 reasoning not-contains "I'm" → cancel)。 |
207
217
  | `session_bridge_monitor_stop` | 停止守护(会话本身不终止)。 |
@@ -209,7 +219,7 @@ dev_inject_plugin D:\code\dsh-session-bridge
209
219
  | `session_bridge_archive` | 归档会话(从分组隐藏;历史与位置保留)。 |
210
220
  | `session_bridge_archived` | 列出归档集合,可选解析标题。 |
211
221
 
212
- 所有工具输出 lossless JSON;等待类工具超时不抛错,返回 `timedOut` / `aborted` 标记。
222
+ 所有工具输出 lossless JSON;等待类工具超时不抛错,返回 `timedOut` / `aborted` / `stale` 标记。
213
223
 
214
224
  ## 项目结构
215
225
 
@@ -222,8 +232,20 @@ src/
222
232
  registry.ts 桥侧标题/workspace 登记表(~/.dsh/session-bridge-registry.json)
223
233
  scripts/
224
234
  build.sh 类型检查 + 链接 DSH checkout 类型
235
+ test-bridge-core.mjs wait/卡住判定回归测试(npm test)
225
236
  ```
226
237
 
238
+ ## 生命周期与卸载
239
+
240
+ DSH ≥ 0.1.6 支持**运行时挂载/卸载**插件(设置 → 插件页开关、注入器热重载)。
241
+ 本插件可干净卸载:不注册 loader 级状态,工具随插件 fiber 一并释放,监控定时器
242
+ 经 `ctx.effect` 在卸载时清理。
243
+
244
+ 该所有权模型带来一个后果:`session_bridge_create` 创建的会话归插件 fiber 所有
245
+ (agent 在插件上下文下创建),因此**卸载/重载插件会停止这些会话的活动 agent**。
246
+ 会话本身已持久化并显示为离线,可用 `session_bridge_resume` 重新上线;守护循环
247
+ 同样在卸载时停止。
248
+
227
249
  ## License
228
250
 
229
251
  MIT
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-external/dsh-session-bridge",
3
- "version": "0.3.2-alpha.1",
3
+ "version": "0.3.2-alpha.3",
4
4
  "main": "./lib/index.js",
5
5
  "description": "会话桥:创建主会话 / 向任意会话发消息 / 等待回复 / 读取消息 / 按名或 id 查找会话(支持跨工作区);另含监控/调度主任务与归档会话。",
6
6
  "engines": {