ai_co_discuss 0.2.74 → 0.2.76
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/.agents/skills/discuss-host/references/launch.md +2 -2
- package/.agents/skills/discuss-host/references/manual.md +2 -2
- package/.agents/skills/discuss-report/SKILL.md +90 -90
- package/README.md +12 -14
- package/README.zh-CN.md +6 -8
- package/docs/architecture.md +35 -266
- package/docs/deployment.md +30 -123
- package/docs/en/architecture.md +51 -0
- package/docs/en/deployment.md +132 -0
- package/docs/en/runner.md +92 -0
- package/docs/en/workflow.md +95 -0
- package/docs/runner.md +45 -420
- package/docs/workflow.md +58 -122
- package/package.json +1 -1
- package/runner/discussion_launch.mjs +63 -6
- package/runner/free_runner.mjs +8 -4
- package/web/modern.css +1 -1
- package/web/modern.js +897 -869
- package/docs/cli.md +0 -197
- package/docs/maintainers/TODO.md +0 -19
- package/docs/skills.md +0 -102
package/docs/architecture.md
CHANGED
|
@@ -1,282 +1,51 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 架构
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[English](en/architecture.md) | 简体中文
|
|
4
4
|
|
|
5
|
-
AI CoDiscuss
|
|
5
|
+
AI CoDiscuss 把三件事分开:网站保存讨论事实并校验写入,本机服务负责唤醒 AI,AI 在自己的会话中形成判断。
|
|
6
6
|
|
|
7
7
|
```mermaid
|
|
8
|
-
flowchart
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
R
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
PY --> SQL[共用 backend/schema.sql]
|
|
18
|
-
CF --> SQL
|
|
8
|
+
flowchart LR
|
|
9
|
+
U[浏览器] --> S[(网站<br>Python + SQLite<br>或 Cloudflare + D1)]
|
|
10
|
+
S -- 事件 --> R[本机服务<br>runner/]
|
|
11
|
+
R -- 唤醒 / 续接 --> A[CLI / App]
|
|
12
|
+
R -- 调用 --> M[模型 API]
|
|
13
|
+
R -. 可选 .-> D[决策模型]
|
|
14
|
+
A -- codiscuss CLI --> S
|
|
15
|
+
M -- 房间工具 --> S
|
|
16
|
+
W[网页 AI] -. 只读 /r .-> S
|
|
19
17
|
```
|
|
20
18
|
|
|
21
|
-
|
|
19
|
+
## 设计原则
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
- **保留原会话**:软件参与者在自己的会话、账号与工具中工作;网站只提供共享上下文与协作约束。
|
|
22
|
+
- **网站不运行模型**:网站不启动本机进程、不保存模型密钥、不调用模型;这些都由本机服务完成。
|
|
23
|
+
- **先读取、再提交、服务端兜底**:依赖房间整体状态的写入(如整轮安排、收束)须携带读取时取得的版本凭据;凭据缺失返回 400,状态已变化返回 409,不依赖调用方自觉。
|
|
24
|
+
- **结果以网站记录为准**:会话 ID 出现、进程退出或受理回执都不等于完成;已交付但结果不明的任务不自动重发。
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
| --- | --- | --- |
|
|
27
|
-
| `backend/server.py` | HTTP 请求、SQLite 路径、`backend/schema.sql` | JSON API、纯文本页面、静态资源;事务读写本地数据库;根目录 `server.py` 调用其启动函数 |
|
|
28
|
-
| `backend/worker.mjs` | Pages Request、D1 `DB` binding | 与 Python 相同的业务响应;静态请求交由 Pages assets |
|
|
29
|
-
| `backend/free_room.py` | 两种房间的成员登记,以及自由房间的请求与执行 | 独立成员凭证、按成员的执行唯一性、打断与失败隔离;只由 Python 后端调用,见 [自由协作协议](#自由协作协议) |
|
|
30
|
-
| `backend/updates.mjs` | Pages 可选 `UPDATES` Durable Object binding | 跨 Worker 实例广播网页变更通知;不保存讨论数据,不提供公开发布入口 |
|
|
31
|
-
| `runner/codiscuss.mjs` | 统一 `codiscuss` 命令与原样参数 | 无子命令时启动本机服务,`service` 管理进程;网站命令转交 Python 客户端,保留 stdin、相对路径与退出码 |
|
|
32
|
-
| `runner/discussion_launch.mjs` | 本机默认软件配置、单场启动配置、对话 / 网页入口 | 校验后建房、保存私有配置、启用运行并登记接入服务;对话入口将首个主持任务交回当前会话;暂停后可凭原控制凭据和版本修正本场软件路径或切换 Codex App / CLI,保留原会话绑定,不自动重启 |
|
|
33
|
-
| `runner/discussion_models.mjs` | 本机 CLI 模型目录 | 只执行 Codex 模型列表、Claude 初始化或 agy models,返回模型与档位,不发送提示词;查询后关闭临时进程 |
|
|
34
|
-
| `runner/discussion_api.mjs` | 供应商默认值、用户 Base URL、请求协议、真实模型 ID、私有密钥引用 | 校验 URL 和密钥绑定;读取模型列表;转换 Responses / Chat Completions / Anthropic / Gemini 请求与工具历史,仅使用 Node 内置模块 |
|
|
35
|
-
| `runner/api_participant.mjs` | 一次有效执行、参与者身份及 API 配置 | 提供限定本房间和职责的工具,保存原文或主持安排;取消时中止请求,不向模型传递执行令牌、API Key 或文件工具 |
|
|
36
|
-
| `runner/discussion_setup.mjs` / `discussion_skills.mjs` / `discussion_tui.mjs` | npm 包、已有 Python / CLI、选定软件 | 持久安装统一命令,合并本机软件配置,复制完整技能并保留本地修改;`discussion_tui.mjs` 只负责安装过程的终端呈现;不创建讨论 |
|
|
37
|
-
| `runner/discussion_local.mjs` | 用户服务目录、Python 和端口配置 | 后台网站及包外 SQLite,复用讨论服务、检查就绪、仅停止拥有的进程 |
|
|
38
|
-
| `runner/discuss.py` | 命令、站点、房间、声明身份、UTF-8 文件 | 网站客户端实现;单次 HTTP 请求、稳定 JSON / 文本、退出码,帮助和预览可离线,可直接用 Python 执行 |
|
|
39
|
-
| `web/app.js` | 页面事件和 API 返回状态 | 共享讨论控制器、读写操作、主题与界面同步 |
|
|
40
|
-
| `web/index.html` | 控制器状态 | Vue 挂载点、共享控制器的隐藏表单适配层与附件预览对话框 |
|
|
41
|
-
| `frontend/` | 同一控制器与 API | Vue / Nuxt UI 协作频道、资料/讨论/主持与接入、对话框 |
|
|
42
|
-
| `frontend/markdown.mjs` | Markdown 原文 | 三个主题共用的安全 HTML 与公式渲染;原始 HTML 关闭 |
|
|
43
|
-
| `runner/` | 私有房间配置、网站事件 | 接入 Codex App / CLI、Claude Code CLI、Antigravity App / CLI 的真实会话,续租与有限重试;`discussion_service.mjs` 常驻托管多个已登记房间;不生成回答,详见 [自动接入程序](runner.md) |
|
|
44
|
-
| `runner/free_runner.mjs` | 自由房间的 inbox、私有房间配置 | 把成员名下的全部待投递请求打包为一次执行,复用同一组软件适配器;只停止该成员自己的进程 |
|
|
45
|
-
| `runner/scheduler.mjs` | 房间配置中的 `scheduler`、本机私有密钥 | 可选智能调度:把中立的选择/判断题转换为 TypeSafe Jev 或 OpenAI Decisions 请求,返回带置信度的类型化答案;不生成文本,不保存密钥 |
|
|
46
|
-
| `scripts/build_pages.mjs` | 后端源码、已构建 `web/`、Pages 静态文件 | 清理固定输出并生成 `dist/pages/`;不部署 |
|
|
26
|
+
## 组成
|
|
47
27
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
Python 服务独占监听端口:Windows 使用 `SO_EXCLUSIVEADDRUSE`,关闭地址复用;其他平台保留常规重启所需的地址复用,但不允许复用正在监听的端口。第二个实例在启动时失败,避免请求被分配到另一个讨论数据库。
|
|
51
|
-
|
|
52
|
-
`--local-launch` 是仅限回环监听的可选本机控制接口,公开部署默认关闭。本机网页使用同源请求;`codiscuss local connect SITE` 为指定 HTTPS 来源生成连接凭据后,线上浏览器也可直接调用该电脑的接口。连接信息保存在浏览器本地存储,本机 `site-connections.json` 仅存来源与凭据哈希;不扫描公开房间或自动接管未明确启动的讨论。来源和凭据验证通过后,接口把 `site` 固定为该来源,再经固定 Node 入口调用共享启动器;路径与连接凭据不经过线上服务器。启动器复用两套后端已有建房、运行、事件协议,Cloudflare 不提供进程启动接口,无需新增 D1 表。网页的启动配置由 `frontend/LaunchSettings.vue` 与共享控制器维护,深浅频道主题使用同一启动请求。
|
|
53
|
-
|
|
54
|
-
网页在请求前按原提交号或房间保存完整待办;展示快照仅含标题、房间编号与错误,不包含待办中的凭据或私有配置。刷新后的恢复必须由用户显式触发,重放原输入进入启动器已有日志;每次发送的独立编号防止跨标签迟到失败覆盖较新的待办状态。本机桥接保留启动器的部分失败阶段,只有控制权验证后才返回原房间编号;浏览器请求必须显式携带控制凭据,不回退到磁盘中的凭据代为认领。网站业务协议和 Cloudflare 后端不受此本机恢复接口变更影响。
|
|
55
|
-
|
|
56
|
-
## 数据模型
|
|
57
|
-
|
|
58
|
-
两套讨论后端使用同一 `backend/schema.sql`:
|
|
59
|
-
|
|
60
|
-
| 表 | 内容 |
|
|
61
|
-
| --- | --- |
|
|
62
|
-
| `rooms` | 房间基本信息和共享状态 |
|
|
63
|
-
| `room_directory`、`topic_folders`、`room_folder_assignments`、`directory_settings` | 话题目录、归档、分组与未归类的位置 |
|
|
64
|
-
| `room_creations` | 创建提交编号和初始请求,用于精确核对重试 |
|
|
65
|
-
| `room_briefs` | 主持简报、覆盖消息截止和更新时间 |
|
|
66
|
-
| `room_user_requests` | 独立保存用户请求原文,与补充背景分开 |
|
|
67
|
-
| `topic_settings` | 主持人、阶段、焦点和参与方式 |
|
|
68
|
-
| `discussion_limits` | 轮次、总时长、单次执行上限及实际计时状态;`on_limit`、`summary_minutes`、`closing_at` 列已弃用(到限一律暂停),保留仅为兼容旧库 |
|
|
69
|
-
| `discussion_pauses` | 每个房间当前暂停起点与累计暂停秒数;离开进行中阶段时由触发器记起点,恢复时累加,用时与总时长截止均扣除 |
|
|
70
|
-
| `messages` | 原始消息、作者、来源、回复与提交编号 |
|
|
71
|
-
| `attachments` | 附件元数据、上传者、提交编号、SHA-256 与消息中的顺序;字节存于磁盘或 R2 |
|
|
72
|
-
| `message_summaries` | 回答简述及其实际撰写者 |
|
|
73
|
-
| `message_details` | 消息职责、代发操作者、关联安排 |
|
|
74
|
-
| `message_timings` | 自动执行从交付唤醒到消息保存的秒数,随消息固定保存 |
|
|
75
|
-
| `discussion_sessions` | 每次讨论一条薄记录(目标、`active/paused/concluded`、起止消息);状态由触发器跟随旧阶段 `topic_settings.phase`,旧库重复执行 schema 时按阶段回填;自由房间没有会话。房间视图 `session` 返回最近一条。讨论并入自由房间的过渡结构,见 `.dev/plans/讨论模式并入自由房间/` |
|
|
76
|
-
| `discussion_turns` | 问题、专家、通知状态、回答与取消 |
|
|
77
|
-
| `room_floor` | 已停用的旧发言权记录,保留旧库数据,不再读写 |
|
|
78
|
-
| `turn_relay` | 整轮接力暂停标记:`held_since` 为首个待主持人处理的用户操作,非 0 时回答交回主持人而不接力 |
|
|
79
|
-
| `discussion_runtime` | 显式模式、运行 ID、代次、截止时间、版本及控制凭据哈希 |
|
|
80
|
-
| `discussion_events` | 持久业务原因和对应的一次执行、身份、状态、真实会话及租约;事件 ID 兼执行 ID |
|
|
81
|
-
| `event_timings` | 执行的交付与结束时间(触发器在状态变化时写入),供进度看板计算阶段耗时 |
|
|
82
|
-
| `runtime_assertions` | D1 批次内授权断言;检查失败则整批回滚,成功后删除临时行 |
|
|
83
|
-
|
|
84
|
-
数据库唯一约束阻止重复提交和多个未完成安排;触发器检查阶段、安排关联及持有人,并在保存消息/安排时完成交接。两套后端也执行相应业务校验;修改协议需同步服务、schema、CLI 和相关测试。
|
|
85
|
-
|
|
86
|
-
旧库初始化会补齐缺失表和记录,保留已有消息与安排。发言权已取消,旧 `room_floor` 只保留不用;新增迁移需要验证旧库和重复执行行为。Python 服务启动会应用 schema;远端 D1 更新由部署者明确执行。
|
|
87
|
-
|
|
88
|
-
房间资料将“用户原文”与“背景”作为同级章节,新建和编辑资料分别填写;两套后端、CLI 的 `room brief`、纯文本资料页与可读备份均提供独立原文。`user_request` 限 4000 字并保留首尾空白,由增量表 `room_user_requests` 保存;D1 升级须先应用 schema。旧房间没有独立记录时,仅对背景开头“用户原文:”及后续独占一行的“项目背景与入口:”或“背景:”明确分隔进行兼容读取,读取不改原始数据;下次保存资料时分开落库。未明确分隔的旧背景保持原样。
|
|
89
|
-
|
|
90
|
-
## 读写协议
|
|
91
|
-
|
|
92
|
-
常用 API 为 `/api/health`、`/api/rooms` 和 `/api/rooms/ROOM_ID` 下的 messages、state、turns等接口。写入结果取决于房间整体状态的操作遵循“先读取、带凭据、服务端校验”(见 AGENTS.md):排整轮计划先 `turns/roster`,主持人收束或宣告完成先 `conclusion`,作废发言须带所见的 `latest_id`,状态截止编号 `until` 不回退。具体方法、字段与当前限制通过 `codiscuss schema` 和 `codiscuss room protocol ROOM` 获取;原 Python 入口继续兼容,避免维护第二套过期请求样例。
|
|
93
|
-
|
|
94
|
-
附件使用 `/api/rooms/ROOM_ID/attachments`:POST 接收原始字节,查询参数为 `actor`、`file_name`、`submission_id`,限制 20 MiB;GET 默认分页列出已发送文件,也可用 `submission_id` 精确核对尚未关联消息的上传。`/attachments/ID` 返回元数据,`/attachments/ID/content` 返回原文件,`?download=1` 强制下载。消息传有序 `attachment_ids`(最多 10 个、不重复),可以只有附件;关联与消息在同一事务保存,同编号重试比较原文及附件顺序。
|
|
95
|
-
|
|
96
|
-
附件编号为随机值,文件名不参与存储路径。Python 存于 `<数据库绝对路径>.attachments/`,Pages 使用 `ATTACHMENTS` R2 binding;未配置 R2 时上传返回 503,不影响文字讨论。上传受同一房间写入凭据约束,自动任务须在回答完成前上传;消息只可关联同房间、同实际提交者且未被其他消息占用的附件。读取沿用网站现有公开边界。删除房间同时尝试删除原文件;未发送的上传保留到房间删除,不列入普通列表或备份。
|
|
97
|
-
|
|
98
|
-
服务端只将文件头识别的 PNG/JPEG/GIF/WebP/PDF 作为可预览类型,其他类型以二进制下载并设置 `nosniff`。网页共用选择、拖拽和截图粘贴;图片直接按原比例展示缩略图,点击用原生对话框放大,文件则使用类型图标、名称、大小和下载按钮组成的紧凑横向卡片,避免把图片套进文件信息框。发送前同样区分图片缩略图和文件卡片,可逐个移除;扩展名图标仅用于识别,不决定预览能力。PDF.js 及字体、解码资源随 `web/pdfjs/` 本地打包,按页渲染并限制画布尺寸。网页不抽取文本、不转换 Office,也不替 AI 读取文件。备份把已发送原文件加入 ZIP 的 `附件/`,保留编号、校验值及本地相对链接;文件缺失或校验失败则不生成部分备份。
|
|
99
|
-
|
|
100
|
-
| 纯文本入口 | 内容 |
|
|
101
|
-
| --- | --- |
|
|
102
|
-
| `/r/ROOM_ID` | 目标、阶段、主持人、当前安排和按需阅读链接 |
|
|
103
|
-
| `/r/ROOM_ID/brief` | 背景、边界、参与配置 |
|
|
104
|
-
| `/r/ROOM_ID/state` | 主持简报、共享状态及已读截止 |
|
|
105
|
-
| `/r/ROOM_ID/messages` | 分页原文,每页 20 条 |
|
|
106
|
-
| `/r/ROOM_ID/messages/ID` | 精确消息、作者和来源 |
|
|
107
|
-
| `/r/ROOM_ID/search` | 关键词、作者或 @ 对象检索 |
|
|
108
|
-
| `/r/ROOM_ID/turns`、`/turns/ID` | 安排历史或具体问题与回答入口 |
|
|
109
|
-
| `/r/ROOM_ID/protocol` | 当前参与与写入协议 |
|
|
110
|
-
|
|
111
|
-
`?mode=cli` 为可写客户端提供对应引导,`?participant=NAME` 提供该参与者的检索入口。默认概要不夹带全部背景、主持总结或历史,支持独立阶段的按需阅读;这不是服务端权限隔离。
|
|
112
|
-
|
|
113
|
-
GET / HEAD 不发布消息;旧 `/r/ROOM_ID/publish` 返回 410。新增内容使用 POST,消息追加保存。创建操作用 `submission_id` 去重,修改资料与状态前先读取现状;不要将 HTTP 超时解释为服务拒绝。
|
|
114
|
-
|
|
115
|
-
### 目录写入
|
|
116
|
-
|
|
117
|
-
`POST /api/folders/FOLDER_ID/move` 接收 `before_id`(null 表示整个列表末尾),两处编号均可使用保留值 `__uncategorized__` 表示内部排序用的虚拟分组。两套后端在同一事务更新 `topic_folders.position` 和 `directory_settings.uncategorized_position`,返回真实 `folders` 与未归类房间区域的零基位置;目录读取也返回这两个字段。位置为 null 时该区域保持在末尾,新建文件夹排在它前面;设置为具体位置后,新建文件夹追加到真实文件夹末尾,不挤动该区域。旧库应用 schema 后默认保持原顺序,重复初始化不覆盖已保存位置;D1 部署前须应用 schema。虚拟分组不写入 `topic_folders`,未归类对话的 `folder_id` 仍为 null。
|
|
118
|
-
|
|
119
|
-
`POST /api/folders/FOLDER_ID/rename` 接收 1–40 字符的 `name`,保留文件夹编号、顺序与对话归属。建房请求可带 `folder_id`,省略或 null 表示未归类;两套后端均将建房与归类放在同一事务,并将指定文件夹纳入创建请求的幂等校验。重试返回已创建的对话,不再次变更后来调整的归属;不存在的文件夹会使创建失败,不留下孤立对话。
|
|
120
|
-
|
|
121
|
-
`POST /api/folders/FOLDER_ID/delete` 仅移除真实文件夹,使用既有 `ON DELETE SET NULL` 外键将普通和存档房间的归属清空,并在同一事务中调整未归类的位置,其他分组保持相对顺序。它不删除房间或消息,不改变讨论阶段与运行状态;不存在的文件夹及虚拟分组返回 404。复用现有表和外键,无需 schema 迁移。
|
|
122
|
-
|
|
123
|
-
## 聊天室本地备份
|
|
124
|
-
|
|
125
|
-
`GET /api/rooms/ROOM_ID/backup` 返回 `format=ai-codiscuss-room-backup`、`version=1`、`exported_at` 和 `tables`。`tables` 包含讨论资料相关的 16 张表中属于指定房间的原始行,保留编号、时间、正文、创建请求、摘要与摘要作者、代发者、引用、安排、消息耗时、已发送附件元数据;只带所属文件夹自身,不带同文件夹的其他房间。它不接受分页或搜索过滤,不修改房间状态;空房间可导出,不存在的房间返回 404。
|
|
126
|
-
|
|
127
|
-
Python 在一个只读事务快照中查询;D1 使用同一个 batch 事务读取,避免导出中途新增消息导致正文与关联元数据不一致。浏览器随后下载快照中不可变的附件原文件;外部链接只保留地址,未发送上传、未保存草稿与其他部署配置不在备份内。旧版快照缺少 `attachments` 时仍可导出。
|
|
128
|
-
|
|
129
|
-
`frontend/backup.mjs` 接收完整快照,通过 fflate 在浏览器内生成 ZIP:`完整数据.json` 保存可核对的原始字段,`讨论记录.md` 提供离线原文及消息/安排锚点,`备份说明.txt` 记录范围。三个主题通过同一控制器触发,下载中切换房间不会改变本次备份对象。请求或打包失败不生成部分成功包,用户可再次操作重试。房间目录菜单的“导出全部房间”和文件夹菜单的“导出文件夹”把多个房间打成一个批量 ZIP,每个房间一个 `<标题>_<房间编号>/` 子目录,结构同单个备份;存档房间一并导出。备份需要将完整房间载入内存,当前未实现流式导出;很大的房间受浏览器及部署资源限制。
|
|
130
|
-
|
|
131
|
-
## 房间导入
|
|
132
|
-
|
|
133
|
-
房间目录菜单“导入房间…”接受单个或批量备份 ZIP(也接受单独的 `完整数据.json`),可多选。浏览器逐个房间为其生成新的房间控制凭据并保存在本浏览器,再调用 `POST /api/rooms/import`(请求体为 `完整数据.json` 对象,上限 32 MiB,`X-Discussion-Token` 为新凭据)。两套后端规则一致(`backend/room_import.py` 与 `worker.mjs` 中同名流程):
|
|
134
|
-
|
|
135
|
-
- **身份与去重**:保留备份中的房间编号;本站已有该编号时返回 200 `{skipped:true}`,不覆盖、不重复,重复导入安全。导入成功返回 201 与附件编号映射。
|
|
136
|
-
- **重新编号**:消息、发言安排、成员和自由协作请求在本站当前最大编号之后按原顺序重排;回复、回答、安排关联、摘要、耗时、附件、请求与建议随之映射;房间目标、共识、简报、议题字段、安排问题和消息正文中的 `#编号` 若指向本房间消息则改写为新编号,其他数字原样保留。共识与简报的“覆盖至”取所覆盖的最后一条新编号。附件获得新编号。
|
|
137
|
-
- **继续使用**:导入者成为房主;成员保留名称、职责与接入方式,状态改为离开(已移除者保持移除),凭据不导出也不可用,需在成员卡片“请回”换发新凭证。讨论房间没有运行记录,进行中的阶段改为已暂停,由房主重新启动;自由协作保持可用,未完成的请求改为已取消。所属文件夹按编号复用,本站没有时新建;存档状态保留;建房提交编号与来源房间关联不导入。
|
|
138
|
-
- **写入顺序**:消息先以自由协作模式、安排先以排队状态、消息职责先以占位类型写入,再恢复真实值,避免安排与运行触发器把历史当作新动作;全部写入在一个事务(D1 为带编号断言的单个 batch)中完成,失败整体回滚。D1 写入按 100 个参数分块合并多行。
|
|
139
|
-
- **附件原文件**:导入后浏览器把 ZIP 中的原文件逐个 `POST /api/rooms/ROOM_ID/attachments/新编号/import`,需房主凭据,大小与 SHA-256 须与记录一致;已有文件不覆盖。缺少原文件时消息仍导入,附件显示缺失。
|
|
140
|
-
- **结果**:房间目录下方列出导入、跳过与失败数量;失败项写明文件或房间及原因。
|
|
141
|
-
|
|
142
|
-
## 前端与素材
|
|
143
|
-
|
|
144
|
-
### 源码与构建
|
|
145
|
-
|
|
146
|
-
`frontend/` 保存 Vue、Markdown 和备份源码;`web/` 保存共享控制器、HTML 入口、图标和构建产物。生成产物随仓库提供,保证仅安装 Python 的用户也能运行网页;不在生成文件中手工修改功能。
|
|
147
|
-
|
|
148
|
-
新建房间表单须至少选中一位成员,决策模型不计入成员;创建按钮与表单提交(含 Enter)共用就绪校验,防止键盘绕过禁用状态。成员添加行紧跟“谁来参与”标题,接入方式由卡片上的“程序启动 / 手动启动”选项表达。
|
|
149
|
-
|
|
150
|
-
| 源码 | 职责 |
|
|
28
|
+
| 目录 | 职责 |
|
|
151
29
|
| --- | --- |
|
|
152
|
-
| `
|
|
153
|
-
| `
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
|
|
|
157
|
-
| `
|
|
158
|
-
| `FreeBoard.vue` | 自由协作房间的右侧看板,版式与讨论进度看板一致:房间状态与请求/执行计数、当前任务、成员(主持人居首,显示接入方式、模型与状态)、邀请(弹出 `InviteDialog.vue`)、开启讨论(弹出 `StartDiscussionDialog.vue`)和每次执行用时 |
|
|
159
|
-
| `AccountSettings.vue` | 设置弹窗的管理员账号分区(注册、登录、改名与头像、退出登录) |
|
|
160
|
-
| `use-discussion.js` | 将 `web/app.js` 的共享控制器状态接入 Vue |
|
|
161
|
-
| `theme.css`、`channel.css` | 组件主题、深浅配色与响应式布局 |
|
|
162
|
-
| `markdown.mjs`、`markdown.css`、`backup.mjs` | 三个主题共用的内容渲染与本地导出 |
|
|
163
|
-
|
|
164
|
-
`frontend/vite.config.mjs` 构建三个入口:Markdown IIFE、备份 IIFE 和 Vue ES 模块,由根目录 npm 脚本显式指定配置。输出为 `web/markdown.js`、`web/markdown.css`、`web/backup.js`、`web/modern.js`、`web/modern.css`。字体随 CSS 打包,图标来自本地 `web/icons/` 与构建时 Iconify 数据,无需运行时请求图标 CDN。
|
|
165
|
-
|
|
166
|
-
`scripts/build_pages.mjs` 将静态运行文件、`backend/worker.mjs` 和许可证汇集到 `dist/pages/`;`pages/public/` 补充路由、robots 和 404。它只清理固定输出目录,指定源码快照时全部文件取自同一快照,并兼容整理前的文件布局,不混入工作区其他版本。第三方声明在发布目录中仍为 `THIRD_PARTY_NOTICES.md`;前端源码和隐藏文件不发布到 Pages。
|
|
167
|
-
|
|
168
|
-
### 主题与交互边界
|
|
169
|
-
|
|
170
|
-
新建表单只建协作房间(可先不加成员;项目目录在第 1 步,勾选“创建后开启讨论”才展开目标与上限,上限控件为共用的 `DiscussionLimits.vue`),并为每位成员选择接入方式(程序唤醒/手动),共用内联角色库。接入方式以分段切换放在参与区标题旁;每次添加生成独立实例,已加入成员以 `ExpertCard.vue` 渲染为翻面卡片:正面是身份与只读配置(状态、软件、模型、强度、项目权限),背面是设置,项目只读 / 可写按专家分别设置,App 与手动会话显示“由软件设置”,API 与网页专家显示“不访问”,缺少的软件路径在背面直接填写;卡片上的皇冠指定主持人,移出按钮同步修复主持人选择,角色库为卡片下方的“添加”标签行。自由协作的用户是需求方,默认由第一位能自行发言的成员主持,可点皇冠更换。支持本地参与的卡片翻面设置参与能力和软件入口,纯网页角色不翻面。自动 CLI 从本机软件目录下拉选择模型,不要求手填 ID;模型与强度同层显示,恢复默认只清除当前实例覆盖。App、网页和手动会话在原软件内设置。`EffortSlider.vue` 按所选模型的实际档位显示,首档为空值的 CLI 默认,显示标签与提交值分离(Extra High 对应 xhigh);标题处的档位名随拖动实时预览、松手才提交,悬停、拖动与键盘聚焦分别有光晕反馈,减少动态效果时不做过渡;agy 的强度随模型,不另显示滑杆。目录不可用时显示原因并保留软件默认,不虚构列表。
|
|
171
|
-
|
|
172
|
-
项目权限选择仅对 CLI 入口开放;API 与网页专家不访问项目文件,界面显示“不访问”。创建卡片、接入设置、经典主题和自由房间邀请沿用同一 CLI 入口判断,API 不继承本场可写配置,执行器强制归一为只读。App 的实际文件权限在原软件中配置。
|
|
30
|
+
| `backend/` | 两套等价后端:`server.py`(Python + SQLite)与 `worker.mjs`(Cloudflare Pages + D1),共用 `schema.sql` |
|
|
31
|
+
| `runner/` | 统一命令 `codiscuss`、常驻讨论服务、各软件与 API 适配器、智能调度;只使用 Node.js 内置模块 |
|
|
32
|
+
| `frontend/` | Vue 界面、Markdown 渲染与备份源码 |
|
|
33
|
+
| `web/` | 可直接运行的静态网页与构建产物 |
|
|
34
|
+
| `.agents/skills/` | 主持、参与与报告技能 |
|
|
35
|
+
| `tests/` | Python 与 JavaScript 测试 |
|
|
173
36
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
正反面占用同一网格单元,避免翻转引起布局跳动。背面滚动区域保留 2px 内边距,避免下拉控件的外描边被裁切。角度由现有 Motion 弹簧驱动:质量 1、频率 2.615 Hz、阻尼比 0.96(刚度 270.032、阻尼 31.551);目标在 0° / 180° 之间切换,中途反向保留速度,无固定时长。隐藏面设为 inert 并退出辅助技术读取,转过 90° 后移交焦点;Escape 返回,减少动态效果时直接到位。滑块采用单位质量、4.5 Hz 临界阻尼弹簧,拖动由 6 Hz 手部弹簧牵引、8 Hz 端点力限制;松手依据真实位置与速度的投影选择档位,点击直接选档,方向键和 Home / End 覆盖全部档位,取消拖动恢复原选项。模型与强度仅在创建前配置,讨论进度中保持只读。
|
|
177
|
-
|
|
178
|
-
界面只提供默认深色「协作频道」与浅色协作频道。深浅频道共用组件,浅色覆盖同时作用于正文、菜单和弹窗;只有深色频道启用根节点的 `dark` 类。外观设置集中在左下角,窄屏可从房间导航抽屉进入。主题保存在浏览器,已移除或未知主题值回退并保存为深色频道;切换保留房间、筛选与未发送草稿。原独立主题样式和切换入口已删除;HTML 中的隐藏控件仍作为共享控制器的表单适配层,不是可显示的第二套界面。
|
|
179
|
-
|
|
180
|
-
房间管理集中在左上方房间条目自带的「更多操作」菜单,右键条目使用同一菜单,窄屏从房间导航抽屉进入。「新建对话」与「讨论房间」的折叠箭头保持图标中心线及文字起点对齐。重命名、复制阅读入口、导出、移动、存档和删除对应被点击的房间,不切换当前讨论;当前房间菜单另提供编辑议题和刷新消息。房间条目不重复显示消息总数,统计保留在主区域顶栏。顶栏保留搜索、筛选、进度和侧栏开关。
|
|
181
|
-
|
|
182
|
-
深浅频道采用统一的低饱和蓝灰色系,由 `channel.css` 的背景、表面、强调色与分级阴影变量维护。左右侧栏共用底色,三栏标题与主持简报共用顶部表面色;正文底色、卡片与控件用明度差区分,避免各区域使用不同冷暖的灰色。深色卡片保留顶部亮边、底部暗边和局部渐变;浅色使用白色卡片、蓝灰细描边与柔和投影。交互状态共用蓝色强调色,参与者和运行状态保留各自的语义颜色。
|
|
183
|
-
|
|
184
|
-
左右侧栏仅用朝向正文的柔和阴影分界,避免竖向描边与内侧高光线重复叠加。分栏拖柄覆盖接缝,层级高于主持简报、低于浮动预览,不占用布局宽度,仅在悬停、聚焦或拖动时显色。新建入口与导航选中项具有独立边界,右栏状态为轻浮起面板,焦点、参与者及耗时以留白和分隔线分组。消息、共识与主持面板使用较轻阴影,按钮使用短投影,菜单和抽屉使用浮层投影,搜索和表单使用内阴影;按下和禁用时降低或移除投影。输入区聚焦保留面板阴影并叠加焦点边界;发送按钮始终用于发言,无正文或附件时禁用,不随 AI 运行状态切换为停止按钮。讨论的暂停、打断与结束保留在进度看板,自由协作的停止保留在成员菜单。材质调整保留三栏结构和阅读宽度。
|
|
185
|
-
|
|
186
|
-
浮层(菜单、预览、消息工具条、抽屉与弹窗)在深色频道使用毛玻璃:半透明上亮下暗渐变底配合 24px 背景模糊与增艳,边缘只用顶部内侧亮线、底部内侧暗线和向下投影,不加外侧白色辉光。浅色频道的控件本体保持不透明白色,只学习材质边界:蓝灰细描边、内侧顶部高光、内侧底部浅蓝灰暗边和蓝灰多层投影,按控件、面板、浮层三级加强。外观设置的「视觉效果」可切换为精简,写入根节点 `data-effects="reduced"` 并随布局偏好保存在浏览器;精简模式全局关闭背景模糊,深色浮层改为实色,深浅阴影各退为单层,用于保证低配设备的流畅度。浅色频道的发言快速导航不加底板,刻度改为发言者实色、固定 2px 粗细(覆盖脚本动画的内联高度,长度与透明度仍由脚本驱动),轮次号与轮次分隔比辅助文字深一级。
|
|
187
|
-
|
|
188
|
-
选中状态用填色、文字和勾选表达,键盘焦点使用中性边缘,不增加蓝色外框;卡片、模型选择、强度滑杆、模式、时长及编辑区遵循同一规则。加载图标在各处统一为每 3 秒匀速旋转一圈;系统开启“减少动态效果”时仍保留缓慢旋转,以表示请求正在进行,其余动效继续遵循减弱设置。
|
|
189
|
-
|
|
190
|
-
接入页分别交付本地成员的私有配置文件和不含 token 的参与说明,在线成员只给阅读说明;前端快照仅含成员接入方式与文件可用状态。手动创建、邀请、请回直接请求网站,自动成员才经本机启动器。创建与成员变更在请求前保存原编号和凭据,结果不明时沿用;复制说明不代表已送达或接入。项目、工作目录、讨论上限和错误按需展开。暂停后的本场软件连接修正使用独立卡片,页面不推断本机服务在线状态,选择模式或下载配置不会启动讨论。
|
|
191
|
-
|
|
192
|
-
消息区显示议题、原文和主持简报;议题卡的参与配置使用与头像一致的 20px 行高,姓名与主持 / CLI 标签保留字号层级并垂直居中。全自动执行期间,消息流末尾按有效执行显示等待状态:专家复用发言卡片的头像、姓名、轮次与样式,显示“正在唤醒…”或“正在准备回复…”;主持执行使用同宽、更矮、弱化底色和边框的“主持工作”卡,显示头像、姓名及右上角耗时,按交付与进入状态显示“正在唤醒主持人…”或“正在安排下一步…”,不推测内部操作。卡片类型按本次执行职责区分,主持人作为专家回答时仍使用专家等待卡;正式主持发言保留普通消息卡。手动接入专家轮到发言时,即使没有自动执行,也按当前安排显示“等待发言”卡片,提示到该 AI 会话发送启动指令;标注“手动接入”,不显示执行动画或耗时。输入区的“复制启动指令”使用无底色、无描边的复制图标文字按钮,复制成功后原位显示“已复制”。自动成员仅有待处理安排、执行过期、暂停或归档时不显示等待卡片。卡片不计入消息记录,没有消息编号、回复或引用操作;执行成功或取消、手动安排已回答或取消后移除;失败执行保留为时间顺序中的卡片,显示失败原因、执行编号及已记录耗时,刷新、重试和收束后仍可查看。拥有控制凭据的浏览器可在当前失败卡片上点“重试”,沿用保留的安排创建新的执行;旧失败卡不消失,也不会作为 AI 原文计数,历史安排已变化时不提供重试;下一位自动专家实际交付、手动专家轮到发言时显示对应卡片。右侧进度看板的“跳过”取消当前专家本次安排及有效执行,保留排队安排并交给主持人继续安排,不暂停讨论。操作按钮保持单行排列。因执行失败或用户操作而暂停、且仍保留当前安排时,仍可点“跳过”:先取消该安排,再按当前运行版本恢复讨论,唤醒主持人而不重试被跳过的专家;恢复失败时说明安排已跳过,可核对后用“重启”继续。搜索与提及筛选时隐藏,状态更新仅在用户原本位于底部时跟随滚动。标题栏、窄屏标签栏与主持简报合为同一块顶部面板,简报展开内容留在面板内,与收起状态共用背景、边线与投影,不再另起浮层。消息视图中该面板浮在消息列表之上,使用与深色浮层同款的毛玻璃(半透明渐变底、24px 背景模糊与增艳;浅色为白色半透明),滚动经过的卡片在其下模糊可见;消息列表顶部留白、导航与筛选条下移量、跳转与当前消息判定都按收起时的面板高度计算,展开简报只覆盖消息,不推动消息与输入区。其他视图中面板按普通流式布局排列。精简视觉效果下面板改为实色。已存档房间在独立弹窗管理,不改变当前房间和草稿。存档房间仍可阅读、恢复与导出;批量删除作用于当前搜索和筛选结果,执行前确认。
|
|
193
|
-
|
|
194
|
-
所有发言卡片右上角显示耗时。等待卡片使用同一执行的 `runtime.timings` 唤醒与执行秒数之和,从交付唤醒起累计,不计之前排队;本地每秒更新,服务器计时变化时校准,状态结束或切换房间后停止。正式发言的 `elapsed_seconds` 在保存消息的同一事务内按有效执行凭据与原作者关联计算,保存在 `message_timings`,此后翻页、重试和运行结束均不改变。主持发言同样计到各自保存时刻;不接受客户端提供的耗时。用户留言、手动或代发内容及缺少记录的旧消息显示“耗时 —”。升级仅为有精确 `message:ID` 完成结果与计时记录的历史专家发言回填,不按时间邻近推测关联,不覆盖已有耗时。
|
|
195
|
-
|
|
196
|
-
进度看板在宽屏消息页的右侧栏显示(视口 ≥1200px),其他布局用同一组件的抽屉。仅当右侧进度栏不可见时,顶栏显示状态按钮(空间充足时附轮次),点击打开独立进度抽屉,关闭后保留原侧栏布局;右侧栏的展开与收起仍由独立的房间概览按钮控制。状态来自当前安排、本轮发言与 `runtime.participants`;会话编号来自最近一次实际进入的执行,`stale` 表示执行租约过期。模型与思考强度另从本机 `/api/local/room-models` 只读获取本场登记配置,仅返回适配器、模型与强度,继承本机桥接的同源或站点连接鉴权;请求站点固定为已验证的来源,不经过公开网站或改变运行状态。读取结果按房间及运行版本缓存,切换房间丢弃迟到响应;未获取时不使用默认配置猜测。手动模式仅按安排和发言展示进度,不推断离线或伪造执行回执。
|
|
197
|
-
|
|
198
|
-
运行操作按服务器状态开放:暂停、重启、取消安排与提前结束均调用既有接口,业务成功以服务器确认为准。重启只用于已暂停或到期的全自动运行,需要本机接入程序运行;服务器按话题上限截短新截止时间。准备、收束和存档状态不显示不可用操作。界面尺寸与动效参数以组件和样式为准,变更通过 [贡献指南](../.github/CONTRIBUTING.md) 的界面验收要求核对。
|
|
199
|
-
|
|
200
|
-
等待提示文字使用局部明暗流光,文字与卡片位置保持稳定,深浅主题继承原有文字颜色。流光采用 Feel 的 `calm, smooth` 参数(质量 1、刚度 78.167、阻尼 18.567),以恒定驱动力 `k / 8` 与阻尼的解析解推进;循环衔接时亮带完全位于文字之外,不使用固定动画时长。每帧只更新文字的 CSS 变量,不触发消息流重绘;卡片移除或开启“减少动态效果”时取消帧循环并恢复普通文字。
|
|
201
|
-
|
|
202
|
-
管理员账号界面:页面启动及标签页重新可见时读取公开的 `GET /api/admin`,用返回的名称和头像覆盖「用户」身份(所有访客可见,无自定义时仍是 User 与指纹图标),登录、注册、退出、改资料后直接更新身份与快照,不刷新页面。会话令牌只存浏览器 `localStorage` 的 `discuss-admin-session`,由控制器附加到写请求的 `X-Discussion-Admin`,不进入视图快照;会话不过期,前端仅在服务端判定未登录时清除本地令牌。左下角用户块可点击打开「设置」:未注册显示“设置管理员账号”并给出注册表单(安装令牌来源提示;服务端未配置时说明并禁止提交),已注册未登录显示“未登录 · 点击登录”,已登录显示名称、头像(居中裁成 96×96 JPEG 数据 URL,超过 60000 字符则提示过大)、恢复默认头像与退出登录;忘记密码时本地版运行 `python server.py --reset-admin` 后重新注册,Cloudflare 版需清空管理员表。表单校验与服务端规则一致,错误行内展示服务端中文原文。本地站点(`GET /api/admin` 返回 `local: true`)不需要账号:不显示注册、登录、退出,不发送也不保存会话令牌,设置里只保留名称与头像编辑,左下角只显示网站连接状态。没有房间控制凭据且未登录时,存档、恢复、删除被拒会提示“需要登录管理员账号或使用创建该房间的浏览器”。
|
|
203
|
-
|
|
204
|
-
### 网页实时更新
|
|
205
|
-
|
|
206
|
-
浏览器通过 `GET /api/updates` 订阅 SSE。房间、消息、主持安排、运行状态及目录的成功写入提交后发送 `change`,只含空对象,不携带正文、身份或执行凭据;普通执行续租不通知。页面在 100ms 内合并连续通知,复用已有接口读取当前房间和目录,消息仍按最后编号增量读取;请求期间的新通知会再触发一次读取,拖动目录时延后处理。筛选、未发送草稿和原有滚动跟随规则保留。
|
|
207
|
-
|
|
208
|
-
每次连接发送 `ready`,页面重新读取快照补齐断线期间的变化;通知本身不做持久重放。正常连接每 60 秒补读一次,覆盖时间到期、通知丢失等没有可用事件的情况;不支持 SSE、连接中断或云端未配置时沿用每 12 秒轮询。页面隐藏或离开时关闭连接与定时读取,返回前台立即重连补取。流每 15 秒发送一次注释心跳,心跳不会刷新页面。
|
|
209
|
-
|
|
210
|
-
Python 通过进程内条件变量唤醒订阅者,不占用数据库连接,也不以服务器端轮询代替事件。Cloudflare 通过独立部署的 `DiscussionUpdates` Durable Object 统一广播,Pages 的 `UPDATES` 绑定可选;缺失时流接口返回 204,让浏览器停止无效重连。绑定故障不改变已提交写入的成功回执,补读负责恢复。域名别名共享同一通道;多个数据库共用一个通知 Worker 时用 `UPDATES_CHANNEL` 隔离,配置见[部署指南](deployment.md#网页推送可选)。
|
|
211
|
-
|
|
212
|
-
### 目录状态同步
|
|
213
|
-
|
|
214
|
-
目录操作先更新本地界面、再后台保存,只限制同一对象或顺序的冲突操作。回执按字段合并,失败仅回退对应操作;保留后来发生的编辑、导航和草稿,写入代次防止较早的读取覆盖新状态。其他目录写入期间仍可刷新当前对话内容。讨论发言、专家安排和运行控制不采用这种乐观成功状态。
|
|
215
|
-
|
|
216
|
-
新建文件夹在编号返回前不可作为归类目标;创建对话在服务器确认前不展示可用房间入口,完成后不抢占后来发生的导航。删除确认后立即从目录移除,失败恢复;存档保留当前原文和草稿。
|
|
217
|
-
|
|
218
|
-
未归类房间直接平铺,文件夹内房间缩进显示。手动顺序与最近发言排序分开保存;最近发言模式可跨文件夹移动,组内排序需切回手动模式。排序偏好保存在浏览器,不改写服务器保存的手动顺序。
|
|
219
|
-
|
|
220
|
-
## 测试与已知边界
|
|
221
|
-
|
|
222
|
-
`tests/test_server.py` 与 `tests/test_discuss.py` 验证临时 SQLite、真实 HTTP 和 CLI 子进程;可显式指向隔离的本地 Worker 复用行为测试。Node 测试覆盖 Markdown、界面状态和 Pages 路由。具体命令见 [贡献指南](../.github/CONTRIBUTING.md)。
|
|
223
|
-
|
|
224
|
-
未提供用户登录、讨论并行发言队列或跨站数据同步。自动执行保护不替代账号认证。Skill 行为、浏览器实际送达、软件原生任务启停与 API 账号的真实可用性必须通过实际环境验证;测试替身成功不能替代这些外部结果。
|
|
225
|
-
|
|
226
|
-
## 讨论选项与智能调度记录
|
|
227
|
-
|
|
228
|
-
- `round_budgets`:程序统计轮次的预算(`base` 为本段计数起点的专家观点数,`cap` 为本段轮次上限、0 表示取 `discussion_limits.max_rounds`,`extra` 为旧版用户累计追加轮数,追加功能已移除)。建房时写入;没有记录的旧房间沿用按花名册逐轮统计的旧口径。房间视图的 `limits` 返回 `segment_rounds`、`round_cap`、`extra_rounds`。
|
|
229
|
-
- `discussion_options`:建房时的讨论选项,目前只有 `named_followup`(0/1,建房 `named_followup` 布尔值,默认 false,计入建房幂等比对)。没有记录的旧房间视为 true;房间视图返回 `named_followup`。
|
|
230
|
-
- `scheduler_decisions`:讨论中每次智能调度判断的可见记录(`kind` 为 `interjection` / `round`,`label`、`confidence`、`applied`、`provider`、`after_message_id`),由接入程序用运行控制凭据 `POST scheduler-decisions` 写入,不是消息,不影响轮次与暂扣;房间视图 `scheduler_decisions` 返回最近 100 条;每条可带 `detail`(独立表 `scheduler_decision_details`,由 `POST scheduler-decisions` 的 `detail` 写入:每个问题的题目说明、选项,以及决策模型选了哪项和各选项概率,最多 4 项、4000 字,不含房间消息摘录,不随备份导出),网页默认折叠,点开才显示“问 / 答”。房间视图另附 `host_arrangements`:由 `discussion_turns` 按同一批创建时间分组得到的主持人安排(不含邀请追加的开场安排,也不含已由智能调度写入决策的那一批),`after_message_id` 取该批之前最新的回答(开场安排前没有回答,取 0,显示在议题卡片下方),网页在该回答后显示与智能调度卡片同样式的“主持人安排:A → B → C”一行,点开才逐条显示专家与主持人写的任务说明(`turns`);没有可跟随的回答(讨论开场)时不显示。不新增表。
|
|
231
|
-
- 两表都是独立表,D1 重新应用 schema 即可;备份、导入(`after_message_id` 随消息重编号)与删除一并处理。智能调度代主持人执行的写入不新增权限,沿用该主持事件的执行凭据。
|
|
232
|
-
|
|
233
|
-
## 自动运行协议
|
|
234
|
-
|
|
235
|
-
`discussion_limits` 保存房间上限及开始/收束时间。房间响应附 `limits` 的只读进度;`POST /api/rooms/:id/limits` 由声明的主持人或用户检查并执行到限处理,沿用房间凭据保护。配置通过创建房间/更新话题的 `limits` 对象写入,仅准备或暂停阶段可修改;时钟不重置。SQLite 在写事务中校验,D1 在同一批次断言参数版本、消息截止及时间边界,避免并发越界。到限处理取消未完成安排、撤销旧代次并暂停;总结需真实主持消息与阶段更新,不由服务器生成。GET 不改变阶段。没有 `force` 和“总结收尾”:用户要提前结束,直接用 `POST discussion {action: end}`。
|
|
236
|
-
|
|
237
|
-
启动事务也校验话题阶段、参与配置、归档状态及上限版本;校验后发生变化则整批回滚。迁移数据中的专家 JSON 按规范化值比较,避免序列化空格差异导致误拒绝。恢复收束采用原收束截止、原运行截止和总截止的最早值;仅修改单次执行时限保留收束时钟。未计时的旧活动房间在首次自动启动时补记开始时间。
|
|
238
|
-
|
|
239
|
-
`backend/runtime.py` 实现 Python 运行控制;Worker 实现同一协议,SQL 触发器与业务保存处于同一事务。正式安排完成主持步骤并入队专家;关联答案完成专家步骤并入队主持。用户留言和约束修改可入队主持;摘要和心跳不触发 AI。领取主持工作时合并更早的待处理主持事件。暂停、收束、归档或参与配置变化撤销旧资格;恢复只重建当前工作,不重放历史。
|
|
240
|
-
|
|
241
|
-
所有新建房间均须提供房主控制凭据及与参与者名单一致的 `members`:每项包含 `name`、独立随机 `token`、`access`(runner/manual/online),可选 `adapter`、`session_mode`。服务端仅保存哈希;同一建房 `submission_id` 的重试必须保持控制凭据、成员配置和成员凭据一致。`runtime_mode=automatic` 在创建事务中初始化 ready 运行;省略或 manual 不启动运行,但仍有成员鉴权。`GET/POST /api/rooms/ROOM/runtime` 查询或按 `revision` 配置、启动、暂停、停止,POST 仅房主可用;`deadline=0` 表示不限运行总时长,非零值须为未来 24 小时内的 Unix 秒数,且不能延长话题或收束截止。暂停保存原阶段。`GET events?after=ID` 仅控制凭据可读,每页最多 100 条;`POST events/ID` 接受 `reserve/enter/view/renew/fail`,程序持久化执行令牌后 reserve,AI 用令牌 enter 一次,业务保存完成执行,不提供 finish。
|
|
242
|
-
|
|
243
|
-
running 状态即使只有 pending 事件,也必须先暂停才能重新启动或配置模式。网页模式选择先读取最新状态,重试复用既有控制凭据;操作始终绑定点击时的房间,避免慢响应影响后来选中的房间。
|
|
244
|
-
|
|
245
|
-
两种房间的用户写入、运行控制及成员管理都必须携带建房时的房主控制凭据;`actor`、`author`、`submitted_by` 只是声明。`room_modes` 为所有新房间保存控制哈希,`room_members` 保存每个实例的独立成员哈希、能力和主持职责。讨论房间的手动本地成员用自己的持久成员凭证;在线成员不能自行写入,由房主或获授主持职责的本地成员代发。成员主持可维护资料、摘要、阶段和安排,不能改名单、授予主持权限、控制自动运行或删除房间。房主凭据不能冒充本地成员提交观点。自动讨论里本机唤醒的成员仍要求本次有效执行凭据,持久成员凭证不能替代执行租约;手动接入的专家(`access=manual`、`adapter=manual`,主持人除外)用自己的成员凭证回答安排,其待派发事件由接入程序跳过,回答保存后由触发器完成事件并唤醒主持人。
|
|
246
|
-
|
|
247
|
-
房间响应的 `runtime.timings` 汇总当前运行各执行的阶段耗时:排队(事件产生到 reserve)、唤醒(reserve 到 AI enter)、执行(enter 到完成、撤销或失败),未结束的阶段计到当前时间;返回最近 20 条与全部合计,进度看板据此展示。升级前的执行没有计时记录,不计入。 `runtime.failures` 单独返回本房间全部失败执行的公开字段(编号、参与者、职责、安排、原因、创建时间与交付到失败的固定耗时),不随新执行覆盖,也不受最近 20 条计时截断影响;旧执行没有计时数据时耗时为 null。前端重试前重读房间,核对最新失败执行与当前保留的安排,再使用现有 runtime start 和 revision 校验恢复;无需新增表或更改执行重试协议。
|
|
248
|
-
|
|
249
|
-
每房间最多一个 dispatched/started 执行;写入同时校验运行代次、期限、身份及职责。租约 120 秒,通知程序每 30 秒续租,单步采用 discussion_limits.execution_minutes,新建默认 0(不限),非零值为 1–60 分钟;设置了整场截止时取更早期限。不限单步时长仍要求有效的租约、运行代次和身份,续租不设隐含执行截止。schema 重建建房触发器并显式写入 0,使旧表默认值为 20 的数据库中新建房间也默认不限,已有房间的配置保持不变。通知受理未必开始,开始未必完成。运行和最新执行状态对网页可见,控制哈希与执行哈希不公开。备份保持原有讨论数据范围,不导出运行凭据或本机绑定。
|
|
250
|
-
|
|
251
|
-
## 自由协作协议
|
|
252
|
-
|
|
253
|
-
执行报告:`free.executions` 返回最近 100 次执行的白名单字段,不含凭据或 `client_id`;包含 `cwd`、数组 `changes`、`changes_recorded`、完整 `request_ids`、`answer_id` 与首条 `request_message_id`。报告不依赖最近请求列表是否截断:完成的报告挂在回答下,失败/取消的报告挂在原请求下。`changes_recorded=false` 表示非 Git 或未取得报告,不能当成无变化;已上报的 `[]` 才表示未观察到文件变化。单次时限与续租中断使用不同失败原因。
|
|
37
|
+
## 参与者身份
|
|
254
38
|
|
|
255
|
-
|
|
39
|
+
AI 平台(Codex、Claude、Gemini、DeepSeek、Qwen、Grok)是类型,房间里的参与者是它的实例,名称为平台名或“平台名-后缀”(如 `Claude-评审`)。每个实例有自己的会话、模型、思考强度与凭证,名称在同一房间内唯一。
|
|
256
40
|
|
|
257
|
-
|
|
258
|
-
- **成员**:`room_members` 以 `id` 为稳定主键,`access` 为 `runner`(接入程序投递)、`manual`(成员自行读写)或 `online`(只读、原文代发),`session_mode` 标记是否借用用户正在使用的会话。房主用控制凭据 `POST members` 运行中加入成员,`POST members/ID {action}` 让成员离开(`away`,作废其进行中执行与未投递请求)、请回(`active`,必须提供不同于原凭证的新凭证,旧凭证副本不再有效)或移除(`remove`,保留历史发言)。同一成员凭证不能复用于其他成员或房主;相同凭证与配置的邀请、请回重试返回已有结果。会话续接由本机接入程序负责,服务端不保存会话绑定;唯一例外是手动成员上报的会话:带凭据的请求可带请求头 `X-Discussion-Session` 与 `X-Discussion-Session-Client`,`message post` 也可在正文带 `session_id` 与 `session_client`(CLI 读取 `CLAUDE_CODE_SESSION_ID` 或 `CODEX_THREAD_ID`,取值 `claude` / `codex`),服务端在 `member_sessions` 保存该手动成员最近上报的会话,成员视图以 `reported_session` 暴露。房主用 `POST members/ID {action:"adopt", adapter:"claude", session_id}`(须与上报一致、仅全自动讨论中在场的手动成员)把成员改为 `runner`、`borrowed` 并清除上报,本机接入程序据此接管。两套后端行为一致。
|
|
259
|
-
- **手动成员注册**:`member_registrations`(`member_id`、`room_id`、`token_hash`、`registered_at`)记录手动成员自己发起的注册,`POST /api/rooms/ID/register` 只接受该成员自己的凭证(房主凭据与他人凭证返回 403),重复调用保留首次时间。记录绑定注册时的凭证哈希:请回换发新凭证后旧记录不再匹配,视图中的 `registered_at` 回到空串,须重新注册。会话 ID 不属于注册内容,仍由 CLI 头部采集进 `member_sessions`;`adopt` 要求已注册。讨论房间的成员视图与自由房间 `free.members` 都带 `registered_at`;新表为独立表,D1 重新应用 schema 即可。
|
|
260
|
-
- **请求**:用户消息按结构化接收者 `to`(成员名)为每位成员生成一条请求;省略且仅有一位在场成员时默认点名该成员,多位成员时交给主持人(`route`),只有在线成员而没有主持人时只作留言,不广播。网页只从正文中代码与引用之外的 `@成员` 生成 `to`,没有 @ 时省略 `to`。请求状态为 `pending`(待投递)→ `delivering`(处理中)→ `completed` / `cancelled` / `failed`。
|
|
261
|
-
- **取消原因**:`request_cancel_reasons`(`request_id`、`reason`)是独立侧表,记录请求被取消的原因(`user_stop`、`member_away`、`member_removed`、`rerouted` 智能调度改派或换主持人、`no_reply` 无需回应、`archived` 房间归档),房间视图的每条请求带 `cancel_reason`;此表加入前已取消的请求没有记录,旧库重复执行 schema 即可。归档自由房间会作废其未完成的执行与排队请求;主持人离开、被移出或被更换时,其尚未开始的 `route` 请求由新主持人重新接收。
|
|
262
|
-
- **执行**:`member_executions` 每位成员最多一个 `dispatched/started`(部分唯一索引),不同成员可并行。接入程序用控制凭据 `GET inbox`、`POST executions {member_id, client_id}`(同一 `client_id` 返回同一执行)把成员的全部待投递请求打包;`POST executions/ID {action}` 支持 `renew`(租约 120 秒,不超过单次执行时限,默认 20 分钟;可附 `activity` 与 `activity_at`,即软件最近一次输出的阶段与秒级时间,阶段为 `thinking`/`command`/`tool`/`editing`/`writing` 或空,时间不早于执行创建、超过服务器时间按当前时间记,不附则保留上次报告;还可附 `notes: [{seq, text}]`,每次最多 10 条,`seq` 为 1–50、`text` 为 1–500 字,同一执行同一 `seq` 只存一次,重试续租不重复。房间视图只给执行附 `notes` 条数,正文由 `GET executions/ID/notes` 读取,读取权限与房间视图相同)、`fail`(请求标记失败)和 `release`(仅确认未送达时退回队列)。租约过期的执行在下次读取或领取时标记失败,不自动重发。
|
|
263
|
-
- **主持人与任务链**:用户是需求方,不担任主持人。只要有在场的非在线成员,房间就保持恰好一位主持人:成员变动时,离开或移出的主持人失去角色,由第一位在场的 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 完成回交,回交请求按(派活消息,主持人)唯一,重复执行不会重复唤醒。
|
|
264
|
-
- **发言顺序**:房间视图 `speaking_order`(亦可 `GET speaking-order` 单独读取)在两种模式下给出同一结构:`kind`、`items[{name, state}]`(`done` / `speaking` / `queued` / `idle`,依次为已发言、正在发言、待发言、其余成员)、可调整的 `movable`、房间默认顺序 `default` 与 `revision`。自由协作有未完成的依次点名时 `kind=queue`(`message_id` 为该消息),否则 `kind=default`、全体成员按默认顺序为 `idle`;讨论为 `kind=turns`,由本轮已发言者、当前安排与本轮的排队安排组成;排队按轮切分(专家在正在填充的一轮里第二次出现即开始下一轮),后续轮次以 `later`(每轮一组名字)列出,不在 `movable` 中,调序请求带上它们返回 400。房主用控制凭据 `POST speaking-order {revision, order}` 调整:缺 revision 400,已变化 409,`order` 须恰好包含 `movable`(缺少或多出的名字列在 400 中)。`queue` 删除并按新顺序重插该消息尚未请求的 `free_queue` 行;`turns` 只在本轮排队安排(队列最前面的若干行)的 id 之间整体交换内容(专家、问题、提交编号、创建时间),id 顺序即接力顺序,原计划的提交编号仍对应原内容;`default` 写入 `speaking_orders`(成员编号 JSON 数组,未列出者排在后面,主持人在前、其余按编号)。主持人的 `turn roster` 按此顺序列出专家。
|
|
265
|
-
- **全体点名**:房主消息带 `to`(多于一人)且 `order: "auto"`(网页 `@全体成员` / `@all`)、主持人由本机唤醒(`access=runner`)时,只给主持人建一条 `purpose=group` 请求;主持人不由本机唤醒时按普通点名同时请求。`group` 请求从不投递(预留执行时排除,`inbox` 仍列出),由接入程序用 `POST recommendations/MESSAGE_ID {action:"all"|"sequential"}` 安排:`all` 与未点名消息的全体相同,把它原地改为 `all` 并为其他成员各建一条 `all` 请求;`sequential` 删除这条请求,再把在场成员按房间默认顺序写入 `free_queue`(同下条依次点名)。对 `group` 请求用其他 action 返回 400,已安排过返回 409 或 404(依次安排后已无该请求)。主持人更换、离开或被移除时,尚未安排的 `group` 请求与 `route` 一样交给新主持人。
|
|
266
|
-
- **依次点名**:房主消息带 `to` 且 `sequential: true`(网页手动输入 `@全体依次` / `@all-seq`)时把被点名成员按房间默认顺序写入 `free_queue`,只给第一位建 `ask` 请求(`created_by=user`、`chain_id` 为原消息、不进 `free_batches`)。每次结算批次(回答、失败、打断、离开、租约过期)时,该消息已没有 `pending` / `delivering` 请求就为下一位尚未请求的成员建请求;尚未请求且已不在场的成员被删去。已请求的行保留,供发言顺序显示已发言者。Python 与 Worker 用相同的集合 SQL 推进;`free_queue` 与 `speaking_orders` 不进备份,删除房间时一并清除(独立表,D1 重新应用 schema 即可)。
|
|
267
|
-
- **智能调度推荐**:接入程序用控制凭据 `POST recommendations {message_id, member_id, provider, confidence}` 为一条已交给主持人(`route`)的未点名消息记录推荐对象(在场的非该主持人成员,含手动与在线成员),写入 `member_recommendations`(每条消息一条,重复提交忽略;独立表,D1 重新应用 schema 即可),有效期 60 秒,主持人的请求不受影响。房主用 `POST recommendations/MESSAGE_ID {action:"adopt", mode}` 采纳:`interrupt` 取消主持人这条请求(已投递则作废主持人本次整个执行),`after` 等主持人这条请求进入终态。两种方式都先置为 `waiting`,由 `release`(采纳时与每次 `inbox` 读取时执行)在主持人请求已终态时为推荐成员建一条 `ask` 请求(`created_by=user`、`chain_id` 为原消息),状态改为 `adopted`。过期 5 秒宽限后采纳返回 409,重复采纳返回原结果。同一入口的 `{action:"all"}` / `{action:"none"}` 不经推荐表:全体把主持人尚未开始的 `route` 请求原地改为 `purpose=all`(每条消息每位成员只有一条请求),并为其余在场成员各建一条 `all` 请求(`created_by=user`、`chain_id` 为原消息、不进 `free_batches`,因此没有 `handoff`);无需回应取消该 `route` 请求;主持人请求已不是 `pending` 时返回 409。主持人本次回答的请求全是 `all` 时,带 `to` 的回答被拒绝(400),避免重复请求其他成员。房间视图 `free.recommendations` 带成员名、状态、方式、置信度与剩余秒数 `expires_in`;备份、导入(仅作历史,不再生效)与删除一并处理。`inbox` 的待投递项带 `purpose` 供接入程序识别。`inbox.members` 只列由本机唤醒的成员;手动与在线成员另列于 `inbox.observers`(含待处理请求,供智能调度识别主持人与推荐对象),接入程序从不向它们投递。
|
|
268
|
-
- **回应建议**:普通成员新发言的结构化 `to` 与正文在场成员 @ 合并保存到 `member_suggestions`(原消息编号、稳定成员编号),排除自己、代码与引用;不会创建请求。消息读取返回 `suggestions`,包含目标在场状态及已转换的请求编号。房主用 `POST suggestions {message_id,member_id}` 转换:同一事务记录用户确认消息,调用现有建请求路径,`message_id` 保留原回答、`chain_id` 使用用户确认消息编号、`created_by=user`。`UNIQUE(message_id,member_id)` 使重复或并发点击只生成一个请求及一条确认,终态不重新派发。权限、在场状态和未存档条件在写入事务内校验;Worker 使用批次断言。升级须应用包含新表的 schema;Python 重启时自动建表,D1 发布前显式执行。已有历史消息不补写建议。
|
|
269
|
-
- **讨论中的直接提问**:讨论房间的用户消息带 `to`(点名成员名列表)时,除保存消息外为每位被点名的在场成员建一条 `ask` 请求(`created_by=user`,不入 `free_batches`,不占安排);讨论运行中点名主持人不重复建请求。`GET inbox`、`POST executions` 与 `executions/ID` 对讨论房间同样可用,成员以 `execution_id`(接入程序交付)或 `request_ids`(手动 / 代发)提交 `chat` 回答,走自由房间的写入校验。同一成员的 `member_executions` 与 `discussion_events` 互斥(任一方领取时另一方有进行中则返回 409)。房间视图 `asks` 给出最近请求与执行,供消息旁的状态徽标。本机服务以 `DiscussionWithQuestions`(`runner/discussion_service.mjs`)并行运行讨论 runner 与 `FreeRoomRunner`,共用同一成员会话绑定。
|
|
270
|
-
- **在线成员**:`access=online` 的成员只读,不进入 `inbox`,不能用自己的凭证发言;房主以控制凭据代发其回答(`author` 为该成员、`submitted_by` 为用户、`source=proxy`),可带 `request_ids` 关闭它的待处理请求。
|
|
271
|
-
- **关联房间(旧)**:早期从自由房间发起的讨论子房间带 `parent_room_id`,记录在 `room_links`,视图仍列出 `linked_rooms` 供查看历史;新房间不再创建关联子房间,讨论在房间内开启(`discussion` 接口)。
|
|
272
|
-
- **手动成员**:手动成员不进入 `inbox`,也不能被领取执行;它用成员凭证读写,回答时可带 `request_ids` 关闭自己的待处理请求,不带则是普通发言、不关闭任何请求。
|
|
273
|
-
- **改动记录**:接入程序在执行前后对成员 cwd 做 Git 工作区快照(状态与内容哈希),执行结束且进程退出后用 `report {changes}` 记录一次。房间视图的 `free.conflicts` 列出不同成员在同一 cwd、时段重叠的执行均观察到变化的文件,只作“可能冲突”提示;非 Git 目录不记录。快照也会包含其他进程在同一目录的写入,不能确定修改者;分工修改不同文件仍可能出现提示,不代表已发生覆盖。
|
|
274
|
-
- **回答**:接入程序投递的成员回答须带 `execution_id` 并使用成员凭证;消息保存、执行完成、请求完成和成员已读游标在同一事务中写入。执行结束、被打断或过期后的回答返回 409;缺少执行编号返回 400,不能改作普通发言。消息类型为 `chat`,不计入讨论轮次。
|
|
275
|
-
- **打断**:`POST members/ID/interrupt`(控制凭据)作废该成员的进行中执行及所有未投递请求,不会自动重新开始;成员的已读游标不前移,下一次请求从同一位置读起。执行失败只影响该成员的请求,房间不暂停。
|
|
276
|
-
- **讨论成员变更**:房主修改讨论名单或读写能力时,为新增或改变能力的成员提供新凭证,未变成员保留原凭证;删去的成员立即停用。成员主持不能自行扩大名单或更改主持职责。运行中名单只能经 `POST members`(邀请,接入方式须与推进方式一致)与 `POST members/ID {action: away|active|remove}` 变更,“编辑议题”改名单返回 400。暂离从 `experts` 中移出、成员行保持 `away`,同事务取消它进行中与排队的安排;请回须新凭证;主持人不能暂离或移除;有进行中安排的专家仅在话题暂停时可暂离或移除。已保存的观点不能删除,没有 `redo` 动作。`experts` 变化不再暂停运行:触发器 `runtime_experts_changed` 在无进行中安排和主持执行时为主持人生成 `members:` 事件,纯新增专家保留排队安排;邀请事务通过 `queue_invited_expert` 将新专家的开场安排追加为 `queued`,到它轮次才产生执行。无现有安排、暂停或收束期间不自动创建安排,由主持人恢复推进时排在本轮末尾。暂离、移除、请回仍作废排队并重排;只有更换主持人才暂停并换代。本机接入程序接受只新增参与者的配置变化,已绑定会话的条目仍须保持不变。
|
|
277
|
-
- **范围**:成员名称是 AI 实例名(见下文“参与者身份”),一个房间最多 12 位 AI。早期创建的自由房间表在 Python 启动时补齐新列。Cloudflare Worker 实现同一协议:成员与执行状态的校验放在同一 D1 批次的断言中,校验后状态变化则整批回滚。备份只导出成员名称、状态与请求,不导出凭据哈希。
|
|
41
|
+
## 凭据与数据边界
|
|
278
42
|
|
|
279
|
-
|
|
43
|
+
- 建房时由浏览器生成房间控制凭据与每位成员的凭证,服务端只保存哈希。
|
|
44
|
+
- 用户写入需要房间控制凭据;成员只能以自己的身份发言;自动执行另需本次有效的执行凭据,执行失效后的迟到回答会被拒绝。
|
|
45
|
+
- 在线 AI 与 CLI 通过纯文本页面 `/r/ROOM_ID` 按需读取,不一次性加载全部历史。
|
|
46
|
+
- 网站目前没有读取权限隔离,能访问站点的人可以阅读房间、附件与备份;详见 [安全说明](../.github/SECURITY.md)。
|
|
47
|
+
- 本机路径、软件配置、会话绑定与 API 密钥只保存在本机服务目录。
|
|
280
48
|
|
|
281
|
-
|
|
49
|
+
## 技术栈
|
|
282
50
|
|
|
51
|
+
Python 3.10+(无第三方依赖)、Node.js 24+、Vue 3 + Nuxt UI、Vite、Cloudflare Pages / D1 / R2。第三方许可见 [第三方声明](THIRD_PARTY_NOTICES.md)。
|