Bundle
@dsh-external/dsh-peer-mailbox
对话间信箱通信:peer_ask / peer_read_mailbox / peer_reply 宿主层工具,jsonl 信箱 + 原子写
- Source
- stark-L-1
- stars
- 1 stars
- License
- BSD-3-Clause
- Updated
- Updated 18 days ago
Readme
# @dsh-external/dsh-peer-mailbox
对话间信箱通信(toolkit + daemon-loop,host-only):让 DSH 的不同会话之间一问一答,
全程不切换会话、不互相占用(绕开 live 会话锁)、不落 skill 文件。
## 工作用途约束(防闲聊烧 token)
- **工具描述标注**:peer_ask / peer_reply / peer_read_mailbox 的描述均声明
「工作用通信通道,不是闲聊工具:只用于传达任务、问事、交付结果,不要寒暄、不回复问候/闲聊内容」。
- **轮数上限(硬约束)**:同一「发送方→接收方」有向对在滑动时间窗口内最多
`workLimitMax` 条(默认 4,建议 3~5)。超限时 peer_ask/peer_reply **拒绝投递**(不落盘),
返回 `{ok:false, limited:true, reason:"已达通信轮数上限…"}`。
- **自动重置**:滑动窗口(默认 1 小时)——窗口过期旧消息自动不计入,无需手动清零;
也可由管理员调整 `workLimitMax`/`workLimitWindowMs` 配置。
- **手动重置(管理员)**:`peer_reset_limit(from, to, scope?)`——给指定对(或 scope=all 整个目录)的
现有消息打 `limit_reset` 标记,统计时忽略它们,该对可立即继续投递、计数从 0 开始。
工具描述带醒目标注「管理员工具,除非接到明确命令否则不许使用」。
- 计数与投递在同一 per-file 锁内原子完成(并发投递不会超发);不同 (from,to) 对独立计数。
## 工具
- `peer_ask(target, message, from?)`:向目标会话的信箱投递一条消息(只投递),返回 `{ok,id,from,to,file}`;超限返回 `{ok:false,limited:true,reason,…}`。
- `peer_read_mailbox(from?, mark_read?, who?)`:读当前会话信箱;`mark_read` 缺省 true = 只看未读并标记已读,false = 看全部不动标记。
- `peer_reply(target, reply_to, message, from?)`:回复某条消息(写 `reply_to` 字段),本质同 peer_ask(同样受轮数限制)。
- `peer_reset_limit(from, to, scope?)`:**管理员工具,除非接到明确命令否则不许使用**。重置指定「发送方→接收方」的轮数计数(scope=pair 默认 / scope=all 整个目录),返回 `{ok,scope,from,to,reset_count}`。
## 唤醒层(daemon-loop)
- 定时轮询 `D:\dsh\peer-mailbox\mb-*.jsonl`(默认 10s,下限 5s),发现目标会话有**未读**消息时:
- 目标在 live registry(`ctx.agents.get(id)` 命中)→ `agent.followup()` 注入一条
`{kind:'plugin', form:'relay'}` 通知(与 dsh-chat-proxy 的注入方式一致),idle 司机开回合 / running 司机排队;
- 目标不在 live registry → 本轮跳过(信箱本身即队列,下轮再试),**绝不主动 attach/抢挂会话**。
- `peer_ask`/`peer_reply` 投递后默认立即尝试一次唤醒(`wakeOnAsk`,低延迟路径);轮询 daemon 是兜底。
- 已读不重复触发:`peer_read_mailbox` 标记 `read: true` 后不再通知;进程内 WakeState 记住已通知的消息 id,
同一条未读只投递一次通知(防 spam / 防死循环)。
- 唤醒目标必须用**真实会话 id**(名字只用于信箱可读性);且目标会话需已挂 agent preset(否则模型回合可能因
`{{cwd}}` 等模板变量缺失或权限策略被拒——生产会话天然满足)。
## 配置(插件 config)
```ts
{
mailboxDir?: string // 默认 D:\dsh\peer-mailbox
pollIntervalMs?: number // 默认 10000,下限 5000
wakeOnAsk?: boolean // 默认 true:投递后立即尝试唤醒
wakeEnabled?: boolean // 默认 true:启用轮询 daemon
wakePreviewChars?: number // 默认 160:唤醒通知里的消息预览长度
workLimitMax?: number // 默认 4:每对(from→to)窗口内最大投递条数;<=0 不限制
workLimitWindowMs?: number // 默认 3600000:滑动窗口 ms,窗口过期自动重置
}
```
## 构建与注入
```bash
bash scripts/build.sh # 需要 DSH_CHECKOUT(本机用合成 checkout C:\Users\86185\dsh-harness)
node scripts/test.mjs # 核心逻辑单测(21 项,含轮数限制与重置 9 项)
# 注入器环境内:dev_inject_plugin <本目录> → dev_reload_package dsh-peer-mailbox → dev_uninject_plugin dsh-peer-mailbox
```
## 消息格式(每信箱一个 jsonl,一行一条,UTF-8 无 BOM、原子写、进程内 per-file 锁)
```json
{"id":"<uuid>","from":"<发送会话>","to":"<目标会话>","text":"...","ts":<时间戳>,"reply_to":"<可选>","read":true}
```
## 已知限制
- 进程内 per-file 锁:同一 web 进程内并发写安全;跨进程(多 DSH 实例)不保证。
- 唤醒通知只是"提醒",目标会话是否真的调用 peer_reply 取决于其模型行为(实测可闭环,但非 100% 确定性)。
- 目标会话未挂载时只排队不唤醒(信箱即队列,挂载后下一轮轮询会补投递)。
Install
dsh plugin --profile web add github:stark-L-1/dsh-peer-mailbox
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-external-dsh-peer-mailbox from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.