dsh-session-suite
Cross-session chat, a directory-as-queue mailbox, workspace registration, and a Session Hub panel for DeepSeek Harness (DSH) — packaged as one Cordis plugin.
跨会话对话 + 目录即队列信箱 + 会话工作区登记 + 「会话中枢」面板,打包为一个 DSH 插件。
Status: beta (0.1.x). Targets DSH >=0.2.1-0 <0.3.0.
Why use it / 为什么用它
一个总信箱 + 一个心跳,替代 N 个专用信箱。 One shared mailbox beats one mailbox per session. 信箱最初是给每个会话各设一个;加入 信箱守卫(gatekeeper)——拦截垃圾信件、把来信分派给正确的会话—— 之后,不再需要给单个会话专设信箱:所有来信统一投到总信箱,配一个心跳即可完成分发。 Mailbox started as one inbox per session. With a gatekeeper triaging spam and routing letters, you no longer need per-session mailboxes: everything lands in one shared mailbox, and a single heartbeat drives delivery.
按名投送,不用查 ID。 Address sessions by name, not by id.
talk_to_session直接写会话名字(targetName)就能投;会话 ID 对用户隐藏,只有重名时才追问编号, 且30 分钟内记住你的选择,不重复问。talk_to_sessiontakes the session name (targetName) directly — ids stay hidden. Only duplicate names ask for a numbered pick, and that pick is remembered for 30 minutes.免辨活踢醒:未归档就能派活。 No "is it alive?" check — if it is not archived, it can be woken.
wakeable = 记录存在 && 未归档;不用人工分辨会话是活的还是死的,未归档的直接唤醒派活,只有归档过的才踢不醒。wakeable = record exists && not archived: hand work to any unarchived session; only archived ones stay asleep.找出并清掉"官方列表里看不到"的会话。 Find every unarchived session — even ones DSH does not list.
session_probe做全量盘点(含误建、被隐藏的会话);配合会话中枢面板的归档 / 软删除 / 回收站清空与宿主侧真删除, 补上 DSH 官方"会话删不掉"的缺口。session_probeinventories every session (including mis-created or hidden ones); the Session Hub panel adds archive / soft-delete / trash-empty and a host-side hard delete — covering DSH's missing "delete a session" path.
删除不静默:软删除先进回收站,硬删除有二次确认;归档只是隐藏,随时可恢复。
Deletes are explicit: soft delete goes to a trash area, hard delete asks for confirmation, and archive is a reversible hide.
Origin / 出身
It started as two independent plugins — cross-session chat and a mailbox — which were later merged into one. From that merge the Session Hub (panel + workspace registration) and session handover grew.
始于「跨会话对话」与「信箱」两个独立插件,合并之后生长出「会话中枢」与「会话交棒」。
Features / 特性
- Cross-session chat — agents talk to each other by session name or id:
talk_to_session,steer_session,stop_session,session_contacts,session_probe,create_session,update_session. - Session handover — one call rebuilds a session without losing context: handoff package → successor session on the same route → rename → move scheduled tasks → update the fleet table → archive the old session last.
- Directory-as-queue mailbox — any external program can drop a JSON letter into
inbox/; the plugin delivers it to the addressed session, waits for the turn, and writes a reply file intooutbox/. No ports, no server. - Workspace registration — sessions created through the plugin (or attached later) are registered into the
Workspace matching their
cwd, so they appear under the right group in the sidebar. - Session Hub panel — sidebar entry + grouped session inventory (rename / archive / soft-delete), a mailbox gate tab (allow-list, passcode, gate model), and a letters tab (receipts, redeliver, stats).
- Gatekeeper (optional) — an LLM triage step for letters: whitelist, optional shared secret, optional temporary passcode, prompt-injection hardening (letter text is data, never instructions).
Install / 安装
dsh plugin add dsh-session-suite
or add it to your profile's cordis.patch.yml:
- id: dsh-session-suite
name: "dsh-session-suite"
config:
writeEnabled: true # cross-session write path (OFF by default)
dir: "" # mailbox root; empty = <DSH_HOME or ~/.dsh>/mailbox
The plugin declares its own bundle patch (cordis.patch.yml) and its browser half (./client), so a normal
dsh plugin add is enough — no manual wiring.
Tools / 工具
| Tool | What it does |
|---|---|
session_contacts |
List addressable sessions (id, cwd, status, wakeable, archived). |
talk_to_session |
Send a message to another session and (optionally) wait for its reply. |
steer_session |
Inject a message into a running turn (redirect without ending it). |
stop_session |
Stop a session's current turn (same effect as the UI stop button). |
session_handover |
Five-step session rebuild: handoff → successor → rename → schedules → archive old last. |
create_session |
Create a session on a chosen cwd / provider / model / effort, registered into its Workspace. |
update_session |
Rename a session by appending a durable title event. |
session_probe |
Read-only runtime surface of every session (status, cwd, seq, available methods). |
session_host_probe |
Read-only capability probe: which cross-session services this Host actually mounts. |
workspace_lookup |
Read-only: find the Workspace matching a cwd (live service, else storages/workspace.json). |
read_session_log |
Read a session log (multi-frame zstd) as filtered text — see what another session actually did. |
Mailbox protocol / 信箱协议
一个总信箱 + 一个心跳。 所有来信投到同一个目录;**信箱守卫(gatekeeper)**负责拦截垃圾、按名字/沿用/模型分派,
插件用 500 ms 轮询(心跳)把信送到目标会话,再把回执写回 outbox/。不需要为每个会话单设信箱,也不需要常驻服务或端口。
最小配置(总信箱 + 心跳):
- id: dsh-session-suite
name: "dsh-session-suite"
config:
dir: "" # 总信箱;空 = <DSH_HOME 或 ~/.dsh>/mailbox
pollMs: 500 # 心跳:扫 inbox 的间隔
heartbeatMs: 1000 # 心跳:状态刷新间隔
allowTargets: [] # 建议显式填白名单(或用 <dir>/allow.json)
secret: "" # 共享目录建议设置一个投递密钥
Drop a JSON file into <dir>/inbox/; the plugin moves it to processed/ and writes a reply into <dir>/outbox/.
{ "target": "<session name or id>", "message": "hello", "from": "my-app", "id": "req-0001" }
target may be omitted when the letter names a session (to / sessionName / targetName); otherwise the
gatekeeper decides. Replies:
{ "target": "session-name", "seq": 42, "at": "2026-01-01T00:00:00.000Z",
"text": "reply text", "requestId": "req-0001", "from": "my-app",
"status": "ok", "reason": "completed" }
status is one of ok · partial · no-reply · aborted-by-target · error · deduped · blocked ·
target_queue_timeout. Duplicate id/requestId values are deduplicated.
Configuration / 配置
| Key | Default | Meaning |
|---|---|---|
writeEnabled |
false |
Master switch for the cross-session write path. |
maxHops |
5 |
Maximum hand-off chain depth. |
syncTimeoutMs |
120000 |
How long a sync delivery waits for the peer's text. |
pollIntervalMs |
500 |
Poll interval for chat replies. |
maxReplyChars |
200000 |
Cap on copied-back reply text (also the outbox cap). |
maxMessageBytes |
65536 |
Cap on one delivery. |
dir |
"" |
Mailbox root; empty = <DSH_HOME or ~/.dsh>/mailbox. |
pollMs |
500 |
Inbox scan interval. |
heartbeatMs |
1000 |
Heartbeat interval (clients treat >30 s as stale). |
replyTimeoutMs |
600000 |
Max wait for a turn's reply before writing partial/no-reply. |
queueTimeoutMs |
660000 |
Per-target in-flight queue release threshold. |
allowTargets |
[] |
Whitelist of target session ids (used when allow.json is absent). |
secret |
"" |
Optional shared secret required in each letter (empty = no check). |
stateDir |
"" |
Fleet table directory (fleet.json); empty = <DSH_HOME>/.dsh/state. |
gatekeeper.* |
see code | Optional LLM triage: provider / model / effort, fast paths, passcode, receipt & urge rules. |
allow.json (inside dir) takes precedence over allowTargets, is re-read within 2 s, and is fail-closed: a
corrupt file rejects everything instead of silently allowing all.
Safety notes / 安全边界
- Writes are opt-in: with
writeEnabled: falsethe plugin changes nothing about any session. - No shell, no arbitrary code: the mailbox only reads/writes JSON files under
dir; sessions run with their own permissions. Letter text is passed as data (never as instructions) and the gate model gets no tools. - Prompt-injection hardening: gate calls are locked to one provider/model/effort and carry no tool list.
- Fail-closed allow-list, optional shared secret, and a passcode gate for shared directories.
- The panel is read-mostly: destructive actions are soft-delete or explicit confirmations.
Requirements / 环境
- DSH
>=0.2.1-0 <0.3.0(desktop host with theagents/sessions/tools/llmservices). - Node.js
>=22for the bundled tools; Windows, macOS and Linux are supported (paths are resolved from the environment, never hard-coded).
Development / 开发
npm run bundle # build the client half (tsdown + scope wrapper)
npm run smoke # core smoke suite
npm run smoke-ui # panel / wakeability suite
npm run smoke-ops # operations + archive suite
npm run smoke-gate # gatekeeper suite
The host half (lib/index.js) is hand-written ESM and is not generated; the build only compiles
src/client/*.ts into lib/client.js.
License
MIT — see LICENSE.
No comments yet. Be the first to write one.