dsh-vibe-math 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ChongCyrus
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,281 @@
1
+ # Vibe Mathematics — 多代理数学问题求解与验证框架
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
4
+ [![GitHub stars](https://img.shields.io/github/stars/ChongCyrus/Vibe-Mathematics)](https://github.com/ChongCyrus/Vibe-Mathematics)
5
+
6
+ > 一个运行在 **DeepSeek Harness** 内的**永久 agent preset**(`vibe-math`)。
7
+ > 它用「**广度探索 → 深度迭代 → 交叉验证 → 知识沉淀**」的闭环,自动求解数学问题,
8
+ > 并对每个结论做多代理交叉验证;支持**断点续跑**与**中途人工干预**,全程可由自然语言驱动。
9
+
10
+ ---
11
+
12
+ ## ✨ 功能特色
13
+
14
+ - **多代理自动求解**:主代理把问题交给调度器,调度器派发 brainstorm / solver / verifier / decider 等子代理协同求解,**你无需逐节点手操**。
15
+ - **多代理交叉验证**:每个结论拆成最小验证单元,≥3 个「严苛审稿人」独立审查 → 辩论 → 裁决(一票否决 / 加权投票)。
16
+ - **知识沉淀**:验证通过的结论晋升进 `Verified/` 可信知识库,供后续方向复用。
17
+ - **断点续跑**:调度状态、任务栈、代理注册表、决策队列、依赖图、验证器历史准确率等全部落盘;重启后 `resume` 即可恢复(含对账,不会卡死)。
18
+ - **中途人工干预(并继续)**:`auto / manual` 模式随时切换;manual 在关键节点挂起决策等你 approve/reject/override;可对任意子代理发消息 / 中断。
19
+ - **按项目隔离**:每个数学问题一个独立项目文件夹,互不干扰,可随时切换。
20
+ - **子代理权限可调控**:可限制子代理允许/禁止的工具、每轮外部工具调用上限,并明确告知其可读 `Verified/` 与进度日志。
21
+ - **可配置**:`vibe_math_setting.json`(含注释)自定义默认参数;`/vibe setup` 交互式问答配置。
22
+ - **自然语言控制**:主代理充当「助手 + 汇报者」,你把需求说成人话,它自己调用工具、汇报进度、配置参数。
23
+
24
+ ---
25
+
26
+ ## 🧠 架构与分工
27
+
28
+ 框架 = **一个主代理(助手)+ 一个代码调度器 + 五类子代理**。
29
+
30
+ | 角色 | 类型 | 职责 |
31
+ |---|---|---|
32
+ | **主代理** | LLM(会话里的那个助手) | **自然语言接口 + 汇报者 + 助手**。它**自己不求解、不调度**,只负责:把你的话翻译成 `vibe_math_*` 工具调用、汇报进展、问答式配置参数、执行调控命令。 |
33
+ | **调度器** | 插件代码(非模型) | 唯一主控:读 `qs.csv`、派发子代理、写文件、推进状态机。**所有调度靠编程,不靠代理**。 |
34
+ | **Brainstorm 子代理** | 子代理 | 元认知头脑风暴:约束分解、边界测试、相似问题映射,把问题拆成多个「大相径庭」的求解方向。 |
35
+ | **Solver 子代理** | 子代理 | 每个方向一个专属求解器,**同一会话内多轮迭代**,产出引理(含证明)、子路线、存活概率、完整解法。 |
36
+ | **Derive 子代理** | 子代理 | 当某问题的所有方向都走进死路仍未解决时,基于历史痛点**派生 1~3 个全新方向**。 |
37
+ | **Verifier 子代理** | 子代理 | 每个验证单元 ≥3 个独立「严苛审稿人」,独立审查 → 辩论(交流群)→ 裁决。 |
38
+ | **Decider 子代理** | 子代理 | `Verified/` 出现新结论时,判断它是否解决了某未解决问题,回写 `qs.csv` 并重命名解法文件。 |
39
+
40
+ > 一句话分工:**主代理负责“和人对话”,调度器负责“干活”,子代理负责“动脑”。**
41
+
42
+ ---
43
+
44
+ ## 📁 目录结构
45
+
46
+ 框架数据落在会话工作区的 `VibeMath/` 下,每个项目一套完整布局:
47
+
48
+ ```
49
+ <会话工作区>/VibeMath/
50
+ ├─ current.json # 当前项目
51
+ ├─ vibe_math_setting.json # (可选,全局回退)默认参数 JSONC,含注释
52
+ └─ Projects/<项目>/
53
+ ├─ vibe_math_setting.json # 该项目默认参数
54
+ ├─ qs/qs.csv # 问题清单:id,description,priority,status,deps
55
+ ├─ Verified/ # 已验证可信知识库(绝对可信)
56
+ ├─ Pending_Verification/ # 待验证原始输出
57
+ ├─ Under_Verification/ # 拆解后的最小验证单元
58
+ ├─ Temp/ # 临时工作区
59
+ ├─ Temp_Validated/ # 已验证、待晋升
60
+ ├─ Progress_Logs/ # 每问题进度 + 辩论日志 + 定期报告
61
+ └─ VibeMath_State/ # 调度器私有持久状态(断点恢复用)
62
+ ```
63
+
64
+ **铁律**:调度器是**唯一文件写者**(子代理只返回结构化 JSON,从不写文件),所有跨目录移动都走**临时原子交换**。
65
+
66
+ ---
67
+
68
+ ## 🚀 安装
69
+
70
+ 两种安装方式,任选其一(也可并存):
71
+
72
+ ### 方式 A:作为插件包一键安装(推荐)
73
+
74
+ 发布到 npm 后:
75
+
76
+ ```sh
77
+ dsh plugin --profile <你的 profile> add dsh-vibe-math
78
+ ```
79
+
80
+ 不发布也能直接从 GitHub 装:
81
+
82
+ ```sh
83
+ dsh plugin --profile <你的 profile> add github:ChongCyrus/Vibe-Mathematics
84
+ ```
85
+
86
+ ### 方式 B:作为 agent preset 手动安装
87
+
88
+ 1. 把本仓库的三个文件放到 preset 目录:
89
+
90
+ ```
91
+ C:\Users\<你>\.dsh\.agent-presets\vibe-math\
92
+ ├─ agent.cordis.yml
93
+ ├─ preset.yml
94
+ └─ vibe-math.js
95
+ ```
96
+
97
+ 2. 新建一个会话,在 preset 选择器里选 **「Vibe Math」**。
98
+ 3. 会话启动后即可使用:工具列表里会出现 20 个 `vibe_math_*` 工具,输入框键入 `/vibe` 有自动补全。
99
+
100
+ > 修改 preset 文件后需**重启 DSH 进程**再开新会话(preset 的 standing mount 会缓存到进程退出)。
101
+
102
+ ---
103
+
104
+ ## ⚡ 快速上手
105
+
106
+ ### 方式 A:直接对话(推荐,最省事)
107
+
108
+ 因为主代理内置了使用说明,你**直接说人话即可**:
109
+
110
+ ```
111
+ 帮我用 Vibe Math 证明 √2 是无理数。
112
+ ```
113
+
114
+ 主代理会自动:`vibe_math_add_problem` 加题 → `vibe_math_start` 启动 → 之后你随时问它进度。
115
+
116
+ ```
117
+ 现在进展怎么样了?
118
+ ```
119
+
120
+ 主代理会自动调用 `vibe_math_status` / `vibe_math_report` 并把结果用人话汇报给你。
121
+
122
+ ### 方式 B:命令 / 工具(精确控制)
123
+
124
+ 在对话里直接调用工具(参数为 JSON):
125
+
126
+ | 工具 | 作用 |
127
+ |---|---|
128
+ | `vibe_math_add_problem` | 加题(id/description/priority) |
129
+ | `vibe_math_start` / `vibe_math_resume` | 启动 / 断点恢复调度器 |
130
+ | `vibe_math_pause` / `vibe_math_abort` | 暂停 / 终止(中断所有子代理) |
131
+ | `vibe_math_status` / `vibe_math_report` | 查看状态 / 完整进度报告 |
132
+ | `vibe_math_set_mode` | 切换 `auto` / `manual` |
133
+ | `vibe_math_set_params` | 运行时调参 |
134
+ | `vibe_math_setup` | 返回参数 schema(交互式配置用) |
135
+ | `vibe_math_save_settings` | 把当前参数存成新默认 |
136
+ | `vibe_math_template` | 生成默认参数模板文件 |
137
+ | `vibe_math_new_project` / `set_project` / `list_projects` | 项目管理 |
138
+ | `vibe_math_list_decisions` / `decide` | 查看 / 裁决人工决策 |
139
+ | `vibe_math_list_agents` / `message_agent` / `interrupt_agent` | 查看 / 发消息 / 中断子代理 |
140
+
141
+ 斜杠命令(与工具等价):`/vibe start|resume|pause|abort|status|report|mode <auto|manual>|setup|save|template [global|project]|add <id> <desc>|project [list|new <name>|<name>]|decisions|agents`
142
+
143
+ ---
144
+
145
+ ## 🎓 教学:让主代理替你干活
146
+
147
+ ### 1. 自然语言驱动(不用记命令)
148
+
149
+ 主代理的作用就是当你的「翻译官」。你只需描述**目标**,它会自己选择并调用工具:
150
+
151
+ | 你说的话 | 主代理做的事 |
152
+ |---|---|
153
+ | “求解 / 证明 XXX” | `add_problem` + `start`,之后汇报 |
154
+ | “现在进度怎么样 / 有哪些代理在跑” | `status` / `report` / `list_agents` 并总结 |
155
+ | “暂停 / 终止求解” | `pause` / `abort` |
156
+ | “切到人工模式,我要逐步把关” | `set_mode manual`,之后有决策就 `list_decisions` 提醒你 |
157
+ | “给 q1 的某个求解方向换个思路(比如改成构造性证明)” | `list_agents` 找到 childId → `message_agent` 注入新指令 |
158
+ | “中断某个卡住的子代理” | `interrupt_agent` |
159
+
160
+ ### 2. 问答式参数配置(/vibe setup)
161
+
162
+ 你甚至不用记参数名。说:
163
+
164
+ ```
165
+ 帮我配置一下参数。
166
+ ```
167
+
168
+ 主代理会调用 `vibe_math_setup` 拿到完整参数 schema(每项含**说明 / 选项 / 建议 / 当前值**),
169
+ 然后用 `ask_user_question` **逐项问你**(选项自带解释与建议),你选完它用 `vibe_math_set_params`
170
+ 应用,最后问你是否 `vibe_math_save_settings` 存为默认。
171
+
172
+ 也可以直接跑命令:`/vibe setup`(看 schema)→ 跟主代理说你要改哪些 → `/vibe save`(存默认)。
173
+
174
+ ### 3. 配置文件(vibe_math_setting.json)
175
+
176
+ - **生成模板**:`/vibe template`(生成到工作区)或 `/vibe template project`(生成到当前项目)——
177
+ 会产出一份**带 `//` 注释、逐项中文说明**的 JSON 模板,你手改后重启/resume 即生效。
178
+ - **保存当前值**:`/vibe save` 把当前生效参数写回该文件。
179
+
180
+ ---
181
+
182
+ ## 🌱 新手示例流程(以“证明 √2 是无理数”为例)
183
+
184
+ **Step 1 — 用一句话启动**
185
+
186
+ ```
187
+ 帮我用 Vibe Math 证明:√2 是无理数。
188
+ ```
189
+
190
+ 主代理执行 `vibe_math_add_problem {"id":"q1","description":"证明:√2 是无理数。","priority":0}`
191
+ 再执行 `vibe_math_start`,然后告诉你“已启动”。
192
+
193
+ **Step 2 — 询问进度**
194
+
195
+ ```
196
+ 进展如何?
197
+ ```
198
+
199
+ 主代理执行 `vibe_math_status` 并用人话汇报:当前活跃子代理数、正在验证的单元、是否有待决策等。
200
+
201
+ **Step 3 — 问答式调参(可选)**
202
+
203
+ ```
204
+ 我想让它用加权投票,并发数设成 6。
205
+ ```
206
+
207
+ 主代理 `vibe_math_set_params {"verdictMode":"weighted-vote","maxParallelThreshold":6}`,
208
+ 并问你是否 `vibe_math_save_settings` 保存。
209
+
210
+ **Step 4 — 中途干预(可选)**
211
+
212
+ ```
213
+ 切到人工模式,我要在每个关键节点把关。
214
+ ```
215
+
216
+ 主代理 `vibe_math_set_mode {"mode":"manual"}`。之后每到一个关键节点它会 `vibe_math_list_decisions`
217
+ 拿到决策,向你说明,等你 `vibe_math_decide {"id":"...","action":"approve"}`(或 `reject` / `override`)。
218
+
219
+ **Step 5 — 收尾**
220
+
221
+ ```
222
+ 结束了吗?结论是什么?
223
+ ```
224
+
225
+ 主代理 `vibe_math_status`:`qs.csv` 里 `q1` 已回写 `solved`,解法文件在 `Verified/` 里并被命名为
226
+ `q1-的解法_<唯一标识>.csv`。
227
+
228
+ ---
229
+
230
+ ## 🖼️ 实际使用示例(长截图)
231
+
232
+ > 截图很长,这里默认**折叠**:点击下方「展开」才加载整张长图,避免它占满页面、遮挡前后文字。
233
+
234
+ <details>
235
+ <summary>📸 展开查看实际使用示例长截图</summary>
236
+
237
+ ![实际使用示例长截图](示例图/实际使用示例-长截图.png)
238
+
239
+ </details>
240
+
241
+ ---
242
+
243
+ ## ⚙️ 参数速查表(默认值)
244
+
245
+ | 参数 | 默认 | 说明 |
246
+ |---|---|---|
247
+ | `mode` | `auto` | `auto` / `manual` |
248
+ | `maxParallelThreshold` | 4 | 全局最大并发子代理数 |
249
+ | `solverMaxRounds` | 20 | 每个求解方向最大迭代轮数 |
250
+ | `verifierCount` | 3 | 每验证单元独立验证器数(≥3) |
251
+ | `debateMaxRounds` | 5 | 验证辩论最大轮数 |
252
+ | `verdictMode` | `direct-veto` | `direct-veto` / `weighted-vote` |
253
+ | `provider` / `model` | 空 | 子代理模型(空 = 继承根代理) |
254
+ | `solverPersona` / `verifierPersona` | 空 | 注入求解器/验证器的额外要求 |
255
+ | `solverToolAllow` / `solverToolDeny` | `[]` | 求解器允许/禁止的工具(硬性 toolFilter) |
256
+ | `verifierToolAllow` / `verifierToolDeny` | `[]` | 验证器允许/禁止的工具 |
257
+ | `solverMaxToolCalls` / `verifierMaxToolCalls` | 0 | 每轮外部工具调用上限(0=不限,软性) |
258
+ | `reportIntervalMs` | 30000 | 自动写进度报告的最小间隔 |
259
+
260
+ ---
261
+
262
+ ## 📝 断点续跑 & 人工干预(两大硬性需求)
263
+
264
+ - **断点续跑**:所有状态落盘到 `VibeMath_State/*.json`,每个子代理都是 DSH 的 **continuable 持久会话**(对话由 DSH 自动保存)。重启后新开会话 → `vibe_math_resume` 即可续跑。
265
+ - **中途人工干预**:`manual` 模式在关键节点(brainstorm/solver 派发、验证裁决、晋升 Verified)挂起决策;可随时 `set_mode auto` 切回自动;可对任意子代理 `message_agent` / `interrupt_agent`。
266
+
267
+ ---
268
+
269
+ ## ⚠️ 已知边界(有意简化)
270
+
271
+ - `Pending_Verification` 按文件逐个拆解,未做跨文件的“去重 / 引用整合”(不损正确性)。
272
+ - `weighted-vote` 会记录每个验证器的历史准确率 + 严谨性权重,但最终裁决仍遵循“证伪优先 / 全 Uncertain→False”(数值权重仅用于审计)。
273
+ - manual 模式在第一个未决关键节点暂停整条主循环。
274
+
275
+ 完整逻辑见仓库内规格文档 `实现方案-多代理数学问题求解与验证框架.md`。
276
+
277
+ ---
278
+
279
+ ## 📄 License
280
+
281
+ MIT
@@ -0,0 +1,45 @@
1
+ # The dsh-vibe-math bundle patch. Applied after `@deepseek-ai/dsh-base`, it:
2
+ # 1. enriches the coding persona with the Vibe Math toolkit instructions, and
3
+ # 2. inserts the vibe-math plugin row — 20 vibe_math_* tools, the /vibe slash
4
+ # command, and a background scheduler (brainstorm → solver iteration →
5
+ # multi-verifier debate → promotion to Verified → triage back to qs.csv).
6
+ #
7
+ # The plugin consumes the HOST subagents/agents/fs/tools/commands services (all
8
+ # provided by dsh-base) and publishes nothing, so it sits loose with no realm.
9
+ #
10
+ # A patch replaces the target row's whole `config`; `system-prompt` only owns
11
+ # `persona`, so restating just that key is complete.
12
+
13
+ - id: system-prompt
14
+ config:
15
+ persona: |-
16
+ You are a coding agent powered by the {{model}} model. Your working directory is {{cwd}}.
17
+
18
+ ## Vibe Math toolkit
19
+
20
+ This session includes the "Vibe Math" multi-agent mathematical problem-solving and
21
+ verification framework. It is driven by a background scheduler (code), NOT by the model:
22
+ you only issue the control tools below and read status; the scheduler automatically runs
23
+ brainstorm → solver iteration → multi-verifier debate → promotion to Verified → triage
24
+ back to the problem list.
25
+
26
+ - vibe_math_add_problem {id, description, priority} — add a problem to qs.csv.
27
+ - vibe_math_start / vibe_math_resume — start / resume the scheduler (resume = continue after a checkpoint or restart).
28
+ - vibe_math_status / vibe_math_report — read scheduler status / full progress report.
29
+ - vibe_math_pause / vibe_math_abort — pause / abort (abort interrupts all children).
30
+ - vibe_math_set_mode {mode: manual|auto} — switch manual / auto control.
31
+ - vibe_math_set_params {...} — tune any parameter (see vibe_math_setup for the full schema).
32
+ - vibe_math_setup — parameter schema for guided configuration.
33
+ - vibe_math_save_settings — persist current params as new defaults.
34
+ - vibe_math_new_project / vibe_math_set_project / vibe_math_list_projects — per-project folders.
35
+ - vibe_math_list_decisions / vibe_math_decide {id, action} — resolve manual decisions.
36
+ - vibe_math_list_agents / vibe_math_message_agent / vibe_math_interrupt_agent — inspect / steer / interrupt subagents.
37
+
38
+ A /vibe slash command mirrors these. When the user asks to solve or verify a math problem:
39
+ add the problem (or ask for it first), then vibe_math_start, then report vibe_math_status.
40
+ In manual mode, poll vibe_math_list_decisions and resolve each with vibe_math_decide.
41
+ Data lives under {{cwd}}/VibeMath/Projects/<project>/ and survives restarts via vibe_math_resume.
42
+
43
+ - insert:
44
+ - id: vibe-math
45
+ name: dsh-vibe-math
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "dsh-vibe-math",
3
+ "description": "Multi-agent mathematical problem-solving & verification framework for DeepSeek Harness: breadth-first brainstorm, iterative solving, multi-verifier cross-validation with debate, and a Verified knowledge base — with checkpoint resume and mid-run human intervention.",
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "main": "vibe-math.js",
7
+ "exports": {
8
+ ".": "./vibe-math.js",
9
+ "./cordis.patch.yml": "./cordis.patch.yml"
10
+ },
11
+ "files": [
12
+ "vibe-math.js",
13
+ "cordis.patch.yml"
14
+ ],
15
+ "license": "MIT",
16
+ "keywords": [
17
+ "deepseek-harness",
18
+ "dsh",
19
+ "dsh-plugin",
20
+ "agent-preset",
21
+ "multi-agent",
22
+ "mathematics",
23
+ "math",
24
+ "theorem-proving",
25
+ "verification",
26
+ "solver",
27
+ "llm-agents"
28
+ ],
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/ChongCyrus/Vibe-Mathematics.git"
32
+ },
33
+ "homepage": "https://github.com/ChongCyrus/Vibe-Mathematics#readme",
34
+ "bugs": {
35
+ "url": "https://github.com/ChongCyrus/Vibe-Mathematics/issues"
36
+ },
37
+ "dsh": {
38
+ "bundle": {
39
+ "patch": "./cordis.patch.yml"
40
+ }
41
+ }
42
+ }
package/vibe-math.js ADDED
@@ -0,0 +1,501 @@
1
+ // Vibe Math — permanent host plugin: multi-agent mathematical problem solving &
2
+ // verification framework ("广度探索 → 深度迭代 → 交叉验证 → 知识沉淀").
3
+ //
4
+ // Preset-local plugin, import-free (Node's ESM resolver cannot see the harness's
5
+ // TypeScript sources). Registers 20 model tools, /vibe slash commands, and a
6
+ // background scheduler; provides NO service, so it sits loose in the preset.
7
+ //
8
+ // Projects: each math project lives in its own folder under
9
+ // <workspace>/VibeMath/Projects/<project>/ with its own qs/Verified/... layout.
10
+ // The current project is recorded in <workspace>/VibeMath/current.json.
11
+ // Default parameters can be overridden by an (optionally commented) JSON file:
12
+ // <project>/vibe_math_setting.json (fallback <workspace>/VibeMath/vibe_math_setting.json).
13
+ export const name = 'vibe-math'
14
+ export const inject = ['subagents', 'agents', 'fs', 'tools', 'commands']
15
+
16
+ export function apply(ctx) {
17
+ const subagents = ctx.subagents
18
+ const agents = ctx.agents
19
+ const fs = ctx.fs
20
+ const tools = ctx.tools
21
+ const commands = ctx.commands
22
+ const subprocess = ctx.get('subprocess')
23
+ const sandboxPolicy = ctx.get('sandboxPolicy')
24
+
25
+ let rootAgent = undefined
26
+ let currentProject = 'default'
27
+ const DEFAULT_PARAMS = {
28
+ mode: 'auto',
29
+ maxParallelThreshold: 4,
30
+ solverMaxRounds: 20,
31
+ verifierCount: 3,
32
+ debateMaxRounds: 5,
33
+ verdictMode: 'direct-veto',
34
+ provider: '',
35
+ model: '',
36
+ solverPersona: '',
37
+ verifierPersona: '',
38
+ solverToolAllow: [],
39
+ solverToolDeny: [],
40
+ verifierToolAllow: [],
41
+ verifierToolDeny: [],
42
+ solverMaxToolCalls: 0,
43
+ verifierMaxToolCalls: 0,
44
+ reportIntervalMs: 30000,
45
+ }
46
+ let params = Object.assign({}, DEFAULT_PARAMS)
47
+ let scheduler = { running: false, activeCount: 0, startedAt: 0, lastCheckpoint: 0, gate: null }
48
+ let agentRegistry = {}
49
+ let dependencies = {}
50
+ let decisionQueue = []
51
+ let tasks = {}
52
+ let tickInFlight = false
53
+ let brainstormRetries = {}
54
+ let deriveRetries = {}
55
+ let solvedByVerified = {}
56
+ let promotionQueue = {}
57
+ let verifierAccuracy = {}
58
+ let activityLog = []
59
+ let lastReportWrite = 0
60
+ let reportDirty = false
61
+
62
+ // ================= helpers =================
63
+ function textBlock(t) { return { type: 'text', text: String(t) } }
64
+ function now() { return Date.now() }
65
+ function uuid() { const h = '0123456789abcdef'; let s = ''; for (let i = 0; i < 36; i++) { if (i === 8 || i === 13 || i === 18 || i === 23) s += '-'; else s += h[Math.floor(Math.random() * 16)] } return s }
66
+ function shortId() { const h = '0123456789abcdef'; let s = ''; for (let i = 0; i < 8; i++) s += h[Math.floor(Math.random() * 16)]; return s }
67
+ function workspaceRoot() { try { if (rootAgent && rootAgent.session && rootAgent.session.header && rootAgent.session.header.cwd) return rootAgent.session.header.cwd } catch (e) {} if (sandboxPolicy && sandboxPolicy.workspaceRoot) return sandboxPolicy.workspaceRoot; return '.' }
68
+ function vibeRoot() { return (workspaceRoot() + '/VibeMath').replace(/\\/g, '/') }
69
+ function projectRoot(slug) { return vibeRoot() + '/Projects/' + slug }
70
+ function frameworkRoot() { return projectRoot(currentProject) }
71
+ function slugify(s) { const t = String(s == null ? '' : s).trim().toLowerCase().replace(/[^a-z0-9_\-\u4e00-\u9fa5]+/g, '-').replace(/^-+|-+$/g, ''); return t || 'project' }
72
+ function getPolicy() { try { if (sandboxPolicy && rootAgent && rootAgent.session) return sandboxPolicy.resolve({ session: rootAgent.session }) } catch (e) {} try { if (sandboxPolicy) return sandboxPolicy.resolve({}) } catch (e) {} return undefined }
73
+ function makeSignal(ms) { return AbortSignal.timeout(ms || 30000) }
74
+ function blocksToText(blocks) { if (!blocks) return ''; let out = ''; for (let i = 0; i < blocks.length; i++) { const b = blocks[i]; if (b && b.type === 'text' && typeof b.text === 'string') out += b.text + '\n' } return out.trim() }
75
+ function parseJson(text) {
76
+ if (typeof text !== 'string') return undefined
77
+ const tryObj = function (s) { try { const v = JSON.parse(s); return (v && typeof v === 'object' && !Array.isArray(v)) ? v : undefined } catch (e) { return undefined } }
78
+ // 1) prefer any ```json ... ``` code fence
79
+ const fenceRe = /```(?:json)?[ \t]*([\s\S]*?)```/gi
80
+ let m
81
+ while ((m = fenceRe.exec(text)) !== null) { const obj = tryObj(m[1].trim()); if (obj !== undefined) return obj }
82
+ // 2) whole trimmed text as pure JSON
83
+ const whole = tryObj(text.trim()); if (whole !== undefined) return whole
84
+ // 3) fallback: scan EVERY balanced {…} block and return the LARGEST one that parses as a JSON object
85
+ // (robust against prose containing braces like {2k+1}, which are not valid JSON)
86
+ let best = undefined; let bestLen = -1
87
+ for (let start = 0; start < text.length; start++) {
88
+ if (text[start] !== '{') continue
89
+ let depth = 0, inStr = false, esc = false, end = -1
90
+ for (let i = start; i < text.length; i++) {
91
+ const c = text[i]
92
+ if (inStr) { if (esc) esc = false; else if (c === '\\') esc = true; else if (c === '"') inStr = false; continue }
93
+ if (c === '"') { inStr = true; continue }
94
+ if (c === '{') depth++
95
+ else if (c === '}') { depth--; if (depth === 0) { end = i; break } }
96
+ }
97
+ if (end === -1) continue
98
+ const obj = tryObj(text.slice(start, end + 1))
99
+ if (obj !== undefined && (end - start + 1) > bestLen) { best = obj; bestLen = end - start + 1 }
100
+ }
101
+ return best
102
+ }
103
+ function csvEscape(v) { const s = String(v == null ? '' : v); return /[",\n\r]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s }
104
+ function csvRow(vals) { return vals.map(csvEscape).join(',') }
105
+ function parseCsv(text) { const rows = []; let row = []; let field = ''; let q = false; for (let i = 0; i < text.length; i++) { const c = text[i]; if (q) { if (c === '"') { if (text[i + 1] === '"') { field += '"'; i++ } else q = false } else field += c } else if (c === '"') q = true; else if (c === ',') { row.push(field); field = '' } else if (c === '\n') { row.push(field); rows.push(row); row = []; field = '' } else if (c !== '\r') field += c } if (field !== '' || row.length > 0) { row.push(field); rows.push(row) } return rows }
106
+ function safeJson(v, fb) { if (v == null || v === '') return fb; try { return JSON.parse(v) } catch (e) { return fb } }
107
+ function stripJsonComments(text) { let out = ''; let inStr = false; let inLine = false; let inBlock = false; let esc = false; for (let i = 0; i < text.length; i++) { const c = text[i]; const n = text[i + 1]; if (inLine) { if (c === '\n') { inLine = false; out += c } continue } if (inBlock) { if (c === '*' && n === '/') { inBlock = false; i++ } continue } if (inStr) { out += c; if (esc) esc = false; else if (c === '\\') esc = true; else if (c === '"') inStr = false; continue } if (c === '"') { inStr = true; out += c; continue } if (c === '/' && n === '/') { inLine = true; i++; continue } if (c === '/' && n === '*') { inBlock = true; i++; continue } out += c } return out }
108
+
109
+ // ================= parameter schema (for setup + settings template) =================
110
+ const PARAM_SCHEMA = [
111
+ { name: 'mode', type: 'enum', options: ['auto', 'manual'], description: 'auto = 无人值守自动通过关键节点;manual = 关键节点挂起人工决策', suggestion: 'auto' },
112
+ { name: 'maxParallelThreshold', type: 'integer', description: '全局最大并发子代理数(并发门阈值)', suggestion: 4 },
113
+ { name: 'solverMaxRounds', type: 'integer', description: '每个求解方向的最大迭代轮数', suggestion: 20 },
114
+ { name: 'verifierCount', type: 'integer', description: '每个验证单元的独立验证器数量(内部强制 ≥3)', suggestion: 3 },
115
+ { name: 'debateMaxRounds', type: 'integer', description: '验证辩论(交流群)最大轮数', suggestion: 5 },
116
+ { name: 'verdictMode', type: 'enum', options: ['direct-veto', 'weighted-vote'], description: 'direct-veto = 一票否决(严谨);weighted-vote = 证伪优先加权', suggestion: 'direct-veto' },
117
+ { name: 'provider', type: 'string', description: '子代理模型 provider(空 = 继承根代理)', suggestion: '' },
118
+ { name: 'model', type: 'string', description: '子代理模型 id(空 = 继承根代理)', suggestion: '' },
119
+ { name: 'solverPersona', type: 'string', description: '注入每个求解器提示词开头的人格/要求', suggestion: '' },
120
+ { name: 'verifierPersona', type: 'string', description: '注入每个验证器提示词开头的人格/要求', suggestion: '' },
121
+ { name: 'solverToolAllow', type: 'string[]', description: '求解器允许的工具名列表(空 = 继承全部工具)', suggestion: [] },
122
+ { name: 'solverToolDeny', type: 'string[]', description: '求解器禁止的工具名列表(如禁用写文件/控制工具)', suggestion: [] },
123
+ { name: 'verifierToolAllow', type: 'string[]', description: '验证器允许的工具名列表(空 = 继承全部)', suggestion: [] },
124
+ { name: 'verifierToolDeny', type: 'string[]', description: '验证器禁止的工具名列表', suggestion: [] },
125
+ { name: 'solverMaxToolCalls', type: 'integer', description: '求解器每轮外部工具调用上限(0 = 不限)', suggestion: 0 },
126
+ { name: 'verifierMaxToolCalls', type: 'integer', description: '验证器每轮外部工具调用上限(0 = 不限)', suggestion: 0 },
127
+ { name: 'reportIntervalMs', type: 'integer', description: '自动写进度报告 report.json 的最小间隔(毫秒)', suggestion: 30000 },
128
+ ]
129
+
130
+ // ================= fs =================
131
+ async function fsTarget(rel) { return await fs.resolve(rel, { cwd: frameworkRoot() }) }
132
+ async function readText(rel) { try { const t = await fsTarget(rel); const s = await fs.stat(t); if (s === undefined) return undefined; return await fs.readText(t) } catch (e) { return undefined } }
133
+ async function readTextAbs(path) { try { const t = await fs.resolve(path); const s = await fs.stat(t); if (s === undefined) return undefined; return await fs.readText(t) } catch (e) { return undefined } }
134
+ async function writeTextAbs(path, content) { try { const t = await fs.resolve(path); await fs.writeText(t, content, undefined, undefined, getPolicy()); return true } catch (e) { return false } }
135
+ async function writeText(rel, content) { const t = await fsTarget(rel); await fs.writeText(t, content, undefined, undefined, getPolicy()); return true }
136
+ async function readJson(rel) { const t = await readText(rel); if (t === undefined || t === '') return undefined; try { return JSON.parse(t) } catch (e) { return undefined } }
137
+ async function writeJson(rel, obj) { return await writeText(rel, JSON.stringify(obj, null, 2)) }
138
+ async function listFiles(rel) { try { const t = await fsTarget(rel); const s = await fs.stat(t); if (s === undefined) return []; const entries = await fs.listDir(t); return entries.filter(function (e) { return e && e.type === 'file' }).map(function (e) { return e.name }) } catch (e) { return [] } }
139
+ async function listDirsAt(base, rel) { try { const t = await fs.resolve(rel, { cwd: base }); const s = await fs.stat(t); if (s === undefined) return []; const entries = await fs.listDir(t); return entries.filter(function (e) { return e && e.type === 'directory' }).map(function (e) { return e.name }) } catch (e) { return [] } }
140
+ async function readCurrentProject() { try { const t = await fs.resolve('current.json', { cwd: vibeRoot() }); const s = await fs.stat(t); if (s === undefined) return 'default'; const txt = await fs.readText(t); const j = safeJson(txt, null); const p = (j && j.project) ? String(j.project) : 'default'; return slugify(p) } catch (e) { return 'default' } }
141
+ async function writeCurrentProject() { try { const t = await fs.resolve('current.json', { cwd: vibeRoot() }); await fs.writeText(t, JSON.stringify({ project: currentProject }), undefined, undefined, getPolicy()) } catch (e) {} }
142
+
143
+ // ================= subprocess =================
144
+ function psQuote(p) { return "'" + String(p).replace(/'/g, "''") + "'" }
145
+ async function runShell(script, cwd) { if (subprocess === undefined) return { ok: false, error: 'no-subprocess' }; try { const handle = subprocess.spawn({ argv: ['powershell', '-NoProfile', '-NonInteractive', '-Command', script], cwd: cwd || workspaceRoot(), stdio: { stdin: 'ignore', stdout: 'inherit', stderr: 'inherit' }, graceMs: 20000 }); const outcome = await handle.done; return { ok: outcome.exitCode === 0, exitCode: outcome.exitCode } } catch (e) { return { ok: false, error: String((e && e.message) || e) } } }
146
+ async function ensureDirs() { const base = frameworkRoot(); const dirs = ['qs', 'Verified', 'Pending_Verification', 'Under_Verification', 'Temp', 'Temp_Validated', 'Progress_Logs', 'VibeMath_State']; const paths = [vibeRoot() + '/Projects'].concat(dirs.map(function (d) { return base + '/' + d })); const list = paths.map(psQuote).join(','); return await runShell('New-Item -Force -ItemType Directory -Path ' + list + ' | Out-Null') }
147
+ async function atomicMove(srcRel, dstRel) { const base = frameworkRoot(); return await runShell('Move-Item -Force -LiteralPath ' + psQuote(base + '/' + srcRel) + ' -Destination ' + psQuote(base + '/' + dstRel)) }
148
+ async function removeFile(rel) { const base = frameworkRoot(); return await runShell('Remove-Item -Force -LiteralPath ' + psQuote(base + '/' + rel) + ' -ErrorAction SilentlyContinue') }
149
+
150
+ // ================= settings file (JSONC) =================
151
+ function sanitizeParams(obj) {
152
+ const out = {}
153
+ const intFields = ['maxParallelThreshold', 'solverMaxRounds', 'verifierCount', 'debateMaxRounds', 'solverMaxToolCalls', 'verifierMaxToolCalls', 'reportIntervalMs']
154
+ const arrayFields = ['solverToolAllow', 'solverToolDeny', 'verifierToolAllow', 'verifierToolDeny']
155
+ for (const k of Object.keys(DEFAULT_PARAMS)) {
156
+ if (!(k in obj)) continue
157
+ const v = obj[k]
158
+ if (intFields.indexOf(k) !== -1) { const n = Number(v); out[k] = Number.isFinite(n) ? Math.floor(n) : DEFAULT_PARAMS[k] }
159
+ else if (arrayFields.indexOf(k) !== -1) { out[k] = Array.isArray(v) ? v.filter(function (x) { return typeof x === 'string' }) : DEFAULT_PARAMS[k] }
160
+ else if (k === 'mode') { out[k] = (v === 'manual' || v === 'auto') ? v : DEFAULT_PARAMS[k] }
161
+ else if (k === 'verdictMode') { out[k] = (v === 'direct-veto' || v === 'weighted-vote') ? v : DEFAULT_PARAMS[k] }
162
+ else { out[k] = v }
163
+ }
164
+ return out
165
+ }
166
+ async function loadSettings() {
167
+ let text = await readText('vibe_math_setting.json')
168
+ if (text === undefined) text = await readTextAbs(vibeRoot() + '/vibe_math_setting.json')
169
+ if (text === undefined) return
170
+ const clean = stripJsonComments(text)
171
+ try {
172
+ const obj = JSON.parse(clean)
173
+ if (obj && typeof obj === 'object' && !Array.isArray(obj)) params = Object.assign({}, params, sanitizeParams(obj))
174
+ } catch (e) {
175
+ console.error('vibe-math: invalid vibe_math_setting.json ignored: ' + String((e && e.message) || e))
176
+ }
177
+ }
178
+ function settingsTemplateFrom(src) {
179
+ const lines = []
180
+ lines.push('{')
181
+ lines.push(' // Vibe Math 默认参数配置(JSON with Comments,可加 // 注释)。')
182
+ lines.push(' // 位置:<项目>/vibe_math_setting.json(全局回退:<工作区>/VibeMath/vibe_math_setting.json)。')
183
+ lines.push(' // 该文件只提供“默认值”;运行期 vibe_math_set_params 改过的值会存到 VibeMath_State/params.json 并覆盖此处。')
184
+ const keys = Object.keys(src).sort()
185
+ for (let i = 0; i < keys.length; i++) {
186
+ const k = keys[i]
187
+ const v = src[k]
188
+ const schema = PARAM_SCHEMA.find(function (p) { return p.name === k })
189
+ const desc = schema ? schema.description : ''
190
+ const comma = i === keys.length - 1 ? '' : ','
191
+ lines.push(' // ' + k + (desc ? ' — ' + desc : ''))
192
+ lines.push(' ' + JSON.stringify(k) + ': ' + JSON.stringify(v) + comma)
193
+ }
194
+ lines.push('}')
195
+ return lines.join('\n') + '\n'
196
+ }
197
+ function settingsTemplate() { return settingsTemplateFrom(params) }
198
+ async function saveSettings() { await writeText('vibe_math_setting.json', settingsTemplate()); return { ok: true, path: frameworkRoot() + '/vibe_math_setting.json' } }
199
+ async function createTemplate(where) { const isGlobal = where !== 'project'; const path = isGlobal ? (vibeRoot() + '/vibe_math_setting.json') : (frameworkRoot() + '/vibe_math_setting.json'); const content = settingsTemplateFrom(DEFAULT_PARAMS); const ok = isGlobal ? await writeTextAbs(path, content) : await writeText('vibe_math_setting.json', content); return { ok: ok, path: path, where: isGlobal ? 'global' : 'project' } }
200
+
201
+ // ================= persistence =================
202
+ async function loadState() { const pj = await readJson('VibeMath_State/params.json'); if (pj) params = Object.assign({}, params, pj); const s = await readJson('VibeMath_State/scheduler_state.json'); if (s) scheduler = Object.assign({}, scheduler, s); const r = await readJson('VibeMath_State/agent_registry.json'); if (r) agentRegistry = r; const d = await readJson('VibeMath_State/dependencies.json'); if (d) dependencies = d; const dq = await readJson('VibeMath_State/decision_queue.json'); if (dq) decisionQueue = dq; const tk = await readJson('VibeMath_State/tasks.json'); if (tk) tasks = tk; const sv = await readJson('VibeMath_State/solved_by_verified.json'); if (sv) solvedByVerified = sv; const pq = await readJson('VibeMath_State/promotion_queue.json'); if (pq) promotionQueue = pq; const va = await readJson('VibeMath_State/verifier_accuracy.json'); if (va) verifierAccuracy = va }
203
+ async function saveAll() { await writeJson('VibeMath_State/params.json', params); await writeJson('VibeMath_State/scheduler_state.json', scheduler); await writeJson('VibeMath_State/agent_registry.json', agentRegistry); await writeJson('VibeMath_State/dependencies.json', dependencies); await writeJson('VibeMath_State/decision_queue.json', decisionQueue); await writeJson('VibeMath_State/tasks.json', tasks); await writeJson('VibeMath_State/solved_by_verified.json', solvedByVerified); await writeJson('VibeMath_State/promotion_queue.json', promotionQueue); await writeJson('VibeMath_State/verifier_accuracy.json', verifierAccuracy); scheduler.lastCheckpoint = now() }
204
+
205
+ // ================= activity log / report =================
206
+ function logActivity(event, detail) { activityLog.push({ at: now(), event: event, detail: String(detail || '') }); if (activityLog.length > 100) activityLog.shift(); reportDirty = true }
207
+ function buildReport() {
208
+ const openTasks = Object.keys(tasks).filter(function (k) { const t = tasks[k]; return t.status === 'spawning' || t.status === 'debating' || t.status === 'awaiting-verdict' })
209
+ return {
210
+ ok: true,
211
+ at: now(),
212
+ project: currentProject,
213
+ frameworkRoot: frameworkRoot(),
214
+ running: scheduler.running,
215
+ mode: params.mode,
216
+ activeCount: scheduler.activeCount,
217
+ maxParallelThreshold: params.maxParallelThreshold,
218
+ pendingDecisions: decisionQueue.filter(function (d) { return d.status === 'pending' }).map(function (d) { return { id: d.id, node: d.node, context: d.context } }),
219
+ openTasks: openTasks.length,
220
+ registeredAgents: Object.keys(agentRegistry).length,
221
+ recentActivity: activityLog.slice(-30),
222
+ params: params,
223
+ }
224
+ }
225
+ async function maybeWriteReport(force) {
226
+ if (!force && !reportDirty) return
227
+ if (!force && (now() - lastReportWrite) < (Number(params.reportIntervalMs) || 30000)) return
228
+ await writeJson('Progress_Logs/report.json', buildReport())
229
+ lastReportWrite = now()
230
+ reportDirty = false
231
+ }
232
+
233
+ // ================= qs & progress =================
234
+ async function getQs() { const text = await readText('qs/qs.csv'); if (text === undefined) return []; const rows = parseCsv(text); const out = []; for (let i = 1; i < rows.length; i++) { const r = rows[i]; if (r.length < 4) continue; out.push({ id: (r[0] || '').trim(), description: r[1] || '', priority: parseInt(r[2], 10) || 0, status: (r[3] || 'unsolved').trim(), deps: safeJson(r[4], []) }) } return out }
235
+ async function writeQs(rows) { const lines = ['id,description,priority,status,deps']; for (let i = 0; i < rows.length; i++) { const q = rows[i]; lines.push(csvRow([q.id, q.description, q.priority, q.status, JSON.stringify(q.deps || [])])) } await writeText('qs/qs.csv', lines.join('\n') + '\n') }
236
+ const PHEADER = ['direction_id', 'title', 'method', 'core_assumption', 'round', 'status', 'survival_probability', 'dead_end_reason', 'lemmas_json', 'sub_routes_json', 'aux_hypotheses_json', 'updated_at']
237
+ async function readProgress(qid) { const text = await readText('Progress_Logs/' + qid + '_progress.csv'); if (text === undefined) return []; const rows = parseCsv(text); const out = []; for (let i = 1; i < rows.length; i++) { const r = rows[i]; if (r.length < 12) continue; out.push({ direction_id: r[0], title: r[1], method: r[2], core_assumption: r[3], round: parseInt(r[4], 10) || 0, status: r[5] || 'active', survival_probability: parseFloat(r[6]) || 0, dead_end_reason: r[7] || '', lemmas: safeJson(r[8], []), sub_routes: safeJson(r[9], []), aux_hypotheses: safeJson(r[10], []), updated_at: r[11] || '' }) } return out }
238
+ async function writeProgress(qid, dirs) { const lines = [csvRow(PHEADER)]; for (let i = 0; i < dirs.length; i++) { const d = dirs[i]; lines.push(csvRow([d.direction_id, d.title || '', d.method || '', d.core_assumption || '', d.round || 0, d.status || 'active', d.survival_probability || 0, d.dead_end_reason || '', JSON.stringify(d.lemmas || []), JSON.stringify(d.sub_routes || []), JSON.stringify(d.aux_hypotheses || []), d.updated_at || String(now())])) } await writeText('Progress_Logs/' + qid + '_progress.csv', lines.join('\n') + '\n') }
239
+
240
+ // ================= child spawn / followup =================
241
+ function pickProvider() { try { const names = subagents.list ? subagents.list() : []; if (names.indexOf('spawn') !== -1) return 'spawn'; if (names.indexOf('fork') !== -1) return 'fork' } catch (e) {} return 'spawn' }
242
+ function childAgentOptions() { const o = {}; try { if (rootAgent && rootAgent.options) { if (rootAgent.options.provider) o.provider = rootAgent.options.provider; if (rootAgent.options.model) o.model = rootAgent.options.model } } catch (e) {} if (params.provider) o.provider = params.provider; if (params.model) o.model = params.model; return o }
243
+ function buildToolFilter(role) { const allow = role === 'solver' ? params.solverToolAllow : role === 'verifier' ? params.verifierToolAllow : undefined; const deny = role === 'solver' ? params.solverToolDeny : role === 'verifier' ? params.verifierToolDeny : undefined; const f = {}; if (Array.isArray(allow) && allow.length > 0) f.allow = allow.slice(); if (Array.isArray(deny) && deny.length > 0) f.deny = deny.slice(); return (f.allow || f.deny) ? f : undefined }
244
+ async function spawnChild(label, promptText, meta) { const request = { prompt: [textBlock(promptText)], parent: rootAgent, agentOptions: childAgentOptions() }; const tf = buildToolFilter(meta && meta.role); if (tf) request.toolFilter = tf; let started; try { started = await subagents.startContinuable({ provider: pickProvider(), label: label, request: request, signal: makeSignal(30000) }) } catch (e) { if (request.toolFilter) { delete request.toolFilter; console.error('vibe-math: startContinuable with toolFilter failed, retrying without it: ' + String((e && e.message) || e)); started = await subagents.startContinuable({ provider: pickProvider(), label: label, request: request, signal: makeSignal(30000) }) } else { throw e } } agentRegistry[started.childId] = Object.assign({ createdAt: now() }, meta || {}); scheduler.activeCount = Math.max(0, scheduler.activeCount) + 1; await saveAll(); return started.childId }
245
+ async function followupChild(childId, promptText) { await subagents.followup(rootAgent, childId, [textBlock(promptText)], { source: { kind: 'user' }, signal: makeSignal(30000) }); scheduler.activeCount = Math.max(0, scheduler.activeCount) + 1; await saveAll() }
246
+ async function interruptChild(childId) { try { subagents.interrupt(childId, { kind: 'ancestor', agent: rootAgent }) } catch (e) {} }
247
+
248
+ // ================= prompts =================
249
+ function solverPersonaText() { return params.solverPersona ? (String(params.solverPersona) + '\n\n') : '' }
250
+ function verifierPersonaText() { return params.verifierPersona ? (String(params.verifierPersona) + '\n\n') : '' }
251
+ function capabilitiesText(role, qid) {
252
+ const maxCalls = role === 'solver' ? params.solverMaxToolCalls : params.verifierMaxToolCalls
253
+ let t = '\nYOUR PERMISSIONS / CAPABILITIES:\n'
254
+ t += '- You may READ any file under Verified/ as a known, trusted dependency.\n'
255
+ if (role === 'solver' && qid) t += '- You may READ Progress_Logs/' + qid + '_progress.csv to inspect ALL directions\' progress, blockers, and prior results for this problem.\n'
256
+ t += '- You may use external tools (web search, symbolic/numeric computation, literature lookup) to assist; '
257
+ t += (maxCalls && Number(maxCalls) > 0) ? ('call such external tools AT MOST ' + maxCalls + ' times this round.\n') : 'no per-round limit by default.\n'
258
+ t += '- You must NOT write files directly: return structured JSON only — the scheduler is the single writer.\n'
259
+ t += '\nHOW TO READ EXISTING KNOWLEDGE (coarse scan → fine read):\n'
260
+ t += '- These files are CSV (RFC 4180); content cells are JSON-encoded strings. A conclusion/lemma object carries summary-index fields ("title", "statement") and the full detail ("proof").\n'
261
+ t += '- COARSE SCAN first: use a read/grep tool to extract ONLY the summary index (e.g. the direction "title" column, or each object\'s "title"/"statement") to quickly locate which files / sub-objects look relevant or valuable — do NOT load full proofs yet.\n'
262
+ t += '- FINE READ after: once you identify a valuable conclusion file or sub-object, read that file again and extract its full JSON content (e.g. "statement", "proof") via the index you just found.\n'
263
+ return t
264
+ }
265
+ function brainstormPrompt(q) { return 'You are a research mathematician orchestrating strategy for one problem.\n\nPROBLEM (id: ' + q.id + '): ' + q.description + '\n\nDo a first-stage metacognitive brainstorm: decompose constraints, test boundary/extreme cases, map to similar known problems. Then propose 3-6 DIVERSE, mutually distinct solution directions.\n' + capabilitiesText('solver', q.id) + '\n\nRespond with ONLY a single JSON object wrapped in a ```json code fence — no prose and no braces { } outside the JSON:\n{"directions":[{"id":"d1","title":"...","method":"...","core_assumption":"...","feasibility":0.5}]}' }
266
+ function deriveDirectionsPrompt(q, prog) { const summary = prog.map(function (d) { return '- ' + d.direction_id + ': ' + d.title + ' (status ' + d.status + ', round ' + d.round + ', survival ' + d.survival_probability + ')' + (d.dead_end_reason ? ' [blocker: ' + d.dead_end_reason + ']' : '') }).join('\n'); return 'You are a research mathematician re-deriving strategy for a problem whose prior directions stalled or failed.\n\nPROBLEM (id: ' + q.id + '): ' + q.description + '\n\nPRIOR DIRECTIONS (with blockers):\n' + summary + '\n' + capabilitiesText('solver', q.id) + '\n\nBased on the pain points and blockers above, deeply derive 1-3 BRAND-NEW directions never tried before, each with a one-line motivation. Avoid any direction equivalent to a failed one.\n\nRespond with ONLY a single JSON object wrapped in a ```json code fence — no prose and no braces { } outside the JSON:\n{"directions":[{"title":"...","method":"...","core_assumption":"...","motivation":"...","feasibility":0.5}]}' }
267
+ function dirSummary(dir) { const lemmas = (dir.lemmas || []).map(function (l) { return l.title }).join('; '); const blockers = (dir.sub_routes || []).filter(function (s) { return s && s.blocker }).map(function (s) { return s.blocker }).join('; '); return 'round ' + dir.round + ', status ' + dir.status + ', survival ' + dir.survival_probability + ', lemmas: ' + lemmas + ', blockers: ' + blockers }
268
+ function solverPrompt(q, dir, round) { let head = solverPersonaText() + 'You are a dedicated solver agent working ONE solution direction of a math problem.\n\nPROBLEM (id: ' + q.id + '): ' + q.description + '\nDIRECTION: ' + dir.title + ' (method: ' + dir.method + '; assumption: ' + dir.core_assumption + ')\nROUND: ' + round + '\n'; if (round > 1) head += '\nYOUR PRIOR PROGRESS: ' + dirSummary(dir) + '\n'; head += capabilitiesText('solver', q.id) + '\n'; head += '\nAdvance the proof/computation for this direction. You MUST produce (even if incomplete):\n- new lemmas / intermediate conclusions WITH full proofs;\n- each concrete sub-route tried, its progress, an explicit feasibility signal, and any blocker;\n- an updated survival probability for this direction.\n\nIf you obtain a COMPLETE solution, adversarially self-check (construct counterexamples, test boundary conditions) BEFORE declaring success, and put the full solution text in "solution".\n\nRespond with ONLY a single JSON object wrapped in a ```json code fence — no prose and no braces { } outside the JSON:\n{"status":"continue|success|dead-end","solution":"complete solution text, or null","lemmas":[{"title":"...","statement":"...","proof":"..."}],"findings":["..."],"sub_routes":[{"title":"...","progress":"...","feasibility_signal":"...","blocker":"..."}],"survival_probability":0.5,"dead_end_reason":"... or null","aux_hypotheses":[{"title":"...","statement":"..."}]}'; return head }
269
+ function verifierReviewPrompt(unit) { return verifierPersonaText() + 'You are a STRICT peer reviewer verifying one mathematical claim. Assume its declared dependencies hold, then check whether the claim is correct.\n\nTARGET CLAIM (id: ' + unit.obj_id + '): ' + unit.title + '\nCONTENT:\n' + unit.content + '\n' + capabilitiesText('verifier') + '\n\nIndependently output your initial review. Respond with ONLY a single JSON object wrapped in a ```json code fence — no prose:\n{"verdict":"true|false|uncertain","reason":"detailed logic chain, potential counterexample, or challenge to a dependency","strictness":"strict|lenient"}' }
270
+ function verifierDebatePrompt(unit, transcript) { return verifierPersonaText() + 'You are one reviewer in a debate ("交流群") about this claim.\n\nTARGET CLAIM (id: ' + unit.obj_id + '): ' + unit.title + '\nCONTENT:\n' + unit.content + '\n' + capabilitiesText('verifier') + '\n\nOTHERS HAVE SAID SO FAR:\n' + transcript + '\n\nRespond to the others (agree / rebut / add evidence). If you changed your mind, say why explicitly. Respond with ONLY a single JSON object wrapped in a ```json code fence — no prose:\n{"verdict":"true|false|uncertain","reason":"...","strictness":"strict|lenient"}' }
271
+ function deciderPrompt(qsList, verifiedContent) { return 'You are a triage agent. Given a newly VERIFIED mathematical result, decide whether it is a COMPLETE solution to any UNSOLVED problem in the list.\n\nUNSOLVED PROBLEMS:\n' + qsList + '\n\nVERIFIED RESULT:\n' + verifiedContent + '\n\nRespond with ONLY a single JSON object wrapped in a ```json code fence — no prose:\n{"solves_qid":"<problem id> or null"}' }
272
+ function solutionText(parsed, dir) { if (parsed && parsed.solution) return String(parsed.solution); const parts = []; if (parsed && parsed.lemmas && parsed.lemmas.length) parts.push('Lemmas: ' + JSON.stringify(parsed.lemmas)); if (parsed && parsed.findings && parsed.findings.length) parts.push('Findings: ' + JSON.stringify(parsed.findings)); if (parts.length) return parts.join('\n'); return 'Direction "' + ((dir && dir.title) || '') + '" reported success (no explicit solution text).' }
273
+ function statusFromStop(stopReason) { return (stopReason === 'completed' || stopReason === 'max-tokens') ? 'continue' : 'dead-end' }
274
+
275
+ // ================= init / control =================
276
+ async function resolveRootAgent(agent) { if (rootAgent) return rootAgent; if (agent) { rootAgent = agent; return rootAgent } try { const roots = agents.roots ? agents.roots() : []; if (roots && roots.length > 0) { rootAgent = roots[0]; return rootAgent } } catch (e) {} return rootAgent }
277
+ async function init(agent) { await resolveRootAgent(agent); if (!rootAgent) return { ok: false, message: 'no root agent available' }; currentProject = await readCurrentProject(); await ensureDirs(); if ((await readText('qs/qs.csv')) === undefined) await writeText('qs/qs.csv', 'id,description,priority,status,deps\n'); params = Object.assign({}, DEFAULT_PARAMS); await loadSettings(); await loadState(); scheduler.activeCount = 0; await saveAll(); return { ok: true } }
278
+ async function startScheduler(agent) { const r = await init(agent); if (!r.ok) return r; scheduler.running = true; scheduler.startedAt = now(); scheduler.gate = null; logActivity('start', 'scheduler started for project ' + currentProject); await saveAll(); await maybeWriteReport(true); scheduleTick(); return { ok: true, message: 'scheduler started', project: currentProject, frameworkRoot: frameworkRoot() } }
279
+ async function resumeScheduler(agent) { const r = await init(agent); if (!r.ok) return r; scheduler.running = true; scheduler.gate = null; logActivity('resume', 'scheduler resumed'); await saveAll(); await maybeWriteReport(true); scheduleTick(); return { ok: true, message: 'scheduler resumed', project: currentProject, frameworkRoot: frameworkRoot() } }
280
+ async function pauseScheduler() { scheduler.running = false; logActivity('pause', 'scheduler paused'); await saveAll(); return { ok: true, message: 'scheduler paused' } }
281
+ async function abortScheduler() { scheduler.running = false; const ids = Object.keys(agentRegistry); for (let i = 0; i < ids.length; i++) await interruptChild(ids[i]); scheduler.activeCount = 0; logActivity('abort', 'scheduler aborted, ' + ids.length + ' child(ren) interrupted'); await saveAll(); return { ok: true, message: 'scheduler aborted', interrupted: ids.length } }
282
+ async function getStatus() { return { ok: true, initialized: rootAgent !== undefined, running: scheduler.running, project: currentProject, projects: await listDirsAt(vibeRoot(), 'Projects'), mode: params.mode, activeCount: scheduler.activeCount, maxParallelThreshold: params.maxParallelThreshold, frameworkRoot: frameworkRoot(), pendingDecisions: decisionQueue.filter(function (d) { return d.status === 'pending' }).length, openTasks: Object.keys(tasks).filter(function (k) { return tasks[k].status === 'spawning' || tasks[k].status === 'debating' || tasks[k].status === 'awaiting-verdict' }).length, registeredAgents: Object.keys(agentRegistry).length, recentActivity: activityLog.slice(-10), params: params } }
283
+
284
+ // ================= projects =================
285
+ async function setProject(slug, create) {
286
+ if (!rootAgent) return { ok: false, message: 'no root agent available' }
287
+ const exists = (await listDirsAt(vibeRoot(), 'Projects')).indexOf(slug) !== -1
288
+ if (!create && !exists) return { ok: false, message: 'project not found: ' + slug }
289
+ if (scheduler.running) await abortScheduler()
290
+ currentProject = slug
291
+ await writeCurrentProject()
292
+ await ensureDirs()
293
+ if ((await readText('qs/qs.csv')) === undefined) await writeText('qs/qs.csv', 'id,description,priority,status,deps\n')
294
+ params = Object.assign({}, DEFAULT_PARAMS); scheduler = { running: false, activeCount: 0, startedAt: 0, lastCheckpoint: 0, gate: null }; agentRegistry = {}; dependencies = {}; decisionQueue = []; tasks = {}; brainstormRetries = {}; deriveRetries = {}; solvedByVerified = {}; promotionQueue = {}; verifierAccuracy = {}; activityLog = []
295
+ await loadSettings(); await loadState(); await saveAll()
296
+ return { ok: true, project: slug, frameworkRoot: frameworkRoot() }
297
+ }
298
+
299
+ // ================= decisions (manual/auto) =================
300
+ function enqueueDecision(node, contextText, data) { const d = { id: uuid(), node: node, context: contextText, data: data, status: 'pending', resolution: null, createdAt: now() }; decisionQueue.push(d); return d }
301
+ async function maybeGate(node, contextText, data, autoFn) { if (params.mode === 'auto') return await autoFn(data); const d = enqueueDecision(node, contextText, data); scheduler.gate = { decisionId: d.id, node: node }; logActivity('gate', node + ': ' + contextText); await saveAll(); return { gated: true, decisionId: d.id } }
302
+ async function applyDecision(node, data, resolution) { if (node === 'spawn') { if (resolution.action === 'approve') { await spawnChild(data.label, data.promptText, data.meta); return { spawned: true } } return { spawned: false } } if (node === 'verdict') { const overridden = resolution.action === 'override' && (resolution.verdict === 'true' || resolution.verdict === 'false'); const v = overridden ? resolution.verdict : data.verdict; await settleVerdict(data.task, v); delete tasks['verify:' + data.unitId]; return { verdict: v, overridden: overridden } } if (node === 'promote') { if (resolution.action === 'approve') { await promoteUnit(data.objId); return { promoted: true } } return { promoted: false } } return {} }
303
+ async function resolveDecision(id, resolution) { const d = decisionQueue.find(function (x) { return x.id === id }); if (!d) return { ok: false, message: 'decision not found' }; if (d.status !== 'pending') return { ok: false, message: 'decision already resolved' }; d.status = 'resolved'; d.resolution = resolution; if (scheduler.gate && scheduler.gate.decisionId === id) scheduler.gate = null; logActivity('decide', id + ' resolved: ' + resolution.action + (resolution.verdict ? ' ' + resolution.verdict : '')); await saveAll(); scheduleTick(); return { ok: true, message: 'decision resolved' } }
304
+
305
+ // ================= scheduler =================
306
+ function scheduleTick() { tick().catch(function (e) { console.error('vibe-math tick error: ' + String((e && e.stack) || e)) }) }
307
+ async function tick() { if (tickInFlight) return; if (!rootAgent) return; if (!scheduler.running) return; if (scheduler.gate) return; tickInFlight = true; try { await processVerification(); await reconcileTasks(); await processPromotion(); await processDecider(); await processSolve(); await maybeWriteReport(false); const qs = await getQs(); const unsolved = qs.filter(function (q) { return q.status !== 'solved' && !solvedByVerified[q.id] }); if (unsolved.length === 0 && Object.keys(agentRegistry).length === 0 && Object.keys(tasks).length === 0) { scheduler.running = false; logActivity('stop', 'no unsolved problems, no active agents/tasks — scheduler stopped'); await saveAll(); await maybeWriteReport(true) } } finally { tickInFlight = false } }
308
+ async function processSolve() { if (scheduler.activeCount >= params.maxParallelThreshold) return; const qs = await getQs(); const unsolved = qs.filter(function (q) { return q.status !== 'solved' && !solvedByVerified[q.id] }).sort(function (a, b) { return a.priority - b.priority }); for (let i = 0; i < unsolved.length; i++) { if (scheduler.activeCount >= params.maxParallelThreshold) break; const q = unsolved[i]; const busy = Object.keys(agentRegistry).some(function (cid) { const m = agentRegistry[cid]; return m && m.qid === q.id && (m.role === 'brainstorm' || m.role === 'solver' || m.role === 'derive') }); if (busy) continue; const prog = await readProgress(q.id); if (prog.length === 0) { if ((brainstormRetries[q.id] || 0) >= 3) { await writeProgress(q.id, [{ direction_id: 'd_' + shortId(), title: 'brainstorm failed', method: '', core_assumption: '', round: 0, status: 'dead-end', survival_probability: 0, dead_end_reason: 'brainstorm produced no directions after 3 attempts', lemmas: [], sub_routes: [], aux_hypotheses: [], updated_at: String(now()) }]); continue } brainstormRetries[q.id] = (brainstormRetries[q.id] || 0) + 1; const label = 'brainstorm:' + q.id; const promptText = brainstormPrompt(q); const r = await maybeGate('spawn', 'brainstorm for problem ' + q.id, { label: label, promptText: promptText, meta: { role: 'brainstorm', qid: q.id } }, async function (d) { await spawnChild(d.label, d.promptText, d.meta); return { spawned: true } }); if (r && r.gated) return } else { for (let j = 0; j < prog.length; j++) { if (scheduler.activeCount >= params.maxParallelThreshold) break; const dir = prog[j]; if (dir.status === 'success' || dir.status === 'dead-end') continue; const running = Object.keys(agentRegistry).some(function (cid) { const m = agentRegistry[cid]; return m && m.qid === q.id && m.direction === dir.direction_id && m.role === 'solver' }); if (running) continue; const label = 'solver:' + q.id + ':' + dir.direction_id; const promptText = solverPrompt(q, dir, Math.max(1, dir.round + 1)); const r = await maybeGate('spawn', 'solver for problem ' + q.id + ' direction ' + dir.direction_id, { label: label, promptText: promptText, meta: { role: 'solver', qid: q.id, description: q.description, direction: dir.direction_id, round: Math.max(1, dir.round + 1) } }, async function (d) { await spawnChild(d.label, d.promptText, d.meta); return { spawned: true } }); if (r && r.gated) return } if (scheduler.activeCount < params.maxParallelThreshold) { const activeDirs = prog.filter(function (d) { return d.status !== 'success' && d.status !== 'dead-end' }); if (activeDirs.length === 0 && (deriveRetries[q.id] || 0) < 3) { deriveRetries[q.id] = (deriveRetries[q.id] || 0) + 1; const dlabel = 'derive:' + q.id; const dprompt = deriveDirectionsPrompt(q, prog); const dr = await maybeGate('spawn', 'derive new directions for problem ' + q.id, { label: dlabel, promptText: dprompt, meta: { role: 'derive', qid: q.id, description: q.description } }, async function (d) { await spawnChild(d.label, d.promptText, d.meta); return { spawned: true } }); if (dr && dr.gated) return } } } } }
309
+
310
+ // ================= child result handling =================
311
+ async function onChildEnd(info) { const meta = agentRegistry[info.id]; if (meta === undefined) return; scheduler.activeCount = Math.max(0, scheduler.activeCount - 1); const output = blocksToText(info.lastAssistantMessage); try { if (meta.role === 'brainstorm') await handleBrainstorm(info.id, meta, output); else if (meta.role === 'solver') await handleSolver(info.id, meta, output, info.stopReason); else if (meta.role === 'verifier') await handleVerifier(info.id, meta, output, info.stopReason); else if (meta.role === 'decider') await handleDecider(info.id, meta, output); else if (meta.role === 'derive') await handleDerive(info.id, meta, output) } catch (e) { console.error('vibe-math onChildEnd error: ' + String((e && e.stack) || e)) } await saveAll(); scheduleTick() }
312
+ async function handleBrainstorm(childId, meta, output) { delete agentRegistry[childId]; const parsed = parseJson(output); const dirs = (parsed && parsed.directions) || []; if (dirs.length === 0) { logActivity('brainstorm', 'problem ' + meta.qid + ' brainstorm returned no directions'); await saveAll(); return } brainstormRetries[meta.qid] = 0; const prog = []; for (let i = 0; i < dirs.length; i++) { const d = dirs[i]; prog.push({ direction_id: 'd_' + shortId(), title: d.title || '', method: d.method || '', core_assumption: d.core_assumption || '', round: 0, status: 'active', survival_probability: (typeof d.feasibility === 'number') ? d.feasibility : 0.5, dead_end_reason: '', lemmas: [], sub_routes: [], aux_hypotheses: [], updated_at: String(now()) }) } logActivity('brainstorm', 'problem ' + meta.qid + ' → ' + prog.length + ' directions'); await writeProgress(meta.qid, prog) }
313
+ async function handleDerive(childId, meta, output) { delete agentRegistry[childId]; const parsed = parseJson(output); const dirs = (parsed && parsed.directions) || []; if (dirs.length === 0) { logActivity('derive', 'problem ' + meta.qid + ' derived no new directions'); await saveAll(); return } const prog = await readProgress(meta.qid); for (let i = 0; i < dirs.length; i++) { const d = dirs[i]; prog.push({ direction_id: 'd_' + shortId(), title: d.title || '', method: d.method || '', core_assumption: d.core_assumption || (d.motivation || ''), round: 0, status: 'active', survival_probability: (typeof d.feasibility === 'number') ? d.feasibility : 0.5, dead_end_reason: '', lemmas: [], sub_routes: [], aux_hypotheses: [], updated_at: String(now()) }) } logActivity('derive', 'problem ' + meta.qid + ' derived ' + dirs.length + ' new directions'); await writeProgress(meta.qid, prog) }
314
+ async function handleSolver(childId, meta, output, stopReason) { const qid = meta.qid; const direction = meta.direction; const parsed = parseJson(output); const prog = await readProgress(qid); const dir = prog.find(function (d) { return d.direction_id === direction }); if (!dir) { delete agentRegistry[childId]; return } const status = (parsed && parsed.status) || statusFromStop(stopReason); dir.round = meta.round; dir.status = status; if (parsed) { if (parsed.lemmas) dir.lemmas = parsed.lemmas; if (parsed.sub_routes) dir.sub_routes = parsed.sub_routes; if (parsed.aux_hypotheses) dir.aux_hypotheses = parsed.aux_hypotheses; if (typeof parsed.survival_probability === 'number') dir.survival_probability = parsed.survival_probability; if (parsed.dead_end_reason) dir.dead_end_reason = parsed.dead_end_reason } if (status === 'success') { dir.status = 'success'; delete agentRegistry[childId]; logActivity('solver', qid + '/' + direction + ' success at round ' + meta.round); await writePending(qid, dir, parsed) } else if (status === 'dead-end' || meta.round >= params.solverMaxRounds) { dir.status = 'dead-end'; if (!dir.dead_end_reason) dir.dead_end_reason = (status === 'dead-end' && !parsed) ? 'solver ended abnormally (' + stopReason + ')' : 'iteration cap reached'; delete agentRegistry[childId]; logActivity('solver', qid + '/' + direction + ' dead-end: ' + dir.dead_end_reason) } else { const q = { id: qid, description: meta.description || '' }; await followupChild(childId, solverPrompt(q, dir, meta.round + 1)); agentRegistry[childId].round = meta.round + 1; dir.round = meta.round + 1 } if (parsed && parsed.aux_hypotheses && parsed.aux_hypotheses.length > 0) await handleAuxHypotheses(qid, parsed.aux_hypotheses); dir.updated_at = String(now()); await writeProgress(qid, prog) }
315
+ async function handleAuxHypotheses(qid, hyps) { const qs = await getQs(); for (let i = 0; i < hyps.length; i++) { const h = hyps[i]; if (!h || !h.title) continue; const subId = qid + '_sub_' + shortId(); qs.push({ id: subId, description: h.title + (h.statement ? ' - ' + h.statement : ''), priority: 1, status: 'unsolved', deps: [] }); dependencies[qid] = dependencies[qid] || []; dependencies[qid].push(subId); await writeQs(qs); logActivity('subquestion', qid + ' spawned sub-question ' + subId + ' (Aux_Hypothesis)') } }
316
+ async function writePending(qid, dir, parsed) { const doc = { qid: qid, direction: dir.direction_id, solution: solutionText(parsed, dir), lemmas: (parsed && parsed.lemmas) || [], findings: (parsed && parsed.findings) || [], sub_routes: (parsed && parsed.sub_routes) || [], aux_hypotheses: (parsed && parsed.aux_hypotheses) || [], survival_probability: dir.survival_probability, created_at: String(now()) }; const id = uuid(); await writeText('Pending_Verification/' + id + '.csv', 'qid,direction,content_json,created_at\n' + csvRow([qid, dir.direction_id, JSON.stringify(doc), String(now())]) + '\n') }
317
+
318
+ // ================= verification =================
319
+ async function processVerification() {
320
+ const files = await listFiles('Pending_Verification')
321
+ for (let i = 0; i < files.length; i++) {
322
+ const fname = files[i]
323
+ const text = await readText('Pending_Verification/' + fname)
324
+ if (text === undefined) continue
325
+ const rows = parseCsv(text)
326
+ const contentJson = rows.length > 1 ? safeJson(rows[1][2], null) : null
327
+ if (!contentJson) { await removeFile('Pending_Verification/' + fname); continue }
328
+ const units = decompose(contentJson)
329
+ for (let j = 0; j < units.length; j++) {
330
+ const unit = units[j]
331
+ await writeText('Under_Verification/' + unit.obj_id + '_' + safeTitle(unit.title) + '.csv', 'obj_id,title,content_json,dependencies_json\n' + csvRow([unit.obj_id, unit.title, JSON.stringify(unit), JSON.stringify(unit.dependencies || [])]) + '\n')
332
+ tasks['verify:' + unit.obj_id] = { id: 'verify:' + unit.obj_id, type: 'verify', unitId: unit.obj_id, unit: unit, status: 'spawning', children: [], childResults: {}, round: 1, expectedCount: Math.max(3, params.verifierCount), createdAt: now() }
333
+ }
334
+ await removeFile('Pending_Verification/' + fname)
335
+ }
336
+ const ids = Object.keys(tasks)
337
+ for (let i = 0; i < ids.length; i++) {
338
+ const t = tasks[ids[i]]
339
+ if (t.type !== 'verify' || t.status !== 'spawning') continue
340
+ if (scheduler.activeCount >= params.maxParallelThreshold) break
341
+ await backfillVerifiers(t)
342
+ }
343
+ }
344
+ function safeTitle(t) { return String(t || 'unit').replace(/[^a-zA-Z0-9_\-\u4e00-\u9fa5]/g, '_').slice(0, 60) }
345
+ function decompose(doc) {
346
+ const units = []
347
+ const qid = doc.qid || ''
348
+ const lemmas = doc.lemmas || []
349
+ for (let i = 0; i < lemmas.length; i++) { const l = lemmas[i]; units.push({ obj_id: uuid(), qid: qid, isSolution: false, title: l.title || ('lemma-' + (i + 1)), content: JSON.stringify(l), dependencies: [] }) }
350
+ const findings = doc.findings || []
351
+ for (let i = 0; i < findings.length; i++) units.push({ obj_id: uuid(), qid: qid, isSolution: false, title: 'finding-' + (i + 1), content: String(findings[i]), dependencies: [] })
352
+ const deps = (doc.aux_hypotheses || []).map(function (h) { return { title: h.title, assumed: true } })
353
+ if (doc.solution) {
354
+ units.push({ obj_id: uuid(), qid: qid, isSolution: true, title: 'solution-' + doc.direction, content: JSON.stringify({ solution: doc.solution, lemmas: lemmas, findings: findings }), dependencies: deps })
355
+ } else if (units.length === 0) {
356
+ units.push({ obj_id: uuid(), qid: qid, isSolution: true, title: 'solution-' + doc.direction, content: JSON.stringify({ solution: null, lemmas: lemmas, findings: findings }), dependencies: deps })
357
+ }
358
+ return units
359
+ }
360
+ async function backfillVerifiers(t) {
361
+ while (t.children.length < t.expectedCount) {
362
+ if (scheduler.activeCount >= params.maxParallelThreshold) break
363
+ const index = t.children.length
364
+ const childId = await spawnChild('verifier:' + t.unitId + ':' + index, verifierReviewPrompt(t.unit), { role: 'verifier', unitId: t.unitId, unit: t.unit, round: 1, index: index })
365
+ t.children.push(childId)
366
+ }
367
+ if (t.children.length >= t.expectedCount) t.status = 'debating'
368
+ }
369
+ async function handleVerifier(childId, meta, output, stopReason) {
370
+ const unitId = meta.unitId
371
+ const parsed = parseJson(output)
372
+ const verdict = (parsed && parsed.verdict) || 'uncertain'
373
+ let t = tasks['verify:' + unitId]
374
+ if (!t) { t = { id: 'verify:' + unitId, type: 'verify', unitId: unitId, unit: meta.unit || { obj_id: unitId, title: unitId, content: '' }, status: 'debating', children: [], childResults: {}, round: 1, expectedCount: Math.max(3, params.verifierCount), createdAt: now() }; tasks[t.id] = t }
375
+ if (t.children.indexOf(childId) === -1) t.children.push(childId)
376
+ t.childResults[childId] = { verdict: verdict, reason: (parsed && parsed.reason) || '', strictness: (parsed && parsed.strictness) || 'lenient', round: meta.round }
377
+ delete agentRegistry[childId]
378
+ const allReported = t.children.every(function (cid) { const r = t.childResults[cid]; return r && r.round === meta.round })
379
+ if (!allReported) { await saveAll(); return }
380
+ await advanceVerification(t, meta.round)
381
+ await saveAll()
382
+ }
383
+ function unitFromTask(t) { return t.unit || { obj_id: t.unitId, title: t.unitId, content: '' } }
384
+ function consensus(t) { const vs = Object.keys(t.childResults).map(function (cid) { return t.childResults[cid].verdict }); if (vs.length === 0) return false; return vs.every(function (v) { return v === 'true' }) || vs.every(function (v) { return v === 'false' }) }
385
+ function buildTranscript(t) { const parts = []; const cids = Object.keys(t.childResults); for (let i = 0; i < cids.length; i++) { const r = t.childResults[cids[i]]; parts.push('Reviewer ' + i + ': verdict=' + r.verdict + ' reason=' + r.reason) } return parts.join('\n') }
386
+ function verifierWeight(cid, strictness) { const acc = verifierAccuracy[cid] || { correct: 0, total: 0 }; const base = acc.total > 0 ? (acc.correct / acc.total) : 0.5; const bonus = strictness === 'strict' ? 0.1 : strictness === 'lenient' ? -0.1 : 0; return Math.max(0, Math.min(1, base + bonus)) }
387
+ function weightsOf(t) { const out = {}; const cids = Object.keys(t.childResults); for (let i = 0; i < cids.length; i++) { const cid = cids[i]; const r = t.childResults[cid]; out[cid] = { accuracy: verifierAccuracy[cid] ? (verifierAccuracy[cid].correct / verifierAccuracy[cid].total) : 0.5, strictness: r.strictness, weight: verifierWeight(cid, r.strictness) } } return out }
388
+ function finalVerdict(t) { const rs = Object.keys(t.childResults).map(function (cid) { return t.childResults[cid] }); const falses = rs.filter(function (r) { return r.verdict === 'false' }); const trues = rs.filter(function (r) { return r.verdict === 'true' }); const uncerts = rs.filter(function (r) { return r.verdict === 'uncertain' }); if (params.verdictMode === 'weighted-vote') { if (falses.length > 0) return 'false'; if (trues.length > 0 && uncerts.length === 0) return 'true'; return 'false' } if (falses.length > 0) return 'false'; if (trues.length === rs.length && rs.length > 0) return 'true'; return 'false' }
389
+ async function advanceVerification(t, round) {
390
+ if (round < params.debateMaxRounds && !consensus(t)) {
391
+ t.round = round + 1
392
+ const transcript = buildTranscript(t)
393
+ for (let i = 0; i < t.children.length; i++) {
394
+ const cid = t.children[i]
395
+ await followupChild(cid, verifierDebatePrompt(unitFromTask(t), transcript))
396
+ agentRegistry[cid] = { role: 'verifier', unitId: t.unitId, round: round + 1, index: i }
397
+ }
398
+ } else {
399
+ const verdict = finalVerdict(t)
400
+ if (params.mode === 'manual') {
401
+ const d = enqueueDecision('verdict', 'verdict for unit ' + t.unitId + ' = ' + verdict, { unitId: t.unitId, verdict: verdict, task: JSON.parse(JSON.stringify(t)) })
402
+ scheduler.gate = { decisionId: d.id, node: 'verdict' }
403
+ t.status = 'awaiting-verdict'
404
+ } else {
405
+ await settleVerdict(t, verdict)
406
+ delete tasks[t.id]
407
+ }
408
+ }
409
+ }
410
+ async function reconcileTasks() {
411
+ const ids = Object.keys(tasks)
412
+ for (let i = 0; i < ids.length; i++) {
413
+ const t = tasks[ids[i]]
414
+ if (t.type !== 'verify' || t.status !== 'debating') continue
415
+ const allReported = t.children.length > 0 && t.children.every(function (cid) { const r = t.childResults[cid]; return r && r.round === t.round })
416
+ if (!allReported) continue
417
+ await advanceVerification(t, t.round)
418
+ }
419
+ }
420
+ async function settleVerdict(t, verdict) { const unit = unitFromTask(t); const unitId = t.unitId; const cids = Object.keys(t.childResults); for (let i = 0; i < cids.length; i++) { const cid = cids[i]; const r = t.childResults[cid]; const acc = verifierAccuracy[cid] || { correct: 0, total: 0 }; acc.total += 1; if (r.verdict === verdict) acc.correct += 1; verifierAccuracy[cid] = acc } await writeJson('VibeMath_State/verifier_accuracy.json', verifierAccuracy); await writeText('Progress_Logs/verification_' + unitId + '.log', JSON.stringify({ verdict: verdict, results: t.childResults, weights: weightsOf(t), transcript: buildTranscript(t), at: now() }, null, 2)); if (verdict === 'true') { const deps = (unit.dependencies || []).map(function (d) { return (d && d.title) || String(d) }); const body = 'Dependencies (Assumed or Verified): ' + JSON.stringify(deps) + '\n---\nTarget Content: ' + unit.content; await writeText('Temp_Validated/' + unitId + '.csv', body); promotionQueue[unitId] = { qid: unit.qid || '', isSolution: !!(unit.isSolution), deps: deps }; await writeJson('VibeMath_State/promotion_queue.json', promotionQueue) } else { await writeText('Progress_Logs/falsified_' + unitId + '.log', JSON.stringify({ unit: (unit.title || unitId), reasons: Object.keys(t.childResults).map(function (cid) { return t.childResults[cid].reason }), direct_veto: params.verdictMode === 'direct-veto', at: now() }, null, 2)) } await removeFile('Under_Verification/' + unit.obj_id + '_' + safeTitle(unit.title) + '.csv'); logActivity('verdict', 'unit ' + unitId + ' = ' + verdict) }
421
+
422
+ // ================= promotion =================
423
+ async function processPromotion() { const files = await listFiles('Temp_Validated'); if (files.length === 0) return; const verifiedText = await verifiedContentSnapshot(); for (let i = 0; i < files.length; i++) { const fname = files[i]; const objId = fname.replace(/\.csv$/, ''); const rec = promotionQueue[objId] || { qid: '', isSolution: false, deps: [] }; const deps = rec.deps || []; const allSatisfied = deps.every(function (title) { return title === '' || verifiedText.indexOf(title) !== -1 }); if (!allSatisfied) continue; const r = await maybeGate('promote', 'promote validated unit ' + objId + ' to Verified', { objId: objId }, async function (d) { await promoteUnit(d.objId); return { promoted: true } }); if (r && r.gated) return } }
424
+ async function verifiedContentSnapshot() { const files = await listFiles('Verified'); let text = ''; for (let i = 0; i < files.length; i++) { const c = await readText('Verified/' + files[i]); if (c !== undefined) text += c + '\n' } return text }
425
+ async function promoteUnit(objId) { await atomicMove('Temp_Validated/' + objId + '.csv', 'Verified/' + objId + '.csv'); const rec = promotionQueue[objId]; if (rec && rec.isSolution && rec.qid) { solvedByVerified[rec.qid] = true; await writeJson('VibeMath_State/solved_by_verified.json', solvedByVerified) } delete promotionQueue[objId]; await writeJson('VibeMath_State/promotion_queue.json', promotionQueue); logActivity('promote', 'unit ' + objId + ' promoted to Verified') }
426
+
427
+ // ================= decider =================
428
+ async function processDecider() { if (scheduler.activeCount >= params.maxParallelThreshold) return; const qs = await getQs(); const unsolved = qs.filter(function (q) { return q.status !== 'solved' }); if (unsolved.length === 0) return; const verified = await listFiles('Verified'); const processed = (await readJson('VibeMath_State/decided_verified.json')) || []; for (let i = 0; i < verified.length; i++) { const vf = verified[i]; if (processed.indexOf(vf) !== -1) continue; const inflight = Object.keys(agentRegistry).some(function (cid) { const m = agentRegistry[cid]; return m && m.role === 'decider' && m.verifiedFile === vf }); if (inflight) continue; if (scheduler.activeCount >= params.maxParallelThreshold) break; const content = await readText('Verified/' + vf); if (content === undefined) continue; const qsList = unsolved.map(function (q) { return q.id + ': ' + q.description }).join('\n'); await spawnChild('decider:' + vf, deciderPrompt(qsList, content.slice(0, 4000)), { role: 'decider', verifiedFile: vf }) } }
429
+ async function handleDecider(childId, meta, output) { delete agentRegistry[childId]; const parsed = parseJson(output); const qid = parsed && parsed.solves_qid; const processed = (await readJson('VibeMath_State/decided_verified.json')) || []; if (processed.indexOf(meta.verifiedFile) === -1) processed.push(meta.verifiedFile); if (qid && qid !== 'null') { const qs = await getQs(); const q = qs.find(function (x) { return x.id === qid }); if (q && q.status !== 'solved') { q.status = 'solved'; solvedByVerified[qid] = true; await writeQs(qs); const newName = qid + '-的解法_' + shortId() + '.csv'; await atomicMove('Verified/' + meta.verifiedFile, 'Verified/' + newName); processed.push(newName); logActivity('decider', 'problem ' + qid + ' solved; Verified file renamed to ' + newName) } } await writeJson('VibeMath_State/decided_verified.json', processed) }
430
+
431
+ // ================= events / timer =================
432
+ ctx.on('subagent/end', function (info) { onChildEnd(info).catch(function (e) { console.error('vibe-math onChildEnd reject: ' + String((e && e.stack) || e)) }) })
433
+ ctx.effect(() => { const t = setInterval(function () { scheduleTick() }, 2000); return () => clearInterval(t) })
434
+
435
+ // ================= tools =================
436
+ function objParams(props, required) { return { type: 'object', properties: props, additionalProperties: false, required: required || [] } }
437
+ function registerTool(name, description, parameters, executeFn) {
438
+ ctx.effect(() => tools.register({
439
+ name: name, description: description, parameters: parameters,
440
+ output: { schema: { type: 'string' }, render: (_args, value) => [{ type: 'text', text: String(value) }] },
441
+ execute: async function (args, exec) { try { const agent = (exec && exec.agent) || undefined; await resolveRootAgent(agent); if (rootAgent) currentProject = await readCurrentProject(); return JSON.stringify(await executeFn(args || {}, agent)) } catch (e) { return JSON.stringify({ ok: false, error: String((e && e.message) || e) }) } },
442
+ }))
443
+ }
444
+
445
+ registerTool('vibe_math_start', 'Start (or restart) the Vibe Math scheduler for the current project.', objParams({}), async function (args, agent) { return await startScheduler(agent) })
446
+ registerTool('vibe_math_resume', 'Resume the scheduler after a checkpoint/restart.', objParams({}), async function (args, agent) { return await resumeScheduler(agent) })
447
+ registerTool('vibe_math_pause', 'Pause the scheduler (in-flight children finish their current turn).', objParams({}), async function () { return await pauseScheduler() })
448
+ registerTool('vibe_math_abort', 'Abort the scheduler and interrupt all active children.', objParams({}), async function () { return await abortScheduler() })
449
+ registerTool('vibe_math_status', 'Show scheduler status, params, active agents, projects, and recent activity.', objParams({}), async function () { return await getStatus() })
450
+ registerTool('vibe_math_report', 'Return the full progress report (status + recent activity + params) and write it to Progress_Logs/report.json.', objParams({}), async function () { await maybeWriteReport(true); return buildReport() })
451
+ registerTool('vibe_math_set_mode', 'Switch between manual and auto (preset) mode.', objParams({ mode: { type: 'string', enum: ['manual', 'auto'] } }, ['mode']), async function (args) { params.mode = args.mode; await saveAll(); return { ok: true, mode: params.mode } })
452
+ registerTool('vibe_math_set_params', 'Update scheduler parameters (partial).', objParams({ maxParallelThreshold: { type: 'integer' }, solverMaxRounds: { type: 'integer' }, verifierCount: { type: 'integer' }, debateMaxRounds: { type: 'integer' }, verdictMode: { type: 'string', enum: ['direct-veto', 'weighted-vote'] }, provider: { type: 'string' }, model: { type: 'string' }, solverPersona: { type: 'string' }, verifierPersona: { type: 'string' }, solverToolAllow: { type: 'array', items: { type: 'string' } }, solverToolDeny: { type: 'array', items: { type: 'string' } }, verifierToolAllow: { type: 'array', items: { type: 'string' } }, verifierToolDeny: { type: 'array', items: { type: 'string' } }, solverMaxToolCalls: { type: 'integer' }, verifierMaxToolCalls: { type: 'integer' }, reportIntervalMs: { type: 'integer' } }), async function (args) { params = Object.assign({}, params, args); await saveAll(); return { ok: true, params: params } })
453
+ registerTool('vibe_math_setup', 'Return the interactive parameter schema (each param: name, type, current, default, description, options, suggestion) for guided configuration.', objParams({}), async function () { const list = PARAM_SCHEMA.map(function (p) { const out = Object.assign({}, p); out.current = params[p.name]; out.default = DEFAULT_PARAMS[p.name]; return out }); return { ok: true, parameters: list, saveTo: frameworkRoot() + '/vibe_math_setting.json' } })
454
+ registerTool('vibe_math_save_settings', 'Write the current params to vibe_math_setting.json (JSON with comments) as new defaults.', objParams({}), async function () { return await saveSettings() })
455
+ registerTool('vibe_math_template', 'Create a fresh vibe_math_setting.json template (with defaults + comments) in the workspace (global) or current project folder.', objParams({ where: { type: 'string', enum: ['global', 'project'] } }), async function (args) { return await createTemplate((args && args.where) || 'global') })
456
+ registerTool('vibe_math_add_problem', 'Add a problem to the current project qs.csv.', objParams({ id: { type: 'string' }, description: { type: 'string' }, priority: { type: 'integer' } }, ['id', 'description']), async function (args) { const qs = await getQs(); if (qs.some(function (q) { return q.id === args.id })) return { ok: false, message: 'problem id already exists' }; qs.push({ id: args.id, description: args.description, priority: args.priority || 0, status: 'unsolved', deps: [] }); await writeQs(qs); scheduleTick(); return { ok: true, message: 'problem added' } })
457
+ registerTool('vibe_math_new_project', 'Create a new math project folder and switch to it.', objParams({ name: { type: 'string' } }, ['name']), async function (args) { const slug = slugify(args.name); return await setProject(slug, true) })
458
+ registerTool('vibe_math_set_project', 'Switch the current math project.', objParams({ name: { type: 'string' } }, ['name']), async function (args) { const slug = slugify(args.name); return await setProject(slug, false) })
459
+ registerTool('vibe_math_list_projects', 'List math projects.', objParams({}), async function () { return { ok: true, current: currentProject, projects: await listDirsAt(vibeRoot(), 'Projects') } })
460
+ registerTool('vibe_math_list_decisions', 'List pending manual decisions.', objParams({}), async function () { return { ok: true, decisions: decisionQueue.filter(function (d) { return d.status === 'pending' }).map(function (d) { return { id: d.id, node: d.node, context: d.context } }) } })
461
+ registerTool('vibe_math_decide', 'Resolve a pending manual decision.', objParams({ id: { type: 'string' }, action: { type: 'string', enum: ['approve', 'reject', 'override'] }, verdict: { type: 'string', enum: ['true', 'false'] } }, ['id', 'action']), async function (args) { const resolution = { action: args.action, verdict: args.verdict }; const d = decisionQueue.find(function (x) { return x.id === args.id }); if (!d) return { ok: false, message: 'decision not found' }; if (args.action === 'override' && args.verdict === undefined) return { ok: false, message: 'override requires a verdict (true|false)' }; const applied = await applyDecision(d.node, d.data, resolution); const r = await resolveDecision(args.id, resolution); return Object.assign({ ok: true, applied: applied }, r) })
462
+ registerTool('vibe_math_list_agents', 'List tracked sub-agents (child sessions).', objParams({}), async function () { const out = []; const ids = Object.keys(agentRegistry); for (let i = 0; i < ids.length; i++) { const m = agentRegistry[ids[i]]; out.push({ childId: ids[i], role: m.role, qid: m.qid, direction: m.direction, round: m.round, unitId: m.unitId }) } return { ok: true, agents: out, count: out.length } })
463
+ registerTool('vibe_math_message_agent', 'Send a message to a tracked child agent (next turn).', objParams({ childId: { type: 'string' }, message: { type: 'string' } }, ['childId', 'message']), async function (args) { if (!agentRegistry[args.childId]) return { ok: false, message: 'unknown childId' }; await followupChild(args.childId, args.message); return { ok: true, message: 'message delivered' } })
464
+ registerTool('vibe_math_interrupt_agent', 'Interrupt a tracked child agent.', objParams({ childId: { type: 'string' } }, ['childId']), async function (args) { await interruptChild(args.childId); return { ok: true, message: 'interrupt requested' } })
465
+
466
+ // ================= slash command /vibe =================
467
+ async function dispatchVibeCommand(cmd, args, agent) {
468
+ if (cmd === 'start') return await startScheduler(agent)
469
+ if (cmd === 'resume') return await resumeScheduler(agent)
470
+ if (cmd === 'pause') return await pauseScheduler()
471
+ if (cmd === 'abort') return await abortScheduler()
472
+ if (cmd === 'status') return await getStatus()
473
+ if (cmd === 'report') { await maybeWriteReport(true); return buildReport() }
474
+ if (cmd === 'mode') { params.mode = (args[0] === 'manual') ? 'manual' : 'auto'; await saveAll(); return { ok: true, mode: params.mode } }
475
+ if (cmd === 'setup') { const list = PARAM_SCHEMA.map(function (p) { const out = Object.assign({}, p); out.current = params[p.name]; out.default = DEFAULT_PARAMS[p.name]; return out }); return { ok: true, parameters: list, saveTo: frameworkRoot() + '/vibe_math_setting.json' } }
476
+ if (cmd === 'save') return await saveSettings()
477
+ if (cmd === 'template') return await createTemplate(args[0] === 'project' ? 'project' : 'global')
478
+ if (cmd === 'add') { const id = args[0]; const desc = args.slice(1).join(' '); if (!id || !desc) return { ok: false, message: 'usage: /vibe add <id> <description>' }; const qs = await getQs(); if (qs.some(function (q) { return q.id === id })) return { ok: false, message: 'problem id already exists' }; qs.push({ id: id, description: desc, priority: 0, status: 'unsolved', deps: [] }); await writeQs(qs); scheduleTick(); return { ok: true, message: 'problem added' } }
479
+ if (cmd === 'project') {
480
+ if (args.length === 0 || args[0] === 'list') return { ok: true, current: currentProject, projects: await listDirsAt(vibeRoot(), 'Projects') }
481
+ if (args[0] === 'new') return await setProject(slugify(args.slice(1).join(' ')), true)
482
+ return await setProject(slugify(args[0]), false)
483
+ }
484
+ if (cmd === 'decisions') return { ok: true, decisions: decisionQueue.filter(function (d) { return d.status === 'pending' }).map(function (d) { return { id: d.id, node: d.node, context: d.context } }) }
485
+ if (cmd === 'agents') { const out = []; const ids = Object.keys(agentRegistry); for (let i = 0; i < ids.length; i++) { const m = agentRegistry[ids[i]]; out.push({ childId: ids[i], role: m.role, qid: m.qid, direction: m.direction, round: m.round }) } return { ok: true, agents: out } }
486
+ return { ok: false, usage: 'start | resume | pause | abort | status | report | mode <auto|manual> | setup | save | template [global|project] | add <id> <description> | project [list | new <name> | <name>] | decisions | agents', message: 'unknown /vibe subcommand: ' + (cmd || '(empty)') }
487
+ }
488
+ ctx.effect(() => commands.register({
489
+ name: 'vibe',
490
+ description: 'control the Vibe Math solver (start/pause/projects/setup/save/decisions/agents)',
491
+ input: { hint: '[start|resume|pause|abort|status|report|mode <auto|manual>|setup|save|template [global|project]|add <id> <desc>|project [list|new <name>|<name>]|decisions|agents]' },
492
+ handler: async function (invocation) {
493
+ const line = String(invocation && invocation.rawInput ? invocation.rawInput : '').trim()
494
+ const parts = line.length > 0 ? line.split(/\s+/) : []
495
+ const cmd = parts[0] || ''
496
+ const rest = parts.slice(1)
497
+ const result = await dispatchVibeCommand(cmd, rest, invocation.agent)
498
+ return { kind: 'success', text: JSON.stringify(result, null, 2) }
499
+ },
500
+ }))
501
+ }