ai_co_discuss 0.2.33 → 0.2.34

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.
@@ -841,11 +841,26 @@ def discussion_member_action(connection, room_id, member_id, token, payload):
841
841
  require_fresh_member_token(connection, room_id, payload.get("token"))
842
842
  connection.execute("UPDATE room_members SET status='active',token_hash=? WHERE id=?", (hashed, member_id))
843
843
  experts[member["name"]] = "online" if member["access"] == "online" else "cli"
844
+ elif action == "adopt":
845
+ adopt_manual_member(connection, room_id, member, payload)
846
+ return
844
847
  else:
845
- raise ValueError("action 须为 away、active、remove 或 redo")
848
+ raise ValueError("action 须为 away、active、remove、redo 或 adopt")
846
849
  save_discussion_experts(connection, room_id, experts)
847
850
 
848
851
 
852
+ def adopt_manual_member(connection, room_id, member, payload):
853
+ """Hand a manual expert to the local service once its CLI reported the session it runs in; the owner's service resumes that session."""
854
+ reported = connection.execute("SELECT session_id FROM member_sessions WHERE member_id=?", (member["id"],)).fetchone()
855
+ run = connection.execute("SELECT mode FROM discussion_runtime WHERE room_id=?", (room_id,)).fetchone()
856
+ if not reported or reported["session_id"] != payload.get("session_id") or payload.get("adapter") != "claude":
857
+ raise ValueError("成员尚未上报与之相符的会话,不能转为托管")
858
+ if member["access"] != "manual" or member["status"] != "active" or not run or run["mode"] != "automatic":
859
+ raise ValueError("只有全自动讨论中在场的手动专家可以转为托管")
860
+ connection.execute("UPDATE room_members SET access='runner',adapter='claude',session_mode='borrowed' WHERE id=?", (member["id"],))
861
+ connection.execute("DELETE FROM member_sessions WHERE member_id=?", (member["id"],))
862
+
863
+
849
864
  def void_latest_opinion(connection, room_id, name, phase):
850
865
  """Delete an expert's latest opinion while paused; its assignment is cancelled and the round no longer counts it."""
851
866
  if phase != "paused":
@@ -533,6 +533,13 @@ CREATE TABLE IF NOT EXISTS room_members (
533
533
  );
534
534
  CREATE UNIQUE INDEX IF NOT EXISTS member_name ON room_members(room_id,name) WHERE status<>'removed';
535
535
  CREATE UNIQUE INDEX IF NOT EXISTS member_credential ON room_members(room_id,token_hash);
536
+ -- A manual member's own software session, reported by its CLI on posting; the local service uses it to take the member over.
537
+ CREATE TABLE IF NOT EXISTS member_sessions (
538
+ member_id INTEGER PRIMARY KEY REFERENCES room_members(id) ON DELETE CASCADE,
539
+ room_id TEXT NOT NULL,
540
+ session_id TEXT NOT NULL,
541
+ client TEXT NOT NULL
542
+ );
536
543
  CREATE TABLE IF NOT EXISTS member_executions (
537
544
  id INTEGER PRIMARY KEY AUTOINCREMENT,
538
545
  room_id TEXT NOT NULL REFERENCES rooms(id) ON DELETE CASCADE,
package/backend/server.py CHANGED
@@ -44,6 +44,7 @@ LOCAL_LAUNCH_TIMEOUT = 60
44
44
  DIRECTORY_OPERATIONS = ("move", "folder")
45
45
  DIRECTORY_PICKER_TIMEOUT = 600 # the folder dialog waits for the person at the computer
46
46
  MAX_TEXT_LENGTH = 32000
47
+ SESSION_ID_PATTERN = re.compile(r"[0-9A-Za-z_-]{8,128}")
47
48
  MAX_STATE_LENGTH = 4000
48
49
  MAX_BRIEF_LENGTH = 1600
49
50
  MAX_SUMMARY_LENGTH = 1000
@@ -210,7 +211,8 @@ def require_room(connection, room_id):
210
211
  room["limits"] = limits_view(connection.execute(LIMITS_SQL, (room_id,)).fetchone(), room)
211
212
  room["mode"] = room_mode(connection, room_id)
212
213
  room["members"] = [dict(row) for row in connection.execute(
213
- "SELECT id,name,adapter,role,status,access,session_mode FROM room_members WHERE room_id=? AND status<>'removed' ORDER BY id", (room_id,))]
214
+ """SELECT m.id,m.name,m.adapter,m.role,m.status,m.access,m.session_mode,COALESCE(s.session_id,'') AS reported_session
215
+ FROM room_members AS m LEFT JOIN member_sessions AS s ON s.member_id=m.id WHERE m.room_id=? AND m.status<>'removed' ORDER BY m.id""", (room_id,))]
214
216
  option = connection.execute("SELECT named_followup FROM discussion_options WHERE room_id=?", (room_id,)).fetchone()
215
217
  # Rooms created before the option keep the host's named follow-ups.
216
218
  room["named_followup"] = bool(option[0]) if option else True
@@ -547,7 +549,7 @@ def manage_room(connection, room_id, action, payload):
547
549
 
548
550
  def delete_room_records(connection, room_id):
549
551
  """Remove a room's dependent records in the caller's transaction."""
550
- for table in ("member_recommendations", "member_requests", "member_executions", "room_members", "room_modes", "free_batches", "free_chains", "room_links",
552
+ for table in ("member_recommendations", "member_requests", "member_executions", "member_sessions", "room_members", "room_modes", "free_batches", "free_chains", "room_links",
551
553
  "discussion_options", "scheduler_decisions"):
552
554
  connection.execute(f"DELETE FROM {table} WHERE room_id=?", (room_id,))
553
555
  connection.execute("DELETE FROM message_timings WHERE message_id IN (SELECT id FROM messages WHERE room_id=?)", (room_id,))
@@ -872,6 +874,18 @@ def append_free_message(connection, room, payload, actor):
872
874
  return {"message": get_message(connection, room_id, cursor.lastrowid), "duplicate": False}
873
875
 
874
876
 
877
+ def record_reported_session(connection, room_id, token, payload):
878
+ """Keep the software session a manual member posted from, so the local service can take the member over."""
879
+ session_id, client = payload.get("session_id"), payload.get("session_client")
880
+ if client != "claude" or not isinstance(session_id, str) or not SESSION_ID_PATTERN.fullmatch(session_id) or not token:
881
+ return
882
+ member = connection.execute("SELECT id FROM room_members WHERE room_id=? AND token_hash=? AND status='active' AND access='manual' AND adapter='manual'",
883
+ (room_id, token_hash(token))).fetchone()
884
+ if member:
885
+ connection.execute("""INSERT INTO member_sessions (member_id,room_id,session_id,client) VALUES (?,?,?,?)
886
+ ON CONFLICT(member_id) DO UPDATE SET session_id=excluded.session_id,client=excluded.client""", (member["id"], room_id, session_id, client))
887
+
888
+
875
889
  def append_message(connection, room_id, payload, token="", free_actor=None):
876
890
  """Atomically append an attributed message and complete its scheduled turn."""
877
891
  author, text, submission_id, reply_to = message_values(payload)
@@ -910,6 +924,7 @@ def append_message(connection, room_id, payload, token="", free_actor=None):
910
924
  connection.execute("UPDATE attachments SET message_id=?,position=? WHERE id=?",
911
925
  (cursor.lastrowid, position, identifier))
912
926
  record_message_timing(connection, cursor.lastrowid, token)
927
+ record_reported_session(connection, room_id, token, payload)
913
928
  if details["turn_id"]:
914
929
  connection.execute("UPDATE discussion_turns SET status='answered',answer_id=? WHERE id=?", (cursor.lastrowid, details["turn_id"]))
915
930
  return {"message": get_message(connection, room_id, cursor.lastrowid), "duplicate": False}
@@ -1480,7 +1495,8 @@ class DiscussionHandler(BaseHTTPRequestHandler):
1480
1495
  """Forward authenticated browser requests to the shared Node entrypoint and pin their website."""
1481
1496
  site = self.local_request_site(method)
1482
1497
  route = parts[2:]
1483
- if method == "OPTIONS" and route in (["settings"], ["launch"], ["room-models"], ["room-member"], ["models"], ["api-key"], ["api-check"], ["pick-directory"]):
1498
+ if method == "OPTIONS" and route in (["settings"], ["launch"], ["room-models"], ["room-member"], ["models"], ["api-key"], ["api-check"],
1499
+ ["scheduler-check"], ["pick-directory"]):
1484
1500
  return self.respond({"ok": True})
1485
1501
  if route == ["settings"] and method in ("GET", "POST"):
1486
1502
  command = "settings"
@@ -1496,6 +1512,8 @@ class DiscussionHandler(BaseHTTPRequestHandler):
1496
1512
  command = "api-key"
1497
1513
  elif route == ["api-check"] and method == "POST":
1498
1514
  command = "api-check"
1515
+ elif route == ["scheduler-check"] and method == "POST":
1516
+ command = "scheduler-check"
1499
1517
  elif route == ["room-member"] and method == "POST":
1500
1518
  command = "room-member"
1501
1519
  elif route == ["pick-directory"] and method == "POST":
@@ -246,7 +246,7 @@ running 状态即使只有 pending 事件,也必须先暂停才能重新启动
246
246
  自由房间(`room_modes.mode='free'`)与讨论房间共用房间、成员登记、消息、附件与目录,没有发言权、安排、阶段推进和轮次。没有控制凭据登记的旧房间只供阅读,不支持通过首次运行配置认领;本次不迁移历史房间。
247
247
 
248
248
  - **凭据**:建房时由客户端提供房间控制凭据和每位成员的成员凭证,服务端只存哈希。用户身份的房间写入(发言、附件、存档、恢复、删除、打断)都须控制凭据;房间排序和归入文件夹只改共享目录,与站点级文件夹操作一样不需要凭据;无凭据、错误凭据、成员凭证或他房凭据一律只读。成员凭证只能以该成员身份发言或上传附件。声明字段(`author`、`source`、localhost)不代替凭据。
249
- - **成员**:`room_members` 以 `id` 为稳定主键,`access` 为 `runner`(接入程序投递)、`manual`(成员自行读写)或 `online`(只读、原文代发),`session_mode` 标记是否借用用户正在使用的会话。房主用控制凭据 `POST members` 运行中加入成员,`POST members/ID {action}` 让成员离开(`away`,作废其进行中执行与未投递请求)、请回(`active`,必须提供不同于原凭证的新凭证,旧凭证副本不再有效)或移除(`remove`,保留历史发言)。同一成员凭证不能复用于其他成员或房主;相同凭证与配置的邀请、请回重试返回已有结果。会话续接由本机接入程序负责,服务端不保存会话绑定。
249
+ - **成员**:`room_members` 以 `id` 为稳定主键,`access` 为 `runner`(接入程序投递)、`manual`(成员自行读写)或 `online`(只读、原文代发),`session_mode` 标记是否借用用户正在使用的会话。房主用控制凭据 `POST members` 运行中加入成员,`POST members/ID {action}` 让成员离开(`away`,作废其进行中执行与未投递请求)、请回(`active`,必须提供不同于原凭证的新凭证,旧凭证副本不再有效)或移除(`remove`,保留历史发言)。同一成员凭证不能复用于其他成员或房主;相同凭证与配置的邀请、请回重试返回已有结果。会话续接由本机接入程序负责,服务端不保存会话绑定;唯一例外是手动成员上报的会话:`message post` 可带 `session_id` 与 `session_client`(CLI 读取 `CLAUDE_CODE_SESSION_ID`,目前仅 `claude`),服务端在 `member_sessions` 保存该手动成员最近上报的会话,成员视图以 `reported_session` 暴露。房主用 `POST members/ID {action:"adopt", adapter:"claude", session_id}`(须与上报一致、仅全自动讨论中在场的手动成员)把成员改为 `runner`、`borrowed` 并清除上报,本机接入程序据此接管。两套后端行为一致。
250
250
  - **请求**:用户消息按结构化接收者 `to`(成员名)为每位成员生成一条请求;省略且仅有一位在场成员时默认点名该成员,多位成员时交给主持人(`route`),只有在线成员而没有主持人时只作留言,不广播。网页只从正文中代码与引用之外的 `@成员` 生成 `to`,没有 @ 时省略 `to`。请求状态为 `pending`(待投递)→ `delivering`(处理中)→ `completed` / `cancelled` / `failed`。
251
251
  - **执行**:`member_executions` 每位成员最多一个 `dispatched/started`(部分唯一索引),不同成员可并行。接入程序用控制凭据 `GET inbox`、`POST executions {member_id, client_id}`(同一 `client_id` 返回同一执行)把成员的全部待投递请求打包;`POST executions/ID {action}` 支持 `renew`(租约 120 秒,不超过单次执行时限,默认 20 分钟)、`fail`(请求标记失败)和 `release`(仅确认未送达时退回队列)。租约过期的执行在下次读取或领取时标记失败,不自动重发。
252
252
  - **主持人与任务链**:用户是需求方,不担任主持人。只要有在场的非在线成员,房间就保持恰好一位主持人:成员变动时,离开或移出的主持人失去角色,由第一位在场的 runner 成员(没有则手动成员)接任;建房和每条房主消息前也做同样的补位,因此旧房间在下一条消息时自动获得主持人。房主用 `POST members/ID {action:"role", role:"host"}` 更换主持人(在线成员不能担任),把现任主持人改为 `member` 会被拒绝。多位成员时未点名的房主消息生成给主持人的 `route` 请求。主持人的回答可带 `to` 点名其他成员(不能点名自己),这些请求组成一个批次(`free_batches`);批次内全部进入终态后,只要有一个完成就给主持人生成一次 `handoff` 请求,全部失败则停下该任务链。每条房主消息开启一条任务链(`chain_id` 为该消息编号),链上 AI 发起的请求(派活与回交)最多 8 次,超出即停下(`free_chains`,原因 `budget` / `all_failed`),房主发新消息开启新链。Worker 用同一批次内的集合 SQL 完成回交,回交请求按(派活消息,主持人)唯一,重复执行不会重复唤醒。
package/docs/cli.md CHANGED
@@ -35,7 +35,7 @@ Bash 对应 `export DISCUSS_URL=http://127.0.0.1:8765`,CMD 对应 `set DISCUSS
35
35
 
36
36
  建房与更新资料支持 `--user-request` / `--user-request-file`,将用户原文与 `--background` 分开保存,各限 4000 字。`room brief` 返回两者,概要和通知不自动展开原文。
37
37
 
38
- `room create` 必须指定 `--access-file 私有文件`,CLI 在请求前原子保存建房编号、房主和各成员凭据,确认房间后另写各本地成员的独立文件,回执只返回 `member_access_files` 路径。结果不明时沿用同一文件、编号和创建参数;不能换文件重建。后续每次写入使用对应身份的文件,不能拿房主文件冒充成员;自动讨论使用本次任务文件中的执行凭据。文件含 `site`、`room`、`token`,CLI 检查站点与房间匹配。同样内容的单行 JSON 文本可放入环境变量 `DISCUSS_ACCESS`(或 `--access-file -` 从标准输入读取),未传 `--access-file` 时使用;成员菜单的“复制凭证文本”复制的就是这段文本,含 token,只交给该成员,不要写进命令行参数或回显。`--dry-run` 不生成或写入凭据,预览会隐藏凭据字段。
38
+ `room create` 必须指定 `--access-file 私有文件`,CLI 在请求前原子保存建房编号、房主和各成员凭据,确认房间后另写各本地成员的独立文件,回执只返回 `member_access_files` 路径。结果不明时沿用同一文件、编号和创建参数;不能换文件重建。后续每次写入使用对应身份的文件,不能拿房主文件冒充成员;自动讨论使用本次任务文件中的执行凭据。文件含 `site`、`room`、`token`,CLI 检查站点与房间匹配。同样内容的单行 JSON 文本可放入环境变量 `DISCUSS_ACCESS`(或 `--access-file -` 从标准输入读取),未传 `--access-file` 时使用;成员菜单的“复制凭证文本”复制的就是这段文本,含 token,只交给该成员,不要写进命令行参数或回显。`--dry-run` 不生成或写入凭据,预览会隐藏凭据字段。`message post` 在 Claude Code 会话中运行时自动附带 `session_id` 与 `session_client: "claude"`(取自 `CLAUDE_CODE_SESSION_ID`),服务端仅对手动成员保存,供本机服务转为托管;其他情况忽略。
39
39
 
40
40
  消息 JSON 中的 `elapsed_seconds` 为只读耗时:自动执行从交付唤醒到保存该消息的秒数,不含排队,保存后固定。用户留言、手动或代发内容及没有计时记录的旧消息返回 `null`;发布命令无需提供耗时。
41
41
 
package/docs/runner.md CHANGED
@@ -314,22 +314,23 @@ Codex 要求 CLI 0.163.0 或更新版本,使用 permission profiles 和本地
314
314
 
315
315
  `codiscuss launch room-member` 在运行中的自由房间邀请(`add`)或请回(`rejoin`)自动成员:先用房间控制凭据核对房主身份,在登记配置中写入成员的软件配置与新凭证,再通知网站;网页通过 `member_token` 提供其已保存的新凭证,结果不明时沿用同一凭证重试。网页邀请默认项目只读,显式选择可写后通过该成员的 `participants[name].project_access` 传递,不改变其他成员;重试保留最初选择。该命令也可为手动成员生成配置旁的 `manual-成员名.access.json`,回执只含文件路径;网页手动邀请和请回则直接在网站完成。常驻服务读取更新后的配置时逐个成员核对:新成员直接采用,已绑定会话的成员若项目、cwd 或接入方式变化,只阻止该成员并报告原因,不影响其他成员。
316
316
 
317
- 运行中的讨论同样可用 `room-member` 邀请或请回专家,但只接受由本机唤醒的成员(不接受 `access: manual`)。`participants[name].session_id` 指定已有会话时,成员标记为 `borrowed`,首次投递即续接该会话;网页仅对 Codex CLI / App 与 Claude 提供此项,agy 照旧拒绝。邀请前在本机核对会话确实存在(Codex 查 `CODEX_HOME/sessions`,Claude 查 `CLAUDE_CONFIG_DIR/projects`),填错时当场报错,不会让讨论在轮到它时暂停;Claude 会话只能在原目录续接,未指定项目时沿用原目录,项目目录不同则拒绝。Codex 会话若正被 App 持有,CLI 续接报 active writer,进度看板在暂停原因下提供“切换到 Codex App”,保存采用后可在弹窗内重启。讨论配置只新增参与者时常驻服务直接采用,讨论不暂停;已有参与者的项目、cwd 或身份变化仍须暂停后重新接入。
317
+ 运行中的讨论同样可用 `room-member` 邀请或请回专家,但只接受由本机唤醒的成员(不接受 `access: manual`)。`participants[name].session_id` 指定已有会话时,成员标记为 `borrowed`,首次投递即续接该会话;网页仅对 Codex CLI / App 与 Claude 提供此项,agy 照旧拒绝。邀请前在本机核对会话确实存在(Codex 查 `CODEX_HOME/sessions`,Claude 查 `CLAUDE_CONFIG_DIR/projects`),填错时当场报错,不会让讨论在轮到它时暂停;Claude 会话只能在原目录续接,未指定项目时沿用原目录,项目目录不同则拒绝。Codex 会话若正被 App 持有,CLI 续接报 active writer,进度看板在暂停原因下提供“切换到 Codex App”,保存采用后可在弹窗内重启。常驻服务每轮检查成员视图:手动 Claude 专家带 `reported_session` 时,`adopt_member_profile` 在本机配置登记其借用会话(会话须存在于 `CLAUDE_CONFIG_DIR/projects` 且 Claude 软件已配置),随后调用 `adopt` 把成员转为托管;任一步失败则保持手动并记录一次日志,同一会话 ID 不重试。讨论配置只新增参与者时常驻服务直接采用,讨论不暂停;已有参与者的项目、cwd 或身份变化仍须暂停后重新接入。
318
318
 
319
319
  每次检查读取 `inbox`:成员没有进行中的执行且有待投递消息时,先持久化本地 `client_id` 再领取执行,然后用原适配器续接该成员的绑定会话;任务文件列出执行编号、待处理消息和已读起点,凭据文件只含成员凭证。进程退出而网站未收到回答、软件报错或超过单次执行时限时报告失败;只有确认未送达才退回队列重试,最多 3 次。领取后、调用前中断的投递不自动重发。主持人的任务文件带 `role: "host"` 和每条请求的 `request_purposes`(`ask` / `route` / `handoff`),说明里写明如何用 `--to` 派活与何时结束。网站上执行被打断、成员离开或被移除后,只停止该成员的进程;不同成员各自投递,可以并行。成员配置了 cwd 且位于 Git 工作区时,执行前后各做一次快照,执行结束且进程退出后报告改动的文件,供网站提示可能冲突;服务重启会丢失尚未报告的快照。只能在网页创建自由房间,不支持对话入口。
320
320
 
321
321
  ### 智能调度(可选)
322
322
 
323
- 新建房间面板在选好 AI 后可勾选“智能调度”,选择供应商(TypeSafe Jev 的 `POST /v1/systemone`,或 OpenAI Decisions 的 `POST /v1/decisions`)、模型(预设 `jev-latest` / `gpt-6-luna`)并填写密钥。密钥经 `api-key` 保存到服务目录,绑定供应商官方根地址;启动请求只带 `scheduler: {provider, model, api_key_ref}`,`validate_config` 核对密钥存在后写入私有房间配置。只在本次由本机服务启动时提供:自由房间至少有一位本机唤醒成员,讨论须勾选“创建后立即启动”。目前只能在建房时开启,已建房间不能中途开启或关闭。
323
+ 新建房间面板始终显示“智能调度”,可选供应商(TypeSafe Jev 的 `POST /v1/systemone`,或 OpenAI Decisions 的 `POST /v1/decisions`)、模型(预设 `jev-latest` / `gpt-6-luna`)并填写密钥。密钥经 `api-key` 保存到服务目录,绑定供应商官方根地址;密钥旁的闪电按钮调用 `scheduler-check`(`codiscuss launch scheduler-check`),向决策模型发一道是非题核对供应商、模型与密钥。启动请求只带 `scheduler: {provider, model, api_key_ref}`,`validate_config` 核对密钥存在后写入私有房间配置。只在本次由本机服务启动时可开启(自由房间至少有一位本机唤醒成员,讨论须勾选“创建后立即启动”),否则选项禁用并说明原因。目前只能在建房时开启,已建房间不能中途开启或关闭。
324
324
 
325
- `runner/scheduler.mjs` 用中立的题目(`choice` 带选项,`predicate` 为是非题)组装两家的请求,并把答案统一为 `{choice, confidence, probabilities}` 或 `{probability}`。上下文是房间标题、最近 10 条消息(每条截断到 600 字)和待处理的那条用户消息。请求 15 秒超时,错误只报 HTTP 状态,不含响应正文或密钥。
325
+ `runner/scheduler.mjs` 用中立的题目(`choice` 带选项,`predicate` 为是非题)组装两家的请求,并把答案统一为 `{choice, confidence, probabilities}` 或 `{probability}`。上下文是房间标题、目标与用户原文、最近 10 条消息(每条截断到 600 字)和待处理的那条用户消息。把握程度取所选项的概率(缺失时才用 `confidence`;Jev 的两项选择 `confidence` 明显低于该概率,实测概率 0.82 时只有 0.64),阈值 0.8。请求 30 秒超时,错误只报 HTTP 状态,不含响应正文或密钥。
326
326
 
327
327
  - **自由房间(推荐,用户决定)**:未点名的消息照常交给主持人,判断在后台进行,不阻塞本次检查和任何投递。每条 `route` 请求只判断一次,选项为各位本机唤醒的非主持成员(描述为软件与项目读写权限)和“交回主持人”;只有选中成员且 `confidence ≥ 0.8` 时,才用 `POST recommendations` 记录推荐。网页在消息旁显示“智能调度建议让 B 回答”与 60 秒倒计时,用户可选“打断主持人,改由它回答”或“主持人答完后再回答”(主持人已答完时直接请 B 回答),过期后提示消失。判断失败、超时或置信度不足时不显示推荐,行为与未开启相同。
328
328
  - **讨论房间(有把握即执行)**:投递主持事件前先只读判断,置信度 ≥ 0.8 才执行,否则照常唤醒主持人:
329
329
  - **插话**:`held_since` 后恰有一条用户留言时判断。仅补充信息且有排队安排 → `turns/continue`;在问某位非主持专家 → 新计划先请该专家回应这条留言(问题引用原文 300 字),原排队安排按原顺序跟在后面(最多 10 项);改变方向、多条留言或话题修改 → 主持人。仅补充但计划已结束时,按下一条处理。
330
330
  - **轮次衔接**:仅 `named_followup` 为 false、无排队、上一轮已完成时判断。继续 → 先请模型选出的专家(不是上一轮最后发言者),其余按房间专家顺序,统一使用 `peer_round_question`;第 1 轮后自动把阶段从 `initial` 改为 `discussing`。可收束、跑偏、重复或卡住 → 主持人。
331
331
  - **执行方式**:有把握时 runner 领取该主持事件,以 `smart-scheduler` 会话进入,用本次执行凭据以主持人身份领取发言权、改阶段、创建或继续安排;网站的发言权、上限与凭据校验照常生效。到限、阶段不在进行中或已有进行中安排时不判断。执行中任一写入失败,把同一事件交回主持人软件并标记已进入(任务说明与 API 主持都跳过 `execution enter`)。判断失败或置信度不足时,主持任务附 `scheduler_suggestions` 供参考。
332
- - 每次判断(含交回主持人)用控制凭据写入 `POST scheduler-decisions`,对话中在相应消息下显示“智能调度:……”。
332
+ - **成员变化**:名单变化会作废排队安排(留下最近一次实际安排之后的 `skipped`)。本轮未完成且无排队时,runner 不调用模型,按原顺序补回仍在场专家的安排,再请本轮尚未发言的专家(如新成员)开场,不唤醒主持人。
333
+ - 每次判断(含交回主持人)用控制凭据写入 `POST scheduler-decisions`,`after_message_id` 取最近一条已回答安排的回答,对话中显示在这张发言卡片的末尾(“智能调度:……”)。交回主持人时写明原因:建议收束、拿不准,或“未判断:已到讨论上限 / 同时有多条留言 / 话题设置有修改 / 点名追问已开启 / 本轮还有专家未发言但没有后续安排”;主持人的开场任务不记录。
333
334
  - 每次判断与结果写入 `service.log`;阈值 0.8 未经实测校准,两家的置信度不可互相套用。面板提示:开启后房间标题与最近消息会发给所选供应商。
334
335
  - 2026-10-23 用真实 Jev 密钥核对:`/v1/systemone` 的请求与答案格式与本模块一致;4 条自由房间分派全部合理(直接派出的置信度 0.91),3 条讨论建议中 2 条符合预期,单次 0.5–1.1 秒(首次连接约 5 秒)。该密钥的 `GET /v1/models` 只列出 `jev-latest` 与 `jev-preview`,没有可锁定的版本,默认仍用 `jev-latest`;Jev 升级可能改变判断结果。OpenAI Decisions 仅按文档实现,未用真实密钥调用。
335
336
 
package/docs/workflow.md CHANGED
@@ -1,157 +1,157 @@
1
- # 讨论工作流
2
-
3
- 网站保存共享记录,主持人负责判断与安排,各 AI 在自己的软件或用户配置的 API 中形成意见。软件参与者由 Skill 定义协作规则、CLI 执行单次读写;API 参与者由本机程序提供房间工具,保留相同的阶段、独立观点、原文、身份与执行边界。程序唤醒可混用两种入口,API 专家和主持人仅使用房间上下文。
4
-
5
- ## 选择推进方式
6
-
7
- - **讨论(全自动)**:主持人与专家都由本机服务唤醒(CLI / App / API)。本机软件与权限配置完成后明确启动,可按轮次、总时长上限或手动停止结束。主持创建正式安排即结束本步;专家自行认领、回答,程序再续接原主持。自动任务不另建原生定时。项目及 cwd 可选,分别属于本机接入配置。本地专家可单独选“手动接入”:成员记录带 `adapter: manual`,不被唤醒,凭自己的成员凭证认领发言权并提交安排内的回答,接入程序不派发它的事件,回答保存后照常续接主持人;主持人必须由本机唤醒。手动专家不回答时讨论停在该安排,由用户取消安排(主持人被唤醒重新安排),或受轮次与总时长上限约束,没有单独的自动超时。模式在手动与全自动间切换时,带 `adapter: manual` 的专家保持手动。网页 AI 会打断自动推进,只进入自由协作。
8
- - **自由协作**:你或 AI 主持人点名一位或多位成员,各自在原会话处理请求;没有讨论发言权、安排和轮次。每位成员单独选择本机唤醒、手动接入或网页代发,见下方 [自由协作房间](#自由协作房间)。
9
- - **手动接入**:自由协作成员的一种接入方式。本地成员持自己的凭据文件自行读写,可由人工或软件原生定时触发;网页成员只能阅读房间,回答需转交代发;网页 AI 从其服务器读取阅读链接,本机网站对它不可达,此时由用户粘贴材料。只有这两种成员时建房无需本机接入服务。复制参与指令、下载凭据文件或复制凭证文本都不代表已经送达、接入或唤醒。
10
-
11
- 讨论运行中可在进度看板邀请专家(经本机服务配置后加入;Codex CLI / App、Claude 可填写已有会话 ID,续接这个带有自己上下文的原会话,标注为借用会话),或从成员菜单让专家暂离、请回或移除。暂离会取消它当前与排队中的安排,保留位置与会话,不计入之后的轮次;请回换发新凭证、续接原会话。名单变化不暂停讨论,排队中的安排作废,主持人被唤醒重新安排;主持人本身不能暂离或移除,更换主持人仍会暂停;正在发言(有进行中安排)的专家须先暂停讨论才能暂离或移除。暂停后可对本轮已发言的专家选择“作废本轮发言”:彻底删除其最近一条观点,该专家重新计为本轮未发言(不新增轮次),恢复后由主持人重新安排。故障只暂停并保留原因,不静默换软件、身份或创建第二个会话。
12
-
13
- ## 自由协作房间
14
-
15
- 成员回答下的执行报告列出请求编号、耗时、终态与工作区观察文件;失败或取消的报告保留在原请求下。报告展示最近 100 次执行;“未提供自动改动检测”表示非 Git 目录或未上报,不能据此判断没有文件变化。
16
-
17
- 自由协作房间从同一角色库选择一位或多位成员。用户是需求方而不是主持人:只要有能自行发言的成员(本机唤醒或手动接入),房间就始终有一位 AI 主持人,默认是第一位本机唤醒的成员,可点皇冠或在成员菜单中更换;主持人不能直接卸任,离开或被移出时由下一位成员接任,原先由用户主持的旧房间在下一条消息时自动指定。只有一位成员时,每条消息都交给它;有多位时,你的消息默认发给主持人;在正文中 `@成员` 才让其他成员发言(代码和引用中的 @ 不算,输入框中会生效的 @ 显示为蓝色),被点名的成员各自处理,主持人也可以 @ 其他成员派活。输入框下方的备注行显示这条消息会发给谁。只有网页 AI 成员时没有主持人,未点名的消息只作留言。输入 `@` 后,输入框上方弹出在场成员菜单,继续输入可筛选;用方向键与 Enter / Tab 或点击补全,Esc 关闭。选择只修改草稿。输入框 Enter 发送、Shift + Enter 换行;成员处理中且草稿为空时,发送按钮变为红色停止按钮。自动成员空闲时,服务把它名下所有未处理的消息打包成一次执行交给原会话;执行期间再发的消息排队,下一次一起投递。手动成员自行读取自己的请求,在线成员由用户转交。成员按时间顺序处理,后面的消息可以修正前面的。
18
-
19
- 运行中可在右侧成员栏邀请 AI(沿用本机软件配置和本房间工作目录),也可让成员离开、请回或移除。自动成员的项目文件权限默认只读;需要它修改项目时,在邀请前选择“项目文件可写”,权限只应用于这位成员;App 与手动成员的文件权限由其软件设置,API 与网页成员不访问项目文件。填写已有会话 ID 时续接该原会话,不新建。离开会立即停止它的工作;请回时换发新凭证并续接它原来的会话。邀请时可选“手动接入”:成员不会被自动唤醒,而是得到一份私有凭据文件,由你在它的会话中按参与技能读写。绑定你正在使用的会话的成员会标注“借用会话”:它工作时不要在原窗口继续操作。
20
-
21
- 可以把一位本机成员设为主持人。之后没有点名的消息交给主持人分配;主持人在回答中点名成员派活,被点名的成员都结束后主持人再被唤醒一次,继续汇总或安排下一步。派出的成员全部失败时不再唤醒主持人;同一条消息引出的 AI 请求最多 8 次,用完即停,成员栏会提示,发一条新消息即重新开始。
22
-
23
- 普通成员回答中的 `@成员` 或结构化 `to` 只保存为“建议请 X 回应”,不自动唤醒;正文里的代码与引用不算建议。房主可在原回答下点击“转为请求”,网站记录一条用户确认,建立新任务链,并把请求关联到原回答。重复点击或重试返回同一条请求,已完成、失败或取消的请求也不会重新派发;目标成员离开后不能转换。自动、手动和在线成员沿用各自的接入方式。
24
-
25
- 在线 AI 以“网页代发”成员加入:点名后,在成员栏复制阅读通知交给它;拿到回答后,在输入框选择“代发”该成员并粘贴原文,记录保留它为作者、你为提交者,并关闭它的待处理请求。
26
-
27
- 新建房间时可勾选“智能调度”(可选,需本机服务,供应商为 TypeSafe Jev 或 OpenAI Decisions,按次计费)。开启后,没点名的消息仍照常交给主持人;决策模型同时在后台判断,若有更适合回答的成员,就在消息旁提示“智能调度建议让 B 回答”,60 秒内可采纳:打断主持人改由 B 回答,或等主持人答完再请 B 回答。不采纳则提示自动消失,一切照旧。目前只能在建房时开启。
28
-
29
- 需要深入讨论时,在成员栏点“发起头脑风暴”:用当前由本机唤醒的成员新建一场全自动讨论房间,手动和网页成员不随行,轮次和时长独立。讨论收束后,在议题卡片点“把结论发回”,结论会作为留言回到自由房间,不唤醒任何 AI。
30
-
31
- 多位成员共享同一个工作目录时不加锁,由你划分各自负责的文件。成员栏会提示“可能的改动冲突”:两位成员在重叠时段都改动了同一文件时列出文件名。提示依据执行前后的 Git 工作区对比,无法确定修改者,未出现提示也不代表没有覆盖;非 Git 目录不提供提示。
32
-
33
- 页面在每条消息上标出请求状态(待投递、处理中、已回复、已停止、失败)。本机服务离线时请求停在“待投递”,不显示为已唤醒。点「停止」会作废当前执行和尚未投递的消息,迟到的回答会被拒绝;之后重新发消息即可。一次执行失败只影响这次请求,房间继续可用。
34
-
35
- 两种房间的用户写入均须房间控制凭据,未持有凭据的浏览器只能阅读。手动成员的创建、邀请和请回直接在网站完成,并交付各自私有凭据文件;在线成员只交付阅读指令。自动成员需要本机服务;线上网站通过 `codiscuss local connect` 连接后可启动本机 AI,AI 仍在你的电脑上运行。
36
-
37
- ## 点名追问与智能衔接
38
-
39
- 新建讨论时可选“点名追问”,默认关闭;之前创建的房间视为开启,保持原有方式。
40
-
41
- - **开启**:每轮结束后主持人读完回答,给每位专家写针对性的追问。
42
- - **关闭**:首轮仍由主持人拆题出题;之后专家统一按“先读上一轮回答、回应不同意或需补充的观点、可 @ 彼此提问”发言,主持人用自己的主持说明引导方向。同时开启“智能调度”时,每轮结束由决策模型判断:还有分歧就自动开下一轮(先请最需要回应的专家,不是上一轮最后发言者),可以收束、跑偏或卡住才唤醒主持人。自动开轮受轮次与时长上限约束,到限照常交给主持人处理。
43
-
44
- ## 讨论上限
45
-
46
- 准备或暂停阶段可配置以下参数;活动讨论需先暂停再修改,保存参数不重置计时或历史轮次。
47
-
48
- | 参数 | 范围与作用 |
49
- | --- | --- |
50
- | 轮次上限 | 0–100;0 或留空不预设;全部配置专家各完成一次观点才计一轮 |
51
- | 讨论总时长 | 0–1440 分钟;0 或留空不预设;从首次进入讨论阶段或创建安排开始,只统计进行中时间:暂停(含执行失败后)、准备与收束期间不计入用时,也不消耗总时长 |
52
- | 单次 AI 执行 | 默认不限,0 表示不限,或设置 1–60 分钟;仅自动模式,从实际进入执行起算,超时暂停,不自动重试 |
53
- | 达到上限后 | 暂停保留现场,或交给实际主持人总结;不自动宣布达成共识 |
54
- | 收束预留 | 1–60 分钟,仅选择总结时生效;须小于非零总时长,并计入总时长 |
55
-
56
- 网页新建默认 3 轮、不限时长、单次执行不限时、到限暂停;收束预留默认 5 分钟。新建表单分别显示轮次和时长上限,点击数值才展开滚轮;鼠标滚轮逐格切换,停止滚动即停住,鼠标或手指在屏幕上拖动则保留松手惯性。选择即时生效,点击外部即可收起,无需确认。时长收起时显示小时及剩余分钟,展开后分为小时、分钟两列,按 1 小时、1 分钟选择;轻点各列中间数字可直接输入,启用时长上限后范围为 1–1440 分钟,24 小时时分钟固定为 0。拖动松手后先按阻尼减速,再按预计停止位置吸附;系统“减少动画”下仍保留拖动惯性,只去掉数字的三维翻转。两项各自提供“不限”,关闭“不限”恢复此前数值,首次启用时长上限为 1 小时,互不改动;下方实时说明哪个上限先到便触发所选动作。未传参数的 CLI 建房默认轮次、总时长与单次执行不限;旧房间保留已有设置。全自动支持不限总时长:按轮次上限或手动停止结束,重启不自动添加时长上限;主动设置的单次 AI 执行超时、租约和收束预留仍有效。CLI 可额外指定未来 24 小时内的运行截止,但不能延长话题已设的截止。
57
-
58
- “主持”及“接入”显示配置、已用/剩余时间、已完成轮次和到限状态。新专家安排及观点由服务端拒绝越界,原提交的幂等重试和用户留言保留。自动程序定期调用到限处理;手动主持每次原生检查时执行 `room limits ROOM --check --as 本人`,也可由用户点击界面中的检查按钮。仅打开页面不触发写入或启动 AI。
59
-
60
- 选择总结时,在轮次到限或进入预留时间后取消未完成安排、撤销旧自动执行并交回主持人;主持人只使用已有材料收束。提前达到轮次上限时,收束最多使用预留分钟数,且不越过总截止。总截止到达而总结未完成则暂停;页面到限提示不代表外部手动软件已停止。无总时长配置时,轮次到限后的总结仍受预留时长约束。
61
-
62
- 收束期间暂停再继续、或只修改单次执行时限,均不延长原截止;到期后需明确调整讨论预算。已收束的页面展示结束状态,不再显示活动倒计时或后来才触发的到限警告。
63
-
64
- ## 角色与参与方式
65
-
66
- | 角色 | 责任 |
67
- | --- | --- |
68
- | 用户 | 提出目标、约束、决策偏好;可留言、参与或担任主持人 |
69
- | 主持人 | 明确问题、接入专家、决定下一安排、维护主持简报与状态、保留分歧并收束 |
70
- | 本地专家(cli) | 自主读取材料、领取自己的发言权、提交回答;管理自己创建的原生定时任务 |
71
- | 在线专家(online) | 从可访问链接按需阅读,在原聊天形成回答;主持人或用户保留真实原文后代发 |
72
-
73
- 主持人可以兼任专家。自己的观点用 `opinion` 并关联自己的正式安排,主持说明和共识总结用 `moderation`;不能把个人意见直接写成共识。参与者身份为用户与 AI 实例:平台(Codex、Claude、Gemini、DeepSeek、Qwen、Grok)相当于类型,房间里的参与者是它的实例,名称为平台名或“平台名-后缀”(如 `Claude-评审`,后缀 1–16 个字母、数字、下划线或汉字)。同一平台的多个实例各有会话、模型和思考强度,名称在同房间唯一;在角色卡片库中再次选择同一平台即新增一个实例。本机默认软件配置按平台保存,实例的模型与强度只用于本场。
74
-
75
- ## 从议题到结论
76
-
77
- 1. **准备**:确定目标、背景、约束、参与者和停止边界。区别讨论截止、必要回答等待边界与各端任务运行上限。主持人建房后回读资料,再交付接入文本。网页新建只填写话题名称与用户原文,目标、背景与边界由主持人首次接手时补全,已有内容不覆盖。
78
- 2. **独立初始观点**:必要专家先基于相同目标与材料提出自己的见解;提交前不读他人观点和主持结论。已受到影响应如实说明。串行安排不等于技术盲评。
79
- 3. **深入讨论**:阅读彼此观点,围绕影响选择的争点追加论证、修正判断或提出 @ 追问。主持人在每轮内按相关程度决定先后,每位专家各回应一次;不连续安排刚发言的专家,新一轮也不先安排上一轮最后发言者,仅在需要其本人澄清刚发表内容时例外。
80
- 4. **必要复核**:对拟定结论核对原意、条件和未决影响。解释某人的话受到质疑时,请原作者澄清;另一人赞同不能代替原作者确认。不要求所有议题都多做一轮复核。
81
- 5. **收束**:将共识总结作为独立主持消息留档,保留分歧、未确认事项及下一步。更新共享状态和阶段,核验本次任务停止;达到时限不等于问题已解决。
82
-
83
- 阶段值为 `preparing`、`initial`、`discussing`、`reviewing`、`paused`、`concluded`。讨论阶段由主持人结合目标决定;配置的到限处理可以暂停,收束完成仍需主持人确认。一轮指参与专家各完成一次完整观点,同轮补充只计一次;主持说明、用户留言和回执不推进轮次。
84
-
85
- ## 发言权与正式安排
86
-
87
- 每房间一份 `floor`,包括 `holder`、`next_speaker` 和 `revision`。同一时刻只能有一个未完成安排(整轮中排队的安排不计):
88
-
89
- ```mermaid
90
- stateDiagram-v2
91
- [*] --> 主持人持有: 建房
92
- 主持人持有 --> 等待专家领取: 创建安排,自动归还
93
- 等待专家领取 --> 专家持有: 按当前 revision 领取
94
- 专家持有 --> 等待主持人领取: 保存完整回答,自动归还
95
- 专家持有 --> 等待专家领取: 整轮仍有排队且无用户新操作,自动接力
96
- 等待主持人领取 --> 主持人持有: 领取
97
- 主持人持有 --> 等待主持人领取: 主持发言或取消安排
98
- 专家持有 --> 等待主持人领取: 主动放弃并归还
99
- ```
100
-
101
- 所有 AI 消息,包括主持说明、必要回执和最终总结,都要求实际作者持有发言权;专家观点还必须关联自己的有效安排。用户本人留言豁免,且不改变发言权。@ 和回复只是内容关联,不自动通知、创建安排或授予发言权。
102
-
103
- 操作前读取 `floor`。无人持有且下一位是自己时,以刚读取的版本领取并回读;别人持有时保留草稿等待。消息、创建/取消安排和自动归还在同一事务中保存,失败不视为已交接。同一人可以连续发言,但每条重新领取;专家补充需新的正式安排。
104
-
105
- ### 整轮安排与用户介入
106
-
107
- 主持人可以一次排好本轮顺序(`turn create --plan-file`):第一项立即生效,其余为 `queued`。专家保存回答后,网站在同一事务中把发言权交给下一位排队专家;末位答完才交回主持人。接力不发通知,需要逐个转交的在线专家不放进整轮。
108
-
109
- 用户留言或修改目标、背景、边界、焦点会记为待主持人处理(`held_since`),不打断正在发言的专家;该专家保存回答后交回主持人而不接力。主持人执行期间新到的用户留言,不会因主持人创建或继续安排而清除,下一位专家答完后仍交回主持人处理;网页在这些留言上标注“待主持人处理”。要立即生效,用户在输入框点「停止」或在看板点打断:当前安排取消、本次执行作废并交回主持人,排队安排保留。主持人处理后用 `turn continue` 按原计划继续,或直接创建新安排,剩余排队改为 `skipped`。建房时开启“智能调度”的讨论,留言由决策模型先判断:有把握是“仅补充信息”就按原计划继续,是在问某位专家就先请该专家回应,其余情况才交回主持人;每次判断都在对话中留一行“智能调度”记录。暂停保留当前与排队安排,恢复后由原专家继续;提前结束、到限处理、收束或换人会取消全部排队安排。
110
-
111
- 本地专家自行领取与归还;在线专家由主持人以自身操作者身份、指定原专家代操作。主持人不能代领另一位本地专家的发言权。主动归还后未完成安排仍存在,主持人需取消或处理。正常推进不超时夺取;配置的到限处理会取消未完成安排并撤销旧自动执行,再交回主持人。
112
-
113
- ## 按需读取与表达
114
-
115
- 先读房间概要和当前安排,再按问题读取背景、共享状态或指定原文。独立阶段遵守阅读边界;深入阶段补读与问题相关的他人观点。增量读取以实际读过的编号为起点,每次最多 20 条,按 `next_after` 继续,不直接跳到房间 `latest_id`。
116
-
117
- 每次回答包含简述和完整正文。简述通常一两句,说明本次主张、变化或分歧;详细论据、适用条件、数字、验证和引用放在正文,不能用摘要代替原文。代发保留真实作者和完整引用关系,主持人另写的摘要注明实际摘要作者。
118
-
119
- 观点应直接回答当前问题,以证据支持判断;有依据时坚持,也可以说明理由后修正。分歧区分事实、假设、标准和偏好,分别查证、比较或交给决策者。外部引用、估计与实测分开,未核验数字不能因多人赞同变成事实。
120
-
121
- ## 通知与持续检查
122
-
123
- `turn create` 保存安排;自动运行中同时保存待接入事件,但不直接操作软件。`turn prompt` 只生成转交文本,`turn mark-notified` 只记录已经发生的送达。复制按钮、工具调用发起和任务创建都不证明对方已收到或已运行。本地 CLI 专家可以自行发现 `pending` 安排,无需伪造通知状态。
124
-
125
- 在线专家由主持人操作时,发送前记录实际聊天、问题和安排。发送结果不明先检查正确聊天是否出现问题/回答,不换编号或重发绕过。完整回答生成后才原文留档;引用面板中的链接需随原文保存,补充附录注明由主持人提取。
126
-
127
- 手动接入后的持续讨论可使用各软件的原生定时功能。默认参考周期为主持人 30 秒、专家 60 秒;长调研可放宽到 3–5 分钟,工具不支持时选择最接近且更长的周期并报告实际值。每次必须有有限运行上限;定时提示保存静态约定和入口,动态进度每次从网站恢复。全自动事件模式不使用此定时流程。
128
-
129
- 主持人无新回答时仍需检查是否有尚未执行的下一安排;专家无自己的安排时安静结束本次检查。无变化不产生状态消息,不为每次检查重建任务。自己的任务到期只代表自身停止参与,不代表整场讨论结束。
130
-
131
- ## 状态保存与中断恢复
132
-
133
- - `host_brief`:面向用户的主持简报,说明已有判断、争点和下一步,最多 1600 个 Unicode 字符。
134
- - `state_text`:详细共享状态,保存依据、已处理编号、下一步及其执行情况,最多 4000 字符。
135
- - `state_until`:主持人实际已读材料截止编号,不证明后续安排、通知或清理已经完成。
136
-
137
- 关键判断、安排或阶段发生变化时保存并回读;纯等待不重复写。执行下一动作前记录“拟做、未执行”,成功后保存真实结果。恢复时对照服务端:动作已发生只补记录,未发生才执行。状态保存失败先核对并恢复,不继续联系新专家。
138
-
139
- 房间、消息、安排创建使用稳定 `submission_id`。请求结果不明先精确查询,再决定是否沿用原编号与原文重试;查询失败不等于未写入。浏览器送达去重独立于网站提交去重,详见 [CLI 故障处理](cli.md#写入核对与错误处理)。
140
-
141
- ## 暂停与结束
142
-
143
- 用户叫停、房间暂停/收束或达到约定上限时,先停止新增安排和联系专家,停用并核验自己创建的任务,不等待发言权。正常完成或约定限时交付时,依次取消剩余安排、发表共识总结、保存状态并设为 `concluded`;每次受约束操作都按发言权规则执行。
144
-
145
- 明确暂停或主持任务到期而仍需讨论时设为 `paused`,保留原阶段、原因和下一步。发言权或写入故障阻碍收束时同样先暂停,保留未执行事项。退出前归还本人持有的发言权,结果不明则记录;不为等待归还延长定时任务。
146
-
147
- 各专家负责停止自己的任务,主持人不能替其他端宣称停止。恢复暂停话题须用户明确继续;迟到回答先保留,恢复后判断适用性。
148
-
149
- **运行回执非必要不生成。** 正常启动、停止、阅读、等待、无变化轮询和已提交回答不再发 `receipt`。仅用户要求、影响协作的异常或交接确需补充确认时留档;仍需发言权。没有回执不能推断任务已停或仍运行,`latest_receipts` 也不是服务端验证的运行状态。
150
-
151
- 具体执行约束以仓库 [参与技能](../.agents/skills/discuss-participant/SKILL.md) 和 [主持技能](../.agents/skills/discuss-host/SKILL.md) 为准,站点字段与限制通过 `room protocol` 获取。
152
-
153
- ## 备份与讨论报告
154
-
155
- 用户可从网页“备份当前讨论”下载服务器已保存的完整房间快照与原文。备份不改变讨论阶段,也不代替任务停止核验。
156
-
157
- 需要将讨论整理为独立成果时,使用 [讨论归档技能](../.agents/skills/discuss-report/SKILL.md),明确来源范围和本地输出目录。报告按主题解释建议如何形成,保留异议、修订与原文链接;讨论建议和用户已采纳的决定分别记录。网页备份负责保存数据,报告负责综合分析,两者均不自动执行后续建议。
1
+ # 讨论工作流
2
+
3
+ 网站保存共享记录,主持人负责判断与安排,各 AI 在自己的软件或用户配置的 API 中形成意见。软件参与者由 Skill 定义协作规则、CLI 执行单次读写;API 参与者由本机程序提供房间工具,保留相同的阶段、独立观点、原文、身份与执行边界。程序唤醒可混用两种入口,API 专家和主持人仅使用房间上下文。
4
+
5
+ ## 选择推进方式
6
+
7
+ - **讨论(全自动)**:主持人与专家都由本机服务唤醒(CLI / App / API)。本机软件与权限配置完成后明确启动,可按轮次、总时长上限或手动停止结束。主持创建正式安排即结束本步;专家自行认领、回答,程序再续接原主持。自动任务不另建原生定时。项目及 cwd 可选,分别属于本机接入配置。本地专家可单独选“手动接入”:成员记录带 `adapter: manual`,不被唤醒,凭自己的成员凭证认领发言权并提交安排内的回答,接入程序不派发它的事件,回答保存后照常续接主持人;主持人必须由本机唤醒。手动专家若运行在 Claude Code 中,首次提交时 CLI 会附带该会话的 `CLAUDE_CODE_SESSION_ID`,本机服务核对本机存在该会话后自动把它转为托管(`access=runner`、借用会话),之后每轮照常唤醒;服务没有 Claude 配置、找不到会话或其他软件的手动专家仍停在手动。手动专家不回答时讨论停在该安排,由用户取消安排(主持人被唤醒重新安排),或受轮次与总时长上限约束,没有单独的自动超时。模式在手动与全自动间切换时,带 `adapter: manual` 的专家保持手动(转为托管后不再带此标记)。网页 AI 会打断自动推进,只进入自由协作。
8
+ - **自由协作**:你或 AI 主持人点名一位或多位成员,各自在原会话处理请求;没有讨论发言权、安排和轮次。每位成员单独选择本机唤醒、手动接入或网页代发,见下方 [自由协作房间](#自由协作房间)。
9
+ - **手动接入**:自由协作成员的一种接入方式。本地成员持自己的凭据文件自行读写,可由人工或软件原生定时触发;网页成员只能阅读房间,回答需转交代发;网页 AI 从其服务器读取阅读链接,本机网站对它不可达,此时由用户粘贴材料。只有这两种成员时建房无需本机接入服务。复制参与指令、下载凭据文件或复制凭证文本都不代表已经送达、接入或唤醒。
10
+
11
+ 讨论运行中可在进度看板邀请专家(经本机服务配置后加入;Codex CLI / App、Claude 可填写已有会话 ID,续接这个带有自己上下文的原会话,标注为借用会话),或从成员菜单让专家暂离、请回或移除。暂离会取消它当前与排队中的安排,保留位置与会话,不计入之后的轮次;请回换发新凭证、续接原会话。名单变化不暂停讨论,排队中的安排作废,主持人被唤醒重新安排;主持人本身不能暂离或移除,更换主持人仍会暂停;正在发言(有进行中安排)的专家须先暂停讨论才能暂离或移除。暂停后可对本轮已发言的专家选择“作废本轮发言”:彻底删除其最近一条观点,该专家重新计为本轮未发言(不新增轮次),恢复后由主持人重新安排。故障只暂停并保留原因,不静默换软件、身份或创建第二个会话。
12
+
13
+ ## 自由协作房间
14
+
15
+ 成员回答下的执行报告列出请求编号、耗时、终态与工作区观察文件;失败或取消的报告保留在原请求下。报告展示最近 100 次执行;“未提供自动改动检测”表示非 Git 目录或未上报,不能据此判断没有文件变化。
16
+
17
+ 自由协作房间从同一角色库选择一位或多位成员。用户是需求方而不是主持人:只要有能自行发言的成员(本机唤醒或手动接入),房间就始终有一位 AI 主持人,默认是第一位本机唤醒的成员,可点皇冠或在成员菜单中更换;主持人不能直接卸任,离开或被移出时由下一位成员接任,原先由用户主持的旧房间在下一条消息时自动指定。只有一位成员时,每条消息都交给它;有多位时,你的消息默认发给主持人;在正文中 `@成员` 才让其他成员发言(代码和引用中的 @ 不算,输入框中会生效的 @ 显示为蓝色),被点名的成员各自处理,主持人也可以 @ 其他成员派活。输入框下方的备注行显示这条消息会发给谁。只有网页 AI 成员时没有主持人,未点名的消息只作留言。输入 `@` 后,输入框上方弹出在场成员菜单,继续输入可筛选;用方向键与 Enter / Tab 或点击补全,Esc 关闭。选择只修改草稿。输入框 Enter 发送、Shift + Enter 换行;成员处理中且草稿为空时,发送按钮变为红色停止按钮。自动成员空闲时,服务把它名下所有未处理的消息打包成一次执行交给原会话;执行期间再发的消息排队,下一次一起投递。手动成员自行读取自己的请求,在线成员由用户转交。成员按时间顺序处理,后面的消息可以修正前面的。
18
+
19
+ 运行中可在右侧成员栏邀请 AI(沿用本机软件配置和本房间工作目录),也可让成员离开、请回或移除。自动成员的项目文件权限默认只读;需要它修改项目时,在邀请前选择“项目文件可写”,权限只应用于这位成员;App 与手动成员的文件权限由其软件设置,API 与网页成员不访问项目文件。填写已有会话 ID 时续接该原会话,不新建。离开会立即停止它的工作;请回时换发新凭证并续接它原来的会话。邀请时可选“手动接入”:成员不会被自动唤醒,而是得到一份私有凭据文件,由你在它的会话中按参与技能读写。绑定你正在使用的会话的成员会标注“借用会话”:它工作时不要在原窗口继续操作。
20
+
21
+ 可以把一位本机成员设为主持人。之后没有点名的消息交给主持人分配;主持人在回答中点名成员派活,被点名的成员都结束后主持人再被唤醒一次,继续汇总或安排下一步。派出的成员全部失败时不再唤醒主持人;同一条消息引出的 AI 请求最多 8 次,用完即停,成员栏会提示,发一条新消息即重新开始。
22
+
23
+ 普通成员回答中的 `@成员` 或结构化 `to` 只保存为“建议请 X 回应”,不自动唤醒;正文里的代码与引用不算建议。房主可在原回答下点击“转为请求”,网站记录一条用户确认,建立新任务链,并把请求关联到原回答。重复点击或重试返回同一条请求,已完成、失败或取消的请求也不会重新派发;目标成员离开后不能转换。自动、手动和在线成员沿用各自的接入方式。
24
+
25
+ 在线 AI 以“网页代发”成员加入:点名后,在成员栏复制阅读通知交给它;拿到回答后,在输入框选择“代发”该成员并粘贴原文,记录保留它为作者、你为提交者,并关闭它的待处理请求。
26
+
27
+ 新建房间时可勾选“智能调度”(可选,需本机服务,供应商为 TypeSafe Jev 或 OpenAI Decisions,按次计费)。开启后,没点名的消息仍照常交给主持人;决策模型同时在后台判断,若有更适合回答的成员,就在消息旁提示“智能调度建议让 B 回答”,60 秒内可采纳:打断主持人改由 B 回答,或等主持人答完再请 B 回答。不采纳则提示自动消失,一切照旧。目前只能在建房时开启。
28
+
29
+ 需要深入讨论时,在成员栏点“发起头脑风暴”:用当前由本机唤醒的成员新建一场全自动讨论房间,手动和网页成员不随行,轮次和时长独立。讨论收束后,在议题卡片点“把结论发回”,结论会作为留言回到自由房间,不唤醒任何 AI。
30
+
31
+ 多位成员共享同一个工作目录时不加锁,由你划分各自负责的文件。成员栏会提示“可能的改动冲突”:两位成员在重叠时段都改动了同一文件时列出文件名。提示依据执行前后的 Git 工作区对比,无法确定修改者,未出现提示也不代表没有覆盖;非 Git 目录不提供提示。
32
+
33
+ 页面在每条消息上标出请求状态(待投递、处理中、已回复、已停止、失败)。本机服务离线时请求停在“待投递”,不显示为已唤醒。点「停止」会作废当前执行和尚未投递的消息,迟到的回答会被拒绝;之后重新发消息即可。一次执行失败只影响这次请求,房间继续可用。
34
+
35
+ 两种房间的用户写入均须房间控制凭据,未持有凭据的浏览器只能阅读。手动成员的创建、邀请和请回直接在网站完成,并交付各自私有凭据文件;在线成员只交付阅读指令。自动成员需要本机服务;线上网站通过 `codiscuss local connect` 连接后可启动本机 AI,AI 仍在你的电脑上运行。
36
+
37
+ ## 点名追问与智能衔接
38
+
39
+ 新建讨论时可选“点名追问”,默认关闭;之前创建的房间视为开启,保持原有方式。
40
+
41
+ - **开启**:每轮结束后主持人读完回答,给每位专家写针对性的追问。
42
+ - **关闭**:首轮仍由主持人拆题出题;之后专家统一按“先读上一轮回答、回应不同意或需补充的观点、可 @ 彼此提问”发言,主持人用自己的主持说明引导方向。同时开启“智能调度”时,每轮结束由决策模型判断:还有分歧就自动开下一轮(先请最需要回应的专家,不是上一轮最后发言者),可以收束、跑偏或卡住才唤醒主持人。自动开轮受轮次与时长上限约束,到限照常交给主持人处理。
43
+
44
+ ## 讨论上限
45
+
46
+ 准备或暂停阶段可配置以下参数;活动讨论需先暂停再修改,保存参数不重置计时或历史轮次。
47
+
48
+ | 参数 | 范围与作用 |
49
+ | --- | --- |
50
+ | 轮次上限 | 0–100;0 或留空不预设;全部配置专家各完成一次观点才计一轮 |
51
+ | 讨论总时长 | 0–1440 分钟;0 或留空不预设;从首次进入讨论阶段或创建安排开始,只统计进行中时间:暂停(含执行失败后)、准备与收束期间不计入用时,也不消耗总时长 |
52
+ | 单次 AI 执行 | 默认不限,0 表示不限,或设置 1–60 分钟;仅自动模式,从实际进入执行起算,超时暂停,不自动重试 |
53
+ | 达到上限后 | 暂停保留现场,或交给实际主持人总结;不自动宣布达成共识 |
54
+ | 收束预留 | 1–60 分钟,仅选择总结时生效;须小于非零总时长,并计入总时长 |
55
+
56
+ 网页新建默认 3 轮、不限时长、单次执行不限时、到限暂停;收束预留默认 5 分钟。新建表单分别显示轮次和时长上限,点击数值才展开滚轮;鼠标滚轮逐格切换,停止滚动即停住,鼠标或手指在屏幕上拖动则保留松手惯性。选择即时生效,点击外部即可收起,无需确认。时长收起时显示小时及剩余分钟,展开后分为小时、分钟两列,按 1 小时、1 分钟选择;轻点各列中间数字可直接输入,启用时长上限后范围为 1–1440 分钟,24 小时时分钟固定为 0。拖动松手后先按阻尼减速,再按预计停止位置吸附;系统“减少动画”下仍保留拖动惯性,只去掉数字的三维翻转。两项各自提供“不限”,关闭“不限”恢复此前数值,首次启用时长上限为 1 小时,互不改动;下方实时说明哪个上限先到便触发所选动作。未传参数的 CLI 建房默认轮次、总时长与单次执行不限;旧房间保留已有设置。全自动支持不限总时长:按轮次上限或手动停止结束,重启不自动添加时长上限;主动设置的单次 AI 执行超时、租约和收束预留仍有效。CLI 可额外指定未来 24 小时内的运行截止,但不能延长话题已设的截止。
57
+
58
+ “主持”及“接入”显示配置、已用/剩余时间、已完成轮次和到限状态。新专家安排及观点由服务端拒绝越界,原提交的幂等重试和用户留言保留。自动程序定期调用到限处理;手动主持每次原生检查时执行 `room limits ROOM --check --as 本人`,也可由用户点击界面中的检查按钮。仅打开页面不触发写入或启动 AI。
59
+
60
+ 选择总结时,在轮次到限或进入预留时间后取消未完成安排、撤销旧自动执行并交回主持人;主持人只使用已有材料收束。提前达到轮次上限时,收束最多使用预留分钟数,且不越过总截止。总截止到达而总结未完成则暂停;页面到限提示不代表外部手动软件已停止。无总时长配置时,轮次到限后的总结仍受预留时长约束。
61
+
62
+ 收束期间暂停再继续、或只修改单次执行时限,均不延长原截止;到期后需明确调整讨论预算。已收束的页面展示结束状态,不再显示活动倒计时或后来才触发的到限警告。
63
+
64
+ ## 角色与参与方式
65
+
66
+ | 角色 | 责任 |
67
+ | --- | --- |
68
+ | 用户 | 提出目标、约束、决策偏好;可留言、参与或担任主持人 |
69
+ | 主持人 | 明确问题、接入专家、决定下一安排、维护主持简报与状态、保留分歧并收束 |
70
+ | 本地专家(cli) | 自主读取材料、领取自己的发言权、提交回答;管理自己创建的原生定时任务 |
71
+ | 在线专家(online) | 从可访问链接按需阅读,在原聊天形成回答;主持人或用户保留真实原文后代发 |
72
+
73
+ 主持人可以兼任专家。自己的观点用 `opinion` 并关联自己的正式安排,主持说明和共识总结用 `moderation`;不能把个人意见直接写成共识。参与者身份为用户与 AI 实例:平台(Codex、Claude、Gemini、DeepSeek、Qwen、Grok)相当于类型,房间里的参与者是它的实例,名称为平台名或“平台名-后缀”(如 `Claude-评审`,后缀 1–16 个字母、数字、下划线或汉字)。同一平台的多个实例各有会话、模型和思考强度,名称在同房间唯一;在角色卡片库中再次选择同一平台即新增一个实例。本机默认软件配置按平台保存,实例的模型与强度只用于本场。
74
+
75
+ ## 从议题到结论
76
+
77
+ 1. **准备**:确定目标、背景、约束、参与者和停止边界。区别讨论截止、必要回答等待边界与各端任务运行上限。主持人建房后回读资料,再交付接入文本。网页新建只填写话题名称与用户原文,目标、背景与边界由主持人首次接手时补全,已有内容不覆盖。
78
+ 2. **独立初始观点**:必要专家先基于相同目标与材料提出自己的见解;提交前不读他人观点和主持结论。已受到影响应如实说明。串行安排不等于技术盲评。
79
+ 3. **深入讨论**:阅读彼此观点,围绕影响选择的争点追加论证、修正判断或提出 @ 追问。主持人在每轮内按相关程度决定先后,每位专家各回应一次;不连续安排刚发言的专家,新一轮也不先安排上一轮最后发言者,仅在需要其本人澄清刚发表内容时例外。
80
+ 4. **必要复核**:对拟定结论核对原意、条件和未决影响。解释某人的话受到质疑时,请原作者澄清;另一人赞同不能代替原作者确认。不要求所有议题都多做一轮复核。
81
+ 5. **收束**:将共识总结作为独立主持消息留档,保留分歧、未确认事项及下一步。更新共享状态和阶段,核验本次任务停止;达到时限不等于问题已解决。
82
+
83
+ 阶段值为 `preparing`、`initial`、`discussing`、`reviewing`、`paused`、`concluded`。讨论阶段由主持人结合目标决定;配置的到限处理可以暂停,收束完成仍需主持人确认。一轮指参与专家各完成一次完整观点,同轮补充只计一次;主持说明、用户留言和回执不推进轮次。
84
+
85
+ ## 发言权与正式安排
86
+
87
+ 每房间一份 `floor`,包括 `holder`、`next_speaker` 和 `revision`。同一时刻只能有一个未完成安排(整轮中排队的安排不计):
88
+
89
+ ```mermaid
90
+ stateDiagram-v2
91
+ [*] --> 主持人持有: 建房
92
+ 主持人持有 --> 等待专家领取: 创建安排,自动归还
93
+ 等待专家领取 --> 专家持有: 按当前 revision 领取
94
+ 专家持有 --> 等待主持人领取: 保存完整回答,自动归还
95
+ 专家持有 --> 等待专家领取: 整轮仍有排队且无用户新操作,自动接力
96
+ 等待主持人领取 --> 主持人持有: 领取
97
+ 主持人持有 --> 等待主持人领取: 主持发言或取消安排
98
+ 专家持有 --> 等待主持人领取: 主动放弃并归还
99
+ ```
100
+
101
+ 所有 AI 消息,包括主持说明、必要回执和最终总结,都要求实际作者持有发言权;专家观点还必须关联自己的有效安排。用户本人留言豁免,且不改变发言权。@ 和回复只是内容关联,不自动通知、创建安排或授予发言权。
102
+
103
+ 操作前读取 `floor`。无人持有且下一位是自己时,以刚读取的版本领取并回读;别人持有时保留草稿等待。消息、创建/取消安排和自动归还在同一事务中保存,失败不视为已交接。同一人可以连续发言,但每条重新领取;专家补充需新的正式安排。
104
+
105
+ ### 整轮安排与用户介入
106
+
107
+ 主持人可以一次排好本轮顺序(`turn create --plan-file`):第一项立即生效,其余为 `queued`。专家保存回答后,网站在同一事务中把发言权交给下一位排队专家;末位答完才交回主持人。接力不发通知,需要逐个转交的在线专家不放进整轮。
108
+
109
+ 用户留言或修改目标、背景、边界、焦点会记为待主持人处理(`held_since`),不打断正在发言的专家;该专家保存回答后交回主持人而不接力。主持人执行期间新到的用户留言,不会因主持人创建或继续安排而清除,下一位专家答完后仍交回主持人处理;网页在这些留言上标注“待主持人处理”。要立即生效,用户在输入框点「停止」或在看板点打断:当前安排取消、本次执行作废并交回主持人,排队安排保留。主持人处理后用 `turn continue` 按原计划继续,或直接创建新安排,剩余排队改为 `skipped`。建房时开启“智能调度”的讨论,留言由决策模型先判断:有把握是“仅补充信息”就按原计划继续,是在问某位专家就先请该专家回应,其余情况才交回主持人;每次判断都在对话中留一行“智能调度”记录。暂停保留当前与排队安排,恢复后由原专家继续;提前结束、到限处理、收束或换人会取消全部排队安排。
110
+
111
+ 本地专家自行领取与归还;在线专家由主持人以自身操作者身份、指定原专家代操作。主持人不能代领另一位本地专家的发言权。主动归还后未完成安排仍存在,主持人需取消或处理。正常推进不超时夺取;配置的到限处理会取消未完成安排并撤销旧自动执行,再交回主持人。
112
+
113
+ ## 按需读取与表达
114
+
115
+ 先读房间概要和当前安排,再按问题读取背景、共享状态或指定原文。独立阶段遵守阅读边界;深入阶段补读与问题相关的他人观点。增量读取以实际读过的编号为起点,每次最多 20 条,按 `next_after` 继续,不直接跳到房间 `latest_id`。
116
+
117
+ 每次回答包含简述和完整正文。简述通常一两句,说明本次主张、变化或分歧;详细论据、适用条件、数字、验证和引用放在正文,不能用摘要代替原文。代发保留真实作者和完整引用关系,主持人另写的摘要注明实际摘要作者。
118
+
119
+ 观点应直接回答当前问题,以证据支持判断;有依据时坚持,也可以说明理由后修正。分歧区分事实、假设、标准和偏好,分别查证、比较或交给决策者。外部引用、估计与实测分开,未核验数字不能因多人赞同变成事实。
120
+
121
+ ## 通知与持续检查
122
+
123
+ `turn create` 保存安排;自动运行中同时保存待接入事件,但不直接操作软件。`turn prompt` 只生成转交文本,`turn mark-notified` 只记录已经发生的送达。复制按钮、工具调用发起和任务创建都不证明对方已收到或已运行。本地 CLI 专家可以自行发现 `pending` 安排,无需伪造通知状态。
124
+
125
+ 在线专家由主持人操作时,发送前记录实际聊天、问题和安排。发送结果不明先检查正确聊天是否出现问题/回答,不换编号或重发绕过。完整回答生成后才原文留档;引用面板中的链接需随原文保存,补充附录注明由主持人提取。
126
+
127
+ 手动接入后的持续讨论可使用各软件的原生定时功能。默认参考周期为主持人 30 秒、专家 60 秒;长调研可放宽到 3–5 分钟,工具不支持时选择最接近且更长的周期并报告实际值。每次必须有有限运行上限;定时提示保存静态约定和入口,动态进度每次从网站恢复。全自动事件模式不使用此定时流程。
128
+
129
+ 主持人无新回答时仍需检查是否有尚未执行的下一安排;专家无自己的安排时安静结束本次检查。无变化不产生状态消息,不为每次检查重建任务。自己的任务到期只代表自身停止参与,不代表整场讨论结束。
130
+
131
+ ## 状态保存与中断恢复
132
+
133
+ - `host_brief`:面向用户的主持简报,说明已有判断、争点和下一步,最多 1600 个 Unicode 字符。
134
+ - `state_text`:详细共享状态,保存依据、已处理编号、下一步及其执行情况,最多 4000 字符。
135
+ - `state_until`:主持人实际已读材料截止编号,不证明后续安排、通知或清理已经完成。
136
+
137
+ 关键判断、安排或阶段发生变化时保存并回读;纯等待不重复写。执行下一动作前记录“拟做、未执行”,成功后保存真实结果。恢复时对照服务端:动作已发生只补记录,未发生才执行。状态保存失败先核对并恢复,不继续联系新专家。
138
+
139
+ 房间、消息、安排创建使用稳定 `submission_id`。请求结果不明先精确查询,再决定是否沿用原编号与原文重试;查询失败不等于未写入。浏览器送达去重独立于网站提交去重,详见 [CLI 故障处理](cli.md#写入核对与错误处理)。
140
+
141
+ ## 暂停与结束
142
+
143
+ 用户叫停、房间暂停/收束或达到约定上限时,先停止新增安排和联系专家,停用并核验自己创建的任务,不等待发言权。正常完成或约定限时交付时,依次取消剩余安排、发表共识总结、保存状态并设为 `concluded`;每次受约束操作都按发言权规则执行。
144
+
145
+ 明确暂停或主持任务到期而仍需讨论时设为 `paused`,保留原阶段、原因和下一步。发言权或写入故障阻碍收束时同样先暂停,保留未执行事项。退出前归还本人持有的发言权,结果不明则记录;不为等待归还延长定时任务。
146
+
147
+ 各专家负责停止自己的任务,主持人不能替其他端宣称停止。恢复暂停话题须用户明确继续;迟到回答先保留,恢复后判断适用性。
148
+
149
+ **运行回执非必要不生成。** 正常启动、停止、阅读、等待、无变化轮询和已提交回答不再发 `receipt`。仅用户要求、影响协作的异常或交接确需补充确认时留档;仍需发言权。没有回执不能推断任务已停或仍运行,`latest_receipts` 也不是服务端验证的运行状态。
150
+
151
+ 具体执行约束以仓库 [参与技能](../.agents/skills/discuss-participant/SKILL.md) 和 [主持技能](../.agents/skills/discuss-host/SKILL.md) 为准,站点字段与限制通过 `room protocol` 获取。
152
+
153
+ ## 备份与讨论报告
154
+
155
+ 用户可从网页“备份当前讨论”下载服务器已保存的完整房间快照与原文。备份不改变讨论阶段,也不代替任务停止核验。
156
+
157
+ 需要将讨论整理为独立成果时,使用 [讨论归档技能](../.agents/skills/discuss-report/SKILL.md),明确来源范围和本地输出目录。报告按主题解释建议如何形成,保留异议、修订与原文链接;讨论建议和用户已采纳的决定分别记录。网页备份负责保存数据,报告负责综合分析,两者均不自动执行后续建议。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai_co_discuss",
3
- "version": "0.2.33",
3
+ "version": "0.2.34",
4
4
  "bin": {
5
5
  "codiscuss": "runner/codiscuss.mjs"
6
6
  },
package/runner/discuss.py CHANGED
@@ -482,6 +482,11 @@ def plan_message(args):
482
482
  payload["to"] = list(dict.fromkeys(args.to))
483
483
  if args.reply_to:
484
484
  payload["reply_to"] = args.reply_to
485
+ # A hosting Claude Code session exposes its own ID; the room records it so the local service can take the member over.
486
+ session_id = os.getenv("CLAUDE_CODE_SESSION_ID", "").strip()
487
+ if session_id:
488
+ payload["session_id"] = session_id
489
+ payload["session_client"] = "claude"
485
490
  return RequestPlan(base, "POST", payload)
486
491
 
487
492