ai_co_discuss 0.1.10 → 0.1.11
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-participant/SKILL.md +4 -0
- package/README.md +5 -3
- package/backend/runtime.py +20 -16
- package/backend/schema.sql +41 -5
- package/backend/server.py +285 -22
- package/docs/THIRD_PARTY_NOTICES.md +2 -0
- package/docs/architecture.md +37 -14
- package/docs/cli.md +21 -3
- package/docs/deployment.md +33 -3
- package/docs/runner.md +17 -9
- package/docs/workflow.md +4 -4
- package/package.json +7 -6
- package/runner/codiscuss.mjs +1 -0
- package/runner/discuss.py +111 -11
- package/runner/discussion_adapters.mjs +8 -0
- package/runner/discussion_launch.mjs +50 -9
- package/runner/discussion_runner.mjs +22 -8
- package/runner/discussion_service.mjs +12 -5
- package/runner/discussion_sidecar.mjs +1 -1
- package/web/app.js +441 -76
- package/web/backup.js +6 -5
- package/web/index.html +17 -9
- package/web/markdown.css +1 -1
- package/web/modern.css +1 -1
- package/web/modern.js +6485 -5844
- package/web/pdfjs/LICENSE +177 -0
- package/web/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
- package/web/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
- package/web/pdfjs/cmaps/78-H.bcmap +0 -0
- package/web/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
- package/web/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
- package/web/pdfjs/cmaps/78-V.bcmap +0 -0
- package/web/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
- package/web/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
- package/web/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
- package/web/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
- package/web/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
- package/web/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
- package/web/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
- package/web/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
- package/web/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
- package/web/pdfjs/cmaps/Add-H.bcmap +0 -0
- package/web/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
- package/web/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
- package/web/pdfjs/cmaps/Add-V.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
- package/web/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
- package/web/pdfjs/cmaps/B5-H.bcmap +0 -0
- package/web/pdfjs/cmaps/B5-V.bcmap +0 -0
- package/web/pdfjs/cmaps/B5pc-H.bcmap +0 -0
- package/web/pdfjs/cmaps/B5pc-V.bcmap +0 -0
- package/web/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
- package/web/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
- package/web/pdfjs/cmaps/CNS1-H.bcmap +0 -0
- package/web/pdfjs/cmaps/CNS1-V.bcmap +0 -0
- package/web/pdfjs/cmaps/CNS2-H.bcmap +0 -0
- package/web/pdfjs/cmaps/CNS2-V.bcmap +3 -0
- package/web/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
- package/web/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
- package/web/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
- package/web/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
- package/web/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
- package/web/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
- package/web/pdfjs/cmaps/EUC-H.bcmap +0 -0
- package/web/pdfjs/cmaps/EUC-V.bcmap +0 -0
- package/web/pdfjs/cmaps/Ext-H.bcmap +0 -0
- package/web/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
- package/web/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
- package/web/pdfjs/cmaps/Ext-V.bcmap +0 -0
- package/web/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
- package/web/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
- package/web/pdfjs/cmaps/GB-H.bcmap +4 -0
- package/web/pdfjs/cmaps/GB-V.bcmap +0 -0
- package/web/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
- package/web/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
- package/web/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
- package/web/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
- package/web/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
- package/web/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
- package/web/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
- package/web/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
- package/web/pdfjs/cmaps/GBT-H.bcmap +0 -0
- package/web/pdfjs/cmaps/GBT-V.bcmap +0 -0
- package/web/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
- package/web/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
- package/web/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
- package/web/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
- package/web/pdfjs/cmaps/H.bcmap +0 -0
- package/web/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
- package/web/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
- package/web/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
- package/web/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
- package/web/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
- package/web/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
- package/web/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
- package/web/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
- package/web/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
- package/web/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
- package/web/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
- package/web/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
- package/web/pdfjs/cmaps/Hankaku.bcmap +0 -0
- package/web/pdfjs/cmaps/Hiragana.bcmap +0 -0
- package/web/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
- package/web/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
- package/web/pdfjs/cmaps/KSC-H.bcmap +0 -0
- package/web/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
- package/web/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
- package/web/pdfjs/cmaps/KSC-V.bcmap +0 -0
- package/web/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
- package/web/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
- package/web/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
- package/web/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
- package/web/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
- package/web/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
- package/web/pdfjs/cmaps/Katakana.bcmap +0 -0
- package/web/pdfjs/cmaps/LICENSE +36 -0
- package/web/pdfjs/cmaps/NWP-H.bcmap +0 -0
- package/web/pdfjs/cmaps/NWP-V.bcmap +0 -0
- package/web/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
- package/web/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
- package/web/pdfjs/cmaps/Roman.bcmap +0 -0
- package/web/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
- package/web/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
- package/web/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
- package/web/pdfjs/cmaps/V.bcmap +0 -0
- package/web/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
- package/web/pdfjs/iccs/CGATS001Compat-v2-micro.icc +0 -0
- package/web/pdfjs/iccs/LICENSE +116 -0
- package/web/pdfjs/pdf.min.mjs +28 -0
- package/web/pdfjs/pdf.worker.min.mjs +28 -0
- package/web/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
- package/web/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
- package/web/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
- package/web/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
- package/web/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
- package/web/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
- package/web/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
- package/web/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
- package/web/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
- package/web/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
- package/web/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
- package/web/pdfjs/standard_fonts/LICENSE_LIBERATION +363 -0
- package/web/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
- package/web/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
- package/web/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
- package/web/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
- package/web/pdfjs/wasm/LICENSE_JBIG2 +196 -0
- package/web/pdfjs/wasm/LICENSE_OPENJPEG +39 -0
- package/web/pdfjs/wasm/LICENSE_PDFJS_JBIG2 +13 -0
- package/web/pdfjs/wasm/LICENSE_PDFJS_OPENJPEG +22 -0
- package/web/pdfjs/wasm/LICENSE_PDFJS_QCMS +19 -0
- package/web/pdfjs/wasm/LICENSE_QCMS +21 -0
- package/web/pdfjs/wasm/jbig2.wasm +0 -0
- package/web/pdfjs/wasm/jbig2_nowasm_fallback.js +15 -0
- package/web/pdfjs/wasm/openjpeg.wasm +0 -0
- package/web/pdfjs/wasm/openjpeg_nowasm_fallback.js +17 -0
- package/web/pdfjs/wasm/qcms_bg.wasm +0 -0
- package/web/pdfjs/wasm/quickjs-eval.js +16 -0
- package/web/pdfjs/wasm/quickjs-eval.wasm +0 -0
- package/web/style.css +0 -574
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"boundaries": "讨论边界",
|
|
25
25
|
"host": "Codex",
|
|
26
26
|
"experts": { "Codex": "cli", "Claude": "cli" },
|
|
27
|
-
"limits": { "max_rounds": 3, "max_minutes": 60, "execution_minutes":
|
|
27
|
+
"limits": { "max_rounds": 3, "max_minutes": 60, "execution_minutes": 0, "on_limit": "pause", "summary_minutes": 5 }
|
|
28
28
|
},
|
|
29
29
|
"participants": {
|
|
30
30
|
"Codex": { "adapter": "codex" }
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
}
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
此示例的主持在 Codex App;若当前是独立 Codex CLI,应使用 `codex_cli`。可执行文件、App 来源聊天或 Sidecar 等缺项仍须来自实际本机配置。主持也须出现在 `experts` 中并设为 `cli`。示例中的上限不能覆盖用户已经指定的参数;总时长为 0
|
|
35
|
+
此示例的主持在 Codex App;若当前是独立 Codex CLI,应使用 `codex_cli`。可执行文件、App 来源聊天或 Sidecar 等缺项仍须来自实际本机配置。主持也须出现在 `experts` 中并设为 `cli`。示例中的上限不能覆盖用户已经指定的参数;总时长为 0 时可不限总时长运行,按轮次上限或手动停止结束。单次执行默认 0(不限),主动设置的单次执行超时仍有效。只有用户另设本次时长时才提供顶层 `minutes`,不能用它延长房间既定截止。
|
|
36
36
|
|
|
37
37
|
已有房间改为指定顶层 `room_id`,省略 `room`;受保护房间另提供原私有配置的 `access_file` 路径,不读取或打印令牌。首次启动命令不替代已暂停房间的继续操作,已有运行按当前房间状态接续。
|
|
38
38
|
|
|
@@ -42,6 +42,8 @@ description: "AI CoDiscuss 的通用参与技能,所有参与者(包括主
|
|
|
42
42
|
|
|
43
43
|
增量读取用实际已读编号 `message list ROOM --after ID`,按 `next_after` / `has_more` 续读;`latest_id` 不等于已读。筛选后的游标只代表该查询范围,不能用来声称已读全场。阶段和安排以最新服务端状态为准。
|
|
44
44
|
|
|
45
|
+
消息的 `attachments` 提供文件名、类型、大小与编号;需要核对内容时用 `attachment view ROOM ID`、`attachment download ROOM ID --dest 明确的新文件路径`,再用当前软件的实际工具读取。独立阶段仍遵循上述材料边界;下载或看见文件名不等于已读,无法解析时如实说明。
|
|
46
|
+
|
|
45
47
|
## 发言权与提交边界
|
|
46
48
|
|
|
47
49
|
专家只回答属于自己的当前未完成安排(pending/notified),使用真实 `--turn`;没有安排就等待,不自行接管主持。提交前核对阶段、安排和上限。暂停、收束、`limits.reached` 或 `limits.closing` 时不新增专家观点,保留草稿;主持人按主持技能处理到限或总结。
|
|
@@ -56,6 +58,8 @@ description: "AI CoDiscuss 的通用参与技能,所有参与者(包括主
|
|
|
56
58
|
|
|
57
59
|
观点用 `message post`,摘要用 `--summary` / `--summary-file`,完整正文优先 UTF-8 `--body-file`;自己的观点署名自己。主持消息用 `moderation`。代发保留真实作者、完整原文及引用关系,摘要归实际撰写者,不把自己的改写冒充对方回答。
|
|
58
60
|
|
|
61
|
+
需要提交原文件时,先 `attachment upload ROOM --as 本人 --file FILE --submission-id 上传编号`,再把回执中的附件 ID 通过 `message post --attachment ID` 关联,可重复指定(最多 10 个,单个 20 MiB)。受保护上传带本次 `--access-file`,须在回答完成、执行资格结束前上传。上传和消息分别保存提交编号;结果不明用 `attachment list ROOM --submission-id 上传编号` 核对,重试沿用原文件与原编号。
|
|
62
|
+
|
|
59
63
|
创建房间、安排、消息前保存 `--submission-id` 与原内容;同次重试沿用,新内容换新编号。成功须核对服务端返回的持久记录、作者和关联;返回不含所需证据时按编号补读,避免重复获取同一份已确认内容。答案落库后只读核验即可结束,不补启停回执或使用旧资格继续写入。
|
|
60
64
|
|
|
61
65
|
结果不明先按原 `submission_id` 精确查询,找到就沿用;明确可重试时仅用原编号原内容重试一次,再不明则保留草稿并报告。不换身份、职责或编号绕过拒绝。首次使用某类写入、代发、超长正文或遇到失败时,读取 [写入与恢复](references/writing.md) 的相关部分;不把全部故障流程作为每次发言的检查表。
|
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ codiscuss setup
|
|
|
63
63
|
|
|
64
64
|
1. 安装完成后进入自动打开的网站;以后运行 `codiscuss local open` 打开或复用本机网站,默认地址为 `http://127.0.0.1:8765`。
|
|
65
65
|
2. 点击“新建讨论”,填写**话题名称**和**用户原文**。把问题、背景与顾虑写在原文中,讨论目标、背景和边界由 AI 主持人据此补全。
|
|
66
|
-
3.
|
|
66
|
+
3. 选择“全自动讨论”,点击“邀请 AI”选择本场专家,并从中指定 AI 主持人;参与区只显示已加入的 AI,点击“移出”调整名单。已配置的 CLI 专家点击卡片正面,在背面配置模型与思考强度,留空使用 CLI 默认值。分别设置轮次和时长上限,点击数值展开滚轮或直接输入。新建默认 **3 轮 / 不限时长**,可直接启动全自动讨论,按轮次上限或手动停止结束;单次 AI 执行也默认不限时,可按需设置 1–60 分钟上限。
|
|
67
67
|
4. 勾选“创建后立即启动 AI”,复核软件配置。需要修改时展开“调整启动配置”;未配置的软件需选择 CLI / App 类型并补齐路径。项目目录、读写权限和已有会话按本场需要填写。
|
|
68
68
|
5. 点击“创建并启动讨论”。程序完成建房、登记与首次任务交付;在进度看板核对 AI 是否实际进入,并查看保存的发言。
|
|
69
69
|
|
|
@@ -105,7 +105,7 @@ codiscuss local connect https://你的站点
|
|
|
105
105
|
|
|
106
106
|
下次打开会保留原讨论,已暂停的房间需在进度看板明确重启。网页已连接或创建成功不代表 AI 已回答,应核对实际执行进展与讨论记录。独立服务管理、停止归属及故障排查见 [接入说明](docs/runner.md#常驻讨论服务);私有配置需保留在原位置。
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
软件失败时先查看暂停原因,登录和权限问题在对应软件中处理。本机网站的已登记房间,可从进度看板 Codex 旁的“…”菜单直接选择“切换 App / CLI”,在弹窗内暂停并保存;软件路径在“接入 → 修正本场软件连接”修改。原会话与讨论进度保留,待服务确认采用后重启;线上连接暂不提供此修正入口。切到 App 后模型和文件权限按 App 设置,切回 CLI 前须让 App 释放原聊天。本场修改不影响后续讨论的默认配置,详见 [配置修正](docs/runner.md#已启动后的配置修正)。
|
|
109
109
|
|
|
110
110
|
**手动接入:** 选择“手动复制冷启动”,将“接入”页的参与指令交给对应 AI 会话;接入后可由软件原生定时或人工触发,无需每轮复制本地 AI 的回答。在线专家由主持人操作浏览器或由用户转交,首次确定分工后沿用。详见 [讨论工作流](docs/workflow.md)。
|
|
111
111
|
|
|
@@ -135,7 +135,9 @@ codiscuss local connect https://你的站点
|
|
|
135
135
|
|
|
136
136
|
默认上限为 **3 轮、不限时长、到限暂停**,可改为请主持人总结,并设置单次执行上限与收束时间。计数和停止规则见 [讨论上限](docs/workflow.md#讨论上限)。
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
消息支持选择文件、拖拽或粘贴截图,每条最多 10 个附件、单个最多 20 MiB。PNG、JPEG、GIF、WebP 可放大查看,PDF 可在站内翻页、缩放,其他文件提供下载(不在线解析 Office 文件)。本地 AI 可通过 CLI 按需下载原文件;实际能否理解图片或文档取决于参与软件,上传不代表 AI 已读取。
|
|
139
|
+
|
|
140
|
+
讨论结束后,可在左上方频道条目的“更多操作 → 导出对话”下载原始数据、全文及已发送附件原文件,窄屏从频道导航进入。也可用 `discuss-report` 整理带来源的综合报告。备份目前不支持导入恢复,也不下载外链内容。
|
|
139
141
|
|
|
140
142
|
网站目前**没有用户登录和读取权限隔离**;公开部署前请按 [安全说明](.github/SECURITY.md) 配置访问边界。同一模型名称只能配置一个参与者,安排串行进行;在线读取、原生定时和浏览器操作能力取决于参与软件。
|
|
141
143
|
|
package/backend/runtime.py
CHANGED
|
@@ -6,9 +6,8 @@ import time
|
|
|
6
6
|
import uuid
|
|
7
7
|
|
|
8
8
|
LEASE_SECONDS = 120
|
|
9
|
-
WORK_SECONDS = 1200
|
|
10
9
|
MAX_RUN_SECONDS = 86400
|
|
11
|
-
LIMIT_DEFAULTS = {"max_rounds": 0, "max_minutes": 0, "execution_minutes":
|
|
10
|
+
LIMIT_DEFAULTS = {"max_rounds": 0, "max_minutes": 0, "execution_minutes": 0,
|
|
12
11
|
"on_limit": "pause", "summary_minutes": 5}
|
|
13
12
|
PUBLIC_EVENT_FIELDS = ("id", "room_id", "run_id", "generation", "cause", "participant",
|
|
14
13
|
"role", "turn_id", "status", "session_id", "lease_until", "result", "created_at")
|
|
@@ -45,7 +44,7 @@ def limit_values(payload, current=None):
|
|
|
45
44
|
result = {key: (current or {}).get(key, value) for key, value in LIMIT_DEFAULTS.items()}
|
|
46
45
|
result.update(payload)
|
|
47
46
|
for key, minimum, maximum in (("max_rounds", 0, 100), ("max_minutes", 0, 1440),
|
|
48
|
-
("execution_minutes",
|
|
47
|
+
("execution_minutes", 0, 60), ("summary_minutes", 1, 60)):
|
|
49
48
|
if type(result[key]) is not int or not minimum <= result[key] <= maximum:
|
|
50
49
|
raise ValueError(f"{key} 须为 {minimum}–{maximum} 的整数")
|
|
51
50
|
if result["on_limit"] not in ("pause", "summarize"):
|
|
@@ -93,7 +92,7 @@ def require_within_limits(room):
|
|
|
93
92
|
def execution_seconds(connection, room_id):
|
|
94
93
|
"""Read the per-execution budget shared by entry, renewal and writes."""
|
|
95
94
|
row = connection.execute("SELECT execution_minutes FROM discussion_limits WHERE room_id=?", (room_id,)).fetchone()
|
|
96
|
-
return row[0]
|
|
95
|
+
return (row[0] if row else LIMIT_DEFAULTS["execution_minutes"]) * 60
|
|
97
96
|
|
|
98
97
|
|
|
99
98
|
def runtime_view(connection, room_id):
|
|
@@ -103,7 +102,7 @@ def runtime_view(connection, room_id):
|
|
|
103
102
|
return {"mode": "manual", "status": "ready", "revision": 0, "protected": False}
|
|
104
103
|
result = {key: row[key] for key in row.keys() if key != "control_hash"}
|
|
105
104
|
result["protected"] = True
|
|
106
|
-
result["expired"] = row["status"] == "running" and row["deadline"] <= int(time.time())
|
|
105
|
+
result["expired"] = row["status"] == "running" and 0 < row["deadline"] <= int(time.time())
|
|
107
106
|
event = connection.execute("SELECT id,participant,role,status,result FROM discussion_events WHERE room_id=? ORDER BY id DESC LIMIT 1", (room_id,)).fetchone()
|
|
108
107
|
result["latest_execution"] = dict(event) if event else None
|
|
109
108
|
result["participants"] = participant_executions(connection, room_id)
|
|
@@ -157,11 +156,12 @@ def authorize_write(connection, room_id, token, operation, payload):
|
|
|
157
156
|
actor = payload.get("actor", payload.get("submitted_by", payload.get("author", "用户")))
|
|
158
157
|
if actor == "用户":
|
|
159
158
|
return
|
|
159
|
+
work_seconds = execution_seconds(connection, room_id)
|
|
160
160
|
event = connection.execute("""SELECT * FROM discussion_events WHERE room_id=? AND token_hash=?
|
|
161
161
|
AND run_id=? AND generation=? AND status='started' AND lease_until>?
|
|
162
|
-
AND started_at+?>?""", (room_id, hashed, run["run_id"], run["generation"],
|
|
163
|
-
|
|
164
|
-
if not hashed or not event or run["status"] != "running" or run["deadline"] <= int(time.time()):
|
|
162
|
+
AND (?=0 OR started_at+?>?)""", (room_id, hashed, run["run_id"], run["generation"],
|
|
163
|
+
int(time.time()), work_seconds, work_seconds, int(time.time()))).fetchone()
|
|
164
|
+
if not hashed or not event or run["status"] != "running" or 0 < run["deadline"] <= int(time.time()):
|
|
165
165
|
raise PermissionError("执行资格无效、已结束或已到期;请重新读取运行状态")
|
|
166
166
|
if actor != event["participant"]:
|
|
167
167
|
raise PermissionError("执行凭据与实际参与身份不符")
|
|
@@ -173,6 +173,8 @@ def authorize_write(connection, room_id, token, operation, payload):
|
|
|
173
173
|
elif operation == "floor":
|
|
174
174
|
if payload.get("speaker", actor) != actor:
|
|
175
175
|
raise PermissionError("自动执行只能操作本人发言权")
|
|
176
|
+
elif operation == "attachments":
|
|
177
|
+
pass
|
|
176
178
|
elif event["role"] != "host" or operation not in ("topic", "state", "turns", "limits"):
|
|
177
179
|
raise PermissionError("本次执行不能进行此操作")
|
|
178
180
|
|
|
@@ -212,7 +214,7 @@ def configure_runtime(connection, room_id, token, payload):
|
|
|
212
214
|
|
|
213
215
|
|
|
214
216
|
def validate_start(connection, room_id, payload):
|
|
215
|
-
"""
|
|
217
|
+
"""Validate an AI-hosted run; zero deadline leaves only the configured limits."""
|
|
216
218
|
row = connection.execute("SELECT * FROM topic_settings JOIN room_directory USING(room_id) WHERE room_id=?",
|
|
217
219
|
(room_id,)).fetchone()
|
|
218
220
|
limits = connection.execute(LIMITS_SQL, (room_id,)).fetchone()
|
|
@@ -224,9 +226,9 @@ def validate_start(connection, room_id, payload):
|
|
|
224
226
|
configured = min(value for value in (configured, closing_deadline, previous_deadline) if value)
|
|
225
227
|
deadline = payload.get("deadline", configured)
|
|
226
228
|
if type(deadline) is int and configured:
|
|
227
|
-
deadline = min(deadline, configured)
|
|
228
|
-
if type(deadline) is not int or not int(time.time()) < deadline <= int(time.time()) + MAX_RUN_SECONDS:
|
|
229
|
-
raise ValueError("deadline
|
|
229
|
+
deadline = min(deadline or configured, configured)
|
|
230
|
+
if type(deadline) is not int or (deadline != 0 and not int(time.time()) < deadline <= int(time.time()) + MAX_RUN_SECONDS):
|
|
231
|
+
raise ValueError("deadline 须为 0(不限时长)或未来 24 小时内的 Unix 秒数")
|
|
230
232
|
experts = json.loads(row["experts"])
|
|
231
233
|
if row["archived"] or row["phase"] == "concluded" or row["host"] == "用户":
|
|
232
234
|
raise ValueError("自动运行需要未归档、未收束的话题及 AI 主持人")
|
|
@@ -262,12 +264,14 @@ def event_action(connection, room_id, event_id, token, payload):
|
|
|
262
264
|
now = int(time.time())
|
|
263
265
|
work_seconds = execution_seconds(connection, room_id)
|
|
264
266
|
action = payload.get("action")
|
|
267
|
+
work_deadline = (now if action == "enter" else event["started_at"]) + work_seconds if work_seconds else 0
|
|
268
|
+
lease_until = min(now + LEASE_SECONDS, work_deadline or now + LEASE_SECONDS, run["deadline"] or now + LEASE_SECONDS)
|
|
265
269
|
controller = token_hash(token) == run["control_hash"]
|
|
266
270
|
if not controller and (not token_hash(token) or token_hash(token) != event["token_hash"]):
|
|
267
271
|
raise PermissionError("执行凭据无效")
|
|
268
272
|
if action == "view":
|
|
269
273
|
return {key: event[key] for key in PUBLIC_EVENT_FIELDS}
|
|
270
|
-
if run["status"] != "running" or run["deadline"] <= now or event["generation"] != run["generation"]:
|
|
274
|
+
if run["status"] != "running" or 0 < run["deadline"] <= now or event["generation"] != run["generation"]:
|
|
271
275
|
raise FileExistsError("本次运行已停止或执行已过期")
|
|
272
276
|
if action == "reserve" and controller:
|
|
273
277
|
reserve_event(connection, event, payload)
|
|
@@ -275,12 +279,12 @@ def event_action(connection, room_id, event_id, token, payload):
|
|
|
275
279
|
if event["status"] != "dispatched" or not payload.get("session_id"):
|
|
276
280
|
raise FileExistsError("本次任务已进入或不可进入;不要重复执行")
|
|
277
281
|
connection.execute("UPDATE discussion_events SET status='started',session_id=?,started_at=?,lease_until=? WHERE id=?",
|
|
278
|
-
(str(payload["session_id"])[:200], now,
|
|
282
|
+
(str(payload["session_id"])[:200], now, lease_until, event_id))
|
|
279
283
|
elif action == "renew" and controller and event["status"] == "started":
|
|
280
|
-
if event["lease_until"] <= now or
|
|
284
|
+
if event["lease_until"] <= now or 0 < work_deadline <= now:
|
|
281
285
|
raise FileExistsError("执行租约已到期,不能自动重新激活")
|
|
282
286
|
connection.execute("UPDATE discussion_events SET lease_until=? WHERE id=?",
|
|
283
|
-
(
|
|
287
|
+
(lease_until, event_id))
|
|
284
288
|
elif action == "fail" and event["status"] in ("dispatched", "started"):
|
|
285
289
|
connection.execute("UPDATE discussion_events SET status='failed',result=? WHERE id=?",
|
|
286
290
|
(str(payload.get("reason", "需要用户处理"))[:500], event_id))
|
package/backend/schema.sql
CHANGED
|
@@ -52,6 +52,27 @@ CREATE TABLE IF NOT EXISTS messages (
|
|
|
52
52
|
UNIQUE(room_id, submission_id)
|
|
53
53
|
);
|
|
54
54
|
CREATE INDEX IF NOT EXISTS messages_room ON messages(room_id, id);
|
|
55
|
+
CREATE TABLE IF NOT EXISTS attachments (
|
|
56
|
+
id TEXT PRIMARY KEY, room_id TEXT NOT NULL REFERENCES rooms(id) ON DELETE CASCADE,
|
|
57
|
+
message_id INTEGER REFERENCES messages(id) ON DELETE CASCADE,
|
|
58
|
+
position INTEGER NOT NULL DEFAULT 0,
|
|
59
|
+
file_name TEXT NOT NULL, content_type TEXT NOT NULL,
|
|
60
|
+
size INTEGER NOT NULL, sha256 TEXT NOT NULL,
|
|
61
|
+
actor TEXT NOT NULL, submission_id TEXT NOT NULL, created_at TEXT NOT NULL,
|
|
62
|
+
UNIQUE(room_id, submission_id)
|
|
63
|
+
);
|
|
64
|
+
CREATE INDEX IF NOT EXISTS attachments_message ON attachments(message_id, position);
|
|
65
|
+
CREATE TRIGGER IF NOT EXISTS validate_attachment_link BEFORE UPDATE OF message_id ON attachments
|
|
66
|
+
WHEN NEW.message_id IS NOT NULL AND (
|
|
67
|
+
(OLD.message_id IS NOT NULL AND OLD.message_id != NEW.message_id)
|
|
68
|
+
OR NOT EXISTS (
|
|
69
|
+
SELECT 1 FROM messages m JOIN message_details d ON d.message_id=m.id
|
|
70
|
+
WHERE m.id=NEW.message_id AND m.room_id=NEW.room_id AND d.submitted_by=NEW.actor
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
BEGIN
|
|
74
|
+
SELECT RAISE(ABORT, 'attachment_unavailable');
|
|
75
|
+
END;
|
|
55
76
|
CREATE TABLE IF NOT EXISTS message_summaries (
|
|
56
77
|
message_id INTEGER PRIMARY KEY REFERENCES messages(id),
|
|
57
78
|
text TEXT NOT NULL DEFAULT '', author TEXT NOT NULL DEFAULT ''
|
|
@@ -191,7 +212,7 @@ CREATE TABLE IF NOT EXISTS discussion_limits (
|
|
|
191
212
|
room_id TEXT PRIMARY KEY REFERENCES rooms(id) ON DELETE CASCADE,
|
|
192
213
|
max_rounds INTEGER NOT NULL DEFAULT 0,
|
|
193
214
|
max_minutes INTEGER NOT NULL DEFAULT 0,
|
|
194
|
-
execution_minutes INTEGER NOT NULL DEFAULT
|
|
215
|
+
execution_minutes INTEGER NOT NULL DEFAULT 0,
|
|
195
216
|
on_limit TEXT NOT NULL DEFAULT 'pause',
|
|
196
217
|
summary_minutes INTEGER NOT NULL DEFAULT 5,
|
|
197
218
|
started_at INTEGER NOT NULL DEFAULT 0,
|
|
@@ -199,10 +220,11 @@ CREATE TABLE IF NOT EXISTS discussion_limits (
|
|
|
199
220
|
stop_reason TEXT NOT NULL DEFAULT '',
|
|
200
221
|
revision INTEGER NOT NULL DEFAULT 0
|
|
201
222
|
);
|
|
202
|
-
INSERT OR IGNORE INTO discussion_limits(room_id) SELECT id FROM rooms;
|
|
203
|
-
|
|
223
|
+
INSERT OR IGNORE INTO discussion_limits(room_id,execution_minutes) SELECT id,0 FROM rooms;
|
|
224
|
+
DROP TRIGGER IF EXISTS limits_room_created;
|
|
225
|
+
CREATE TRIGGER limits_room_created AFTER INSERT ON rooms
|
|
204
226
|
BEGIN
|
|
205
|
-
INSERT INTO discussion_limits(room_id) VALUES (NEW.id);
|
|
227
|
+
INSERT INTO discussion_limits(room_id,execution_minutes) VALUES (NEW.id,0);
|
|
206
228
|
END;
|
|
207
229
|
CREATE TRIGGER IF NOT EXISTS limits_topic_started AFTER INSERT ON topic_settings
|
|
208
230
|
WHEN NEW.phase IN ('initial','discussing','reviewing')
|
|
@@ -225,7 +247,7 @@ CREATE TABLE IF NOT EXISTS discussion_runtime (
|
|
|
225
247
|
generation INTEGER NOT NULL DEFAULT 1,
|
|
226
248
|
status TEXT NOT NULL CHECK(status IN ('ready','running','paused','stopped','concluded')),
|
|
227
249
|
control_hash TEXT NOT NULL,
|
|
228
|
-
deadline INTEGER NOT NULL DEFAULT 0,
|
|
250
|
+
deadline INTEGER NOT NULL DEFAULT 0, -- 0 means no run duration limit; execution leases remain bounded.
|
|
229
251
|
revision INTEGER NOT NULL DEFAULT 1,
|
|
230
252
|
resume_phase TEXT NOT NULL DEFAULT 'initial',
|
|
231
253
|
updated_at TEXT NOT NULL
|
|
@@ -258,6 +280,20 @@ CREATE TABLE IF NOT EXISTS event_timings (
|
|
|
258
280
|
dispatched_at INTEGER NOT NULL DEFAULT 0,
|
|
259
281
|
finished_at INTEGER NOT NULL DEFAULT 0
|
|
260
282
|
);
|
|
283
|
+
-- Freeze dispatch-to-publication time independently of the recent execution list.
|
|
284
|
+
CREATE TABLE IF NOT EXISTS message_timings (
|
|
285
|
+
message_id INTEGER PRIMARY KEY REFERENCES messages(id) ON DELETE CASCADE,
|
|
286
|
+
elapsed_seconds INTEGER NOT NULL CHECK(elapsed_seconds>=0)
|
|
287
|
+
);
|
|
288
|
+
-- Only completed expert events identify a historical message unambiguously.
|
|
289
|
+
INSERT INTO message_timings(message_id,elapsed_seconds)
|
|
290
|
+
SELECT m.id,MAX(0,CAST(strftime('%s',m.created_at) AS INTEGER)-t.dispatched_at)
|
|
291
|
+
FROM messages AS m JOIN discussion_events AS e
|
|
292
|
+
ON e.room_id=m.room_id AND e.result='message:'||m.id AND e.participant=m.author
|
|
293
|
+
JOIN event_timings AS t ON t.event_id=e.id
|
|
294
|
+
WHERE m.source='cli' AND e.role='expert' AND e.status='completed'
|
|
295
|
+
AND e.started_at>0 AND t.dispatched_at>0 AND t.finished_at>0
|
|
296
|
+
ON CONFLICT(message_id) DO NOTHING;
|
|
261
297
|
CREATE TRIGGER IF NOT EXISTS record_event_timing AFTER UPDATE OF status ON discussion_events
|
|
262
298
|
WHEN OLD.status<>NEW.status
|
|
263
299
|
BEGIN
|