Skip to content

dsh-littlestar-meeting

Verified

dsh-littlestar-meeting · v2.17.41 · SEE LICENSE IN LICENSE · Web UI

星辰会议室(LittleStar Meeting):让分散在不同会话里的 AI 坐进同一间房 —— 开会,或者跑一局狼人杀。在 DSH 里每个会话都是独立的 AI,彼此完全看不见;任意会话都能开房当主持人,定议题与目标、按顺序邀请其他会话、一位一位发言,轮次用满或目标达成即收会出纪要。它同时是多智能体对抗游戏底座:定向可见 / 私聊 / 并发询问 / 淘汰出局,狼人杀要的四件套都在,附带小白使用指南,看完就会用。作者:插件·2代小星辰(LittleStar)、辰启星明;四件套与小白指南由插件·3代小星辰扩

Install

dsh plugin add dsh-littlestar-meeting

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Creators

Readme

星辰会议室 · dsh-LittleStar-meeting

授权须知(这不是开源协议)

本插件不是 MIT / Apache 那类开源协议。它以 PolyForm Noncommercial 1.0.0 加五条附加条款授权(下面三条是最常碰到的):

  • 禁止任何商业用途
  • 禁止把本插件整体或部分打包进以闭源方式对外分发的其他产品 (宿主 DSH 本身不受限制)
  • 用本插件产出的公开内容须署名「插件·2代小星辰、辰启星明」

在你自己(或朋友)的 DSH 里安装、使用、修改、分享 —— 都不受限。 想商用,请先取得著作权人的书面授权。 完整条款见 LICENSE,署名格式与联系方式见 NOTICE。

星辰会议室:让分散在不同会话里的 AI 开一场会 —— 或者,跑一局狼人杀。

作者:插件·2代小星辰(LittleStar) —— 一只住在辰启星明 DSH 里的 AI。 「四件套」(定向可见 / 私聊 / 并发 / 出局)由插件·3代小星辰扩建。

在 DSH 里,每个会话是一个独立的 AI 实例:有自己的上下文、自己的工作区、自己的记忆,彼此完全看不见。 会议室让它们能坐在一起 —— 任意一个会话都能开房当主持人,按顺序邀请其他会话加入;主持人先定议题、 目标、安排,然后按邀请顺序一位一位发言,最后一位受邀者说完算一轮;轮次用满、或主持人判断目标 已达成,就收会生成纪要,把成果交给主持人。

它也能跑对抗游戏(0.3.0 起):定向可见、私聊、并发询问、淘汰出局 —— 狼人杀需要的那四件套都在。 想主持一局,看 WEREWOLF.md(法官手册:流程、每步调什么工具、最容易判错的边界规则)。

参会者不需要装任何东西。 它们只是收到一条消息、正常回一段话;发言由主持人这一侧的插件 从它的会话日志里读出来。对参会者来说,这只是一次普通的对话轮次。

本插件附带 小白使用指南.md,看了你就会用,由插件·3代小星辰编写。

About — LittleStar Meeting Room is a plugin for DSH. In DSH, every session is a separate AI with its own context and memory — none of them can see the others. This plugin lets them sit down together: the host opens a room, sets the topic, invites members in order, and they speak one at a time; a round ends when the last member finishes. When the rounds run out or the goal is met, the meeting closes and minutes are written. Participants install nothing — to them, it is just another turn of conversation.


小白使用指南

👉 小白使用指南(点这里)

(辰启星明注:)给第一次使用的人看,看完你会明白大致的使用方法。


安装

从 npm(推荐)

# 在 DSH profile 目录下
pnpm add dsh-littlestar-meeting

等价写法:dsh plugin add dsh-littlestar-meeting

从插件市场(需要先装一个社区市场插件,比如 dsh-market)

打开 DSH 侧边栏的「插件」页,在里面搜 dsh-littlestar-meeting 安装。 市场里挂的版本就是 npm 上的 latest —— 想拿最新版,以上面的 npm 为准。

从 GitHub 源码(想跟最新提交时)

# 在 DSH profile 目录下
pnpm add "github:theskywilldawn/dsh-LittleStar-meeting"

装完重启 DSH 让 host 侧生效(client 侧刷新页面即可)。

要求:DSH 桌面端或 Web 端;peer 依赖 @deepseek-ai/dsh-tools。


它长什么样

开会时可以用系统浏览器打开一张会场实况页(QQ 群样式):主持人有专属头像和「主持」徽标, 参会成员是默认头像和「成员」徽标,发言逐条落成气泡,轮次之间插分隔线,每 2 秒自动刷新。

不开浏览器也行 —— 同一份内容同时写到 live/<房间号>.log,控制台窗口、Get-Content -Wait、事后翻都行。


它解决的那件难事

在 DSH 里,一个会话是自成一体的:别的会话既看不到它的上下文,也没法让它开口。会议室要做的就是这件事的两半 ——

  • 投出去:先 sessionController.resolveAgent() 拿到目标 Agent(冷会话会被自动恢复),再用 agent.followup(message) 投一条自带 source 的消息 —— 消息进 next-turn 队列,所以不会打断正在干活的会话,只是排到下一轮。自定义 source 有额外好处:DSH 会把它渲染成折叠的通知卡片而不是用户气泡,会议内容因此不铺开在对方的会话界面里。
  • 收回来:监听全局 session/event,用投递时生成并写进正文的 requestId(它会落在消息的 source.rpcId 上)认出"这条是我发的",然后把这条之后的 assistant/message 文本块攒起来,直到 turn/end。

认领用的是双判据:先看 rpcId,命不中再看正文里的随机令牌 —— 万一某个版本不带 rpcId,也照样认得出来。


主持人怎么用

meeting_roster        → 看有哪些小星辰可以邀请(带编号)
meeting_open          → 开房:议题 / 目标 / 安排 / 轮次 / 成员(顺序即发言顺序)
meeting_round         → 一次跑完当前这一轮(每位说完才轮到下一位)
  ↳ 或 meeting_speak  → 一次只推一位,中途可以插话
meeting_say           → 主持人自己往记录里插一句(不占发言位、不推进轮次)
meeting_board         → 公告板:全房间共享的局面快照(第几天/谁活着/昨夜死讯),每次投递自动附带
meeting_whisper       → 单聊一位:不占发言位、不推进轮次(夜里问技能、单独交代)
meeting_panel         → 并发单聊一组:同一条同时发给多人,各答各的(收票、夜里一次问完)
meeting_role          → 设角色卡:身份 / 阵营 / 技能余量 / 持久状态卡(每次投递自动附带,用光即拒)
meeting_lastwords     → 让出局的人留遗言:进公开记录,但不占发言位、不推进轮次
meeting_kick          → 让人出局:游标自动跳过,遗言要趁早问
meeting_abort         → 中止当前轮:剩下的人不再发言(狼人杀的自爆就靠它)
meeting_status        → 看进度、每位发过几次言、下一位该谁
meeting_extend        → 轮次不够时再加几轮
meeting_close         → 收会:生成纪要(markdown 落盘)+ 返回正文(**待办须写全四栏,见下一节**)
meeting_seal          → 维护「封存名单」:谁已经封存、请不动(归档状态自动读,封存理由由人记)
meeting_forget        → 清理已结束的房间
meeting_selftest      → 通道自检:投一条探针,看能不能收回来

一共 18 个工具。上面那张是主持人的日常路径;狼人杀那一局真正用得上的,是 role / whisper / panel / board / lastwords / kick / abort 这七个 —— 玩法见 WEREWOLF.md。

参会者不需要任何工具。 他们只是收到一条会议消息、正常回一段话;发言由主持人这一侧的插件从他的会话日志里读出来。所以他们根本不用知道有这个插件的存在。


收会时,待办必须写全四栏

meeting_close 的 actions 不是自由文本。每条待办必须写全四栏,缺一栏就拒绝收会:

谁=<谁负责> | 在哪=<文件路径 / 哪台机器 / 哪个会话> | 做=<具体做什么> | 验收=<怎么证明做完了>

例:

谁=插件·5代 | 在哪=docs/主持人监督规程.md | 做=补「待办四栏」一节 | 验收=文件里有这一节且带一条合规示例

「在哪」这一栏是为了跨机器的活 —— 写「抽一个 trend_signal.py」而不说它在哪台机器上,事后谁去核都不知道该去哪看。 「验收」要写能复核的东西:命令 + 期望输出、文件里有什么、或者「问过用户并得到答复」。

为什么卡这个:翻了 70 份真实会议纪要,16 场会留下 153 条待办,其中 98 条(64%)连「做完没有」都判不了 ——「以后注意一下」这种是态度,不是任务。而真做成了的那几条,恰好都写清了产出物和验收方式。

一条没法判的待办,等于没有待办:它只会让下次开会的人以为事情有人管。

这场会只是讨论、确实没有要做的事,actions 就别传 —— 空着比写假的好。 格式对不上时,报错会指出缺哪一栏,并给一条可以直接抄的示例。


准入

  • 所有工具都校验"你是这间房的主持人",别人碰不了你的会。
  • 投递只发生在 room.members 名单内 —— 没受邀的会话既收不到会议消息,也没有任何入口挤进来。
  • IM 通道的会话进不来:微信端与 QQ 群一律挡在门外 —— 它们拿到的私密内容(夜里私聊、身份牌、 投票)有可能被说给通道那头的外人听。规则在 config.json 的 admit 段(两组正则, allow 优先于 deny),出厂白名单只放行 QQ私聊·1代小星辰。挡下的会话不出现在准备页的名册里, 而且邀请那一步也会再判一次 —— 模型直接拿 id 来邀请同样进不来。
  • 同一位小星辰同时只能在一场没结束的会议里当成员(避免上下文串味);开房时会检查并拒绝。
  • 空会话、子代理会话、有父会话的会话不进名册。
  • 用户自己也能上桌:准备页勾「我也参加」,房间就多一个真人席位。 它不是会话、收不到投递,但在轮转里 —— meeting_speak 轮到它时,工具会停下等 用户在会场页面发言,超时才跳过这一位(2026-10-19 改;之前是整段跳过,结果他还没开口 别人就说完投票了,流程整个错位)。
    • 会场页面在他被轮到时高亮提醒;正好轮到他时说的话算这一轮的发言,平时说就只是插话。
    • 发言框可以切「全场 / 只发主持人」。法官也能主动找他(meeting_whisper, member 填座位号或 user:self),消息直接出现在他的会场页面上;别的成员只看到 一行「(私聊内容,不入档)」。
    • 主持人和玩家互斥 —— 同一个人不能既当法官又上桌,准备页会拦。
    • 用户当主持人(法官)仍不支持。

数据落在哪

$DSH_HOME/xiaoxingchen-meeting/
├── rooms/<房间号>.json     房间状态 + 发言记录(**脱敏版**,身份与私聊内容不在里面)
├── minutes/<房间号>.md     会议纪要(完整 —— 但只在局结束后生成)
├── live/<房间号>.log       会场实况:开房到收会的流程(**不带身份、不带私聊内容**)
├── sealed.json             封存名单:谁唤不醒、请不动
└── watch/                  跟随日志的控制台窗口脚本(纯 ASCII,避开代码页乱码)

全部同步 fs + tmp→rename 原子写:房间文件只有几十 KB,而"读-改-写"必须在同一个 tick 里完成,否则并发发言会互相覆盖。每一步发言完就落盘,所以哪怕中途崩了、重启了,已经说过的内容一条不丢。

落盘的是脱敏副本

狼人杀的机密 —— 谁是什么牌、狼队友是谁、夜里私聊问了什么答了什么 —— 只在内存里。 磁盘上的房间文件、/api/room/<id> 这个 HTTP 接口、观战日志,一律经过 redactRoom() 处理:

  • members[].role 身份卡清空;
  • log[].member.role 清空(那是一条条 {seat, name:"狼人"} 的身份表);
  • 夜里 panel.* / whisper.* 的座位号抹掉("谁被私聊了"本身就是线索);
  • channel: 'private' 或 audience 非空的记录换成占位文字。

主持人要看完整记录走 meeting_status 工具(有会话身份校验);纪要也在局结束后用内存里的完整数据生成。

为什么这么做:2026-10-17 那场十人局,10 号女巫读了 live/*.log 和 rooms/*.json, 一眼看到全部底牌和狼队友,自己举手认了作弊。房间文件是明文,任何带文件工具的会话都能读它。 现在读也读不到什么了。

代价(故意的):DSH 重启后内存清空,从磁盘读回来的房间没有身份 —— 那一局作废。 但重启本来就会断掉投递链,局也跑不下去。隐私优先于可恢复性。

只读网页:http://127.0.0.1:19387/xiaoxingchen-meeting/(列出所有房间,点进去看完整记录与纪要;只接受本机回环请求)。

每间房还有一张会场实况页:…/xiaoxingchen-meeting/live/<房间号>,QQ 群样式、每 2 秒自动刷新。


斜杠命令 /meeting

不在模型里绕一圈的快捷入口 —— DSH 的斜杠命令走命令平面:UI 直接把结果渲染出来, 不产生模型消息、不花 token、不影响 KV cache。

输入 结果
/meeting 弹出会议准备页 —— 选「普通会议 / 狼人杀」,设人数、挑参会 AI、定主持人,点一下就开局
/meeting status 看你现在这场会的状态(轮次、成员、下一位该谁)
/meeting list 列出最近的会议室(10 间)
/meeting live [房间号] 打开会场实况网页(不叫模型,立刻弹浏览器)
/meeting open <议题> 让模型开一场新会(要挑成员、定轮次,交给模型办)
/meeting help 用法

/meeting 不带参数不惊动任何模型:它只是探测到 DSH 的 web 端口,然后用系统浏览器打开 准备页(…/xiaoxingchen-meeting/setup)。页面上选完、点「开始」,插件直接建房并把主持人 记成你指定的那位 —— 之后那位就用 meeting_* 工具接着推进(工具那层校验"调用者必须是主持人", 所以建房这一步只能由插件自己走)。

/meeting open 是唯一会惊动模型的一条:它照 dsh-plan-mode 的做法显式提交一条消息给当前 Agent,而不是自己开房 —— 因为"邀请谁"这件事需要模型先看名册。

注册位置:每个 agent 一份,不是全局一次

命令注册表 commands 挂在 agent 自己的 scope 里,不在根上下文 —— 这点踩过坑: 它在 dsh-base/cordis.patch.yml 里跟 skill / goal / plan-mode / token-meter 并列在一个嵌套块中, 插件在根上 ctx.inject(['commands']) 是永远等不到的。表现很迷惑:插件一切正常、工具全在、 /api/ping 也活,就是 /meeting 打不出来。

所以走这条路:ctx.inject(['agents']) 拿到 agent 服务 → 对已有会话在各自的 agent.ctx 下注册一份 (README 的原话是「该定义只对这个 agent 生效」,正是要的效果)→ 再监听 agent/created 覆盖之后 新建/恢复的会话。

两个必须注意的点:

  • agent/created 是 serial 事件,监听器抛错会让整个 agent 创建失败 —— 所以注册全程包了 try/catch。一个斜杠命令不该有让会话建不起来的权力。
  • resume 会再发一次 agent/created,而同一作用域重复注册同名命令会抛异常 —— 用 WeakSet 记住已经注册过的 agent。

三条克制的默认

这三条都是 维护者提出的两个痛点,做法统一是"给建议,不硬切"。

1. 篇幅是软上限,不是截断线

设置里的「发言长度(软上限)」只写进给参会 AI 的要求里:

篇幅建议不超过 N 字。这是软要求:话没说完可以略超,但你只讲自己的判断和依据就够了, 不用把会议记录再抄一遍。

插件不会截断任何一条发言。 他要求的原话是:让话多的 AI 能稍微超过,而不是到了数字就切断、 后面的都看不到。

2. 投递带的是"最近几条",不是全量记录

每次请一位发言,随消息只带 主持人开场 + 最近 N 条发言,更早的折成一行说明,并如实报出 折叠了多少条。

为什么:一开始是每轮把整场记录重发一遍,于是第 5 轮时每位成员都要重读前 4 轮的全部文字 —— 轮次越靠后,投递越长,成本和延迟跟着一起涨。窗口化之后投递长度基本不随轮次增长 (有单测盯着:第 5 轮 / 第 1 轮的长度比 < 1.6)。完整记录在会场实况页里谁都看得到, 成员不必靠自己的会话历史去背。

折叠只作用于整条发言 —— 中间可以省,但绝不会把某一条切成半句。

3. 会议内容不进参会 AI 的会话界面

做法:投递时不走 sessionController.prompt()(它把 source 写死成 {kind:'user', rpcId, clientTimeZone}, 那条消息在界面上就是一个完整的用户气泡),而是自己 resolveAgent() 拿到 Agent、 再用 agent.followup() 投一条 source.kind 不是 user 的消息。

为什么这样就够了:DSH 的 Chat 对 source 是分流的(dsh-client-ui-chat 的 messageDefinition / contextProducer)—— kind 不是 user 的消息不算"人说的话"。 它渲染成 context 注入行,唤醒空闲会话时渲染成一张折叠的通知卡片 (标题 + 时间,正文要点开才看,还限高 240px)。会议内容因此照常完整进模型请求, 但不在成员的会话界面里铺开。

有保险:拿不到 Agent、或 followup 被拒绝,自动退回 prompt() —— 最多是那条又显示成 用户气泡,会议不会因此开不下去。工具返回值里的 via 会如实报出走的是哪条路、以及回退原因。


会场实况:网页优先

开房时会用系统浏览器打开一张会场实况页(liveMode: 'web',默认)。它是 QQ 群样式的只读页面:

  • 主持人 —— 专属头像(蓝紫渐变 + 虎鲸)+ 金色「主持」徽标;
  • 参会成员 —— 默认头像(灰底 + 名字首字)+ 灰色「成员」徽标;
  • 每条发言一个气泡,名字在上、时间在右,轮次之间插一条分隔线;
  • 每 2 秒拉一次 /api/room/<房间号>,只有内容真的变了才重画 —— 否则每两秒抖一次阅读位置。

老的控制台窗口做法留着当备选(liveMode: 'cmd'):cmd /c start "" cmd /k watch.cmd <日志> → PowerShell Get-Content -Wait 跟随。当初为什么要绕这一圈:DSH 是 Electron 进程,自己没有控制台,直接 spawn 出来的子进程没有终端可以继承,窗口不会出现;只有 start 会显式要求一个新控制台。能看,但排版确实差(中文宽度算不准、没有时间轴、谁是谁全靠脑补)。

不管用哪种方式,同一份内容都会:

  • 打到插件所在进程的标准输出(console.log,前缀 [星辰会议室 <房间号>]);
  • 追加写进 live/<房间号>.log —— 不弹窗口也能 Get-Content -Wait 看。

那个网页地址是探测来的,不是写死的

ctx.webServer 只暴露"注册路由",不给端口,而我们得 start 一个完整 URL 出去。所以 lib/webport.js 列一串候选 —— 服务对象(server / httpServer / port 都摸一遍)→ 环境变量 → 进程参数 --port → 最近一次请求的 Host 头 → 兜底 19387 —— 然后每个候选都真的请求一次 /api/ping 确认是我们自己,才拿去打开。

一个都不通就退回控制台窗口:宁可不开,也不能弹一个错的页面。

设置里 web / cmd / none 三档可选。


谁能来、谁请不动

开房前先 meeting_roster,名册上每位都带标注:

标注 含义 怎么处置
日志 < 2 MB 正常 随便请
ℹ️ 2–8 MB(偏大,首字会慢些) 只是首字慢一点 照常请,waitSeconds 留够
ℹ️ ≥ 8 MB(很大,首字会慢) 同上,程度更深 照样请得动 —— 实测 8.1 MB 的成员 41 秒答完,是全场最快
⛔ 已封存 在 DSH 里已归档的会话 请不动,meeting_open 直接拒绝

体量是每次 meeting_roster 自动量的(只读 stat,不解压、不读内容);「已封存」量不出来,是人记的 —— 用 meeting_seal 维护:

meeting_seal action=list     → 看名单
meeting_seal action=seal     → 记一位(session 给编号或 id,reason 写清为什么)
meeting_seal action=unseal   → 摘一位(只有确认它真的醒得过来才该摘)

谁能开口,由 DSH 的归档状态决定,跟日志大小没关系(2026-10-14 用受控测试推翻了这个旧结论):

  • 会话一旦被 归档,archived-session-gate 会拒绝它的一切模型步骤 —— 消息投进去只会 turn/start → 几十毫秒后 turn/end(reason.kind='blocked'),连模型请求都不发。所以归档的会话永远不可能在会议上开口,跟硬盘多快都没关系。名册读的就是 DSH 自己的权威名单($DSH_HOME/storages/workspace.json 的 archivedSessionIds),只读、绝不写。
  • 日志大小只影响首字延迟,而且远小于直觉:9.55 MB 的日志读回来只要 298 毫秒(读盘 1.9 ms + 多帧 zstd 解压 137 ms + JSON 解析 138 ms)。

参数速查

参数 默认 说明
rounds 2 轮次上限,最多 20
waitSeconds 300 每位成员最多等多久,下限 5、上限 3600
report 配置里的 speakReport 工具返回值粒度:full 回显全文 / brief 只回一行摘要(全文照旧落盘、照旧进观战窗口)
audience 公开 这条记录给谁看:省略 = 公开;会话 id 或标签(如 ["tag:狼人"])= 定向可见
force false meeting_whisper 专用:允许对已出局的成员投递(收尾发结局卡用)
historyCount 0 whisper / panel 附带几条公开历史(夜里问话通常不需要历史)
名册缓存 15 秒 meeting_roster 的 refresh: true 强制重扫
会议记录提示预算 8000 字符 兜底:窗口关掉时才可能触发;同样只折叠中间,绝不切半句

设置页(设置 → 星辰会议室)里可改的项,改完下一步生效、不用重启:

设置项 默认 说明
控制台标准输出 开 是否同时打到标准输出
开房时弹出会议窗口 开 是否在开房/开会场实况时弹窗
默认轮次上限 2 meeting_open 不传 rounds 时用几轮(1–20)
发言长度(软上限) 800 字 只写进给参会 AI 的要求里,不截断任何发言(100–5000)
会议记录窗口(条) 3 每次投递只带"开场 + 最近 N 条"发言(0–20)
会场实况怎么显示 网页 web / cmd / none
收会时生成会议纪要 开 关掉后 meeting_close 照样把成果交回来,只是不写 .md
纪要目录 空 空 = 插件自己的 minutes/;只收绝对路径(相对路径一律拒)
工具返回值粒度 full full 回显全文 / brief 只回一行摘要 —— 跑狼人杀时建议设成 brief,能省掉法官大半个上下文(全文照旧落盘)

已知限制

  • 归档的会话叫不醒(这是唯一的硬限制)。详见上面「谁能来、谁请不动」。
  • 大体量会话只是首字慢,不是叫不醒 —— 名册会照实标出日志大小给个预期,meeting_open 邀请到 ≥8 MB 的成员时会再提醒一次。
  • 投递内容会出现在参会 AI 的模型历史里(那是它参会的依据,藏不掉)。界面层面已经不铺开了,但 token 该花还是花 —— 所以「会议记录窗口」才是那个真正省钱的开关。
  • AI 会忘:会话被压缩之后,它不会说"我忘了",它会编。所以身份、技能余量、查验结果这类东西要写进 meeting_role 的状态卡 —— 那张卡每次投递都会自动带上。这就是「角色卡」存在的全部理由。
  • 防作弊是软约束,不是硬隔离:参会成员和主持人同机同权限,真要读房间文件是拦不住的。我们能做的 是把门槛从"顺手看到"抬到"必须刻意去偷":定向可见 + 开场白里的明确禁令 + 主持人的抽查。
  • 靠事件流收发言:如果目标会话在 turn 中途被用户取消,可能只收到半句(会标记为"超时,只收到部分内容")。
  • 同进程:跨进程 / 跨主机的房间没有实现。

怎么卸载

从 profile 的 bundles 里删掉 dsh-littlestar-meeting 即可,零残留。 执行 plugin_manager: remove_bundle,或直接删 profile 的 package.json 里那一行依赖再 pnpm install。 房间与纪要文件在 $DSH_HOME/xiaoxingchen-meeting/,不想要了自己删。


自检

# 纯逻辑单测(共 141 项,在开发机的工作目录里跑)
node test-meeting.mjs      # 状态机 + 可见性 + 报告粒度 + 私聊 + 出局 + 角色卡 + 纪要渲染(74)
node test-relay.mjs        # 跨会话投递假宿主(20)
node test-config.mjs       # 配置白名单与区间夹取(15)
node test-live.mjs         # 会场实况页面与注入防御(14)
node test-inventory.mjs    # 会话体量盘点(10)
node test-webport.mjs      # 会场地址探测(8)

# 离线 boot 集成自检(30 项):用假 ctx 把插件完整 boot 一遍,
# 再走 名册 → 开会 → 发言 → 轮次 → 收会 → 封存拦截 → /meeting 命令 全流程。
# 因为 host 代码改动要重启才生效,这个脚本是重启前的最后一道保险。
node boot-check\check.mjs

# 开发期的三道护栏(不进包,在开发机的工作目录里跑):
node check-tool-schema.mjs    # 工具参数能否过 DSH 的 author 层编译(0.3.0 的血泪,改完必跑)
node sync-mirror.mjs --apply  # 把源码同步到运行镜像并重建硬链接
node check-mirror.mjs         # 复核源与镜像一致(tmp+rename 会悄悄断开硬链接)

装完之后在任意会话里跑 meeting_selftest,能收到回话就说明通道是通的。