dsh-vibe-math 0.3.3 → 0.3.5
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/README.md +3 -1
- package/installer.js +127 -29
- package/package.json +1 -1
- package/vibe-math-v1/vibe-math.js +24 -3
- package/vibe-math-v2/vibe-math-v2.js +101 -33
package/README.md
CHANGED
|
@@ -43,6 +43,7 @@ dsh plugin --profile <你的 profile> add github:ChongCyrus/Vibe-Mathematics
|
|
|
43
43
|
|
|
44
44
|
安装时插件会自动把两个 preset 写入 `~/.dsh/.agent-presets/`:`vibe-math-v1/` 与 `vibe-math-v2/`。
|
|
45
45
|
之后新建会话,预设选择器里选择 **Vibe Math**(v1)或 **Vibe Math V2**(v2)即可。
|
|
46
|
+
**升级包版本后重启 DSH,未手动改过的 preset 文件会自动更新到新版本**(细节见文末「v2」安装器说明)。
|
|
46
47
|
|
|
47
48
|
### 方式 B:作为 agent preset 手动安装
|
|
48
49
|
|
|
@@ -420,6 +421,7 @@ flowchart TB
|
|
|
420
421
|
| `reportMode` | `file` | `file` = 写报告文件 / `push` = 推送主代理汇报 / `both` |
|
|
421
422
|
| `promoteValueThreshold` | 0.7 | Propos 中「价值/关键性」≥ 该值且未决(0,1) 的命题自动加入 qs.json |
|
|
422
423
|
| `priorityAdjust` | `none` | `none` / `deadend-deprioritize`(全死路降优先级)/ `survival-map`(按存活率重算) |
|
|
424
|
+
| `proposPriorityAdjust` | `none` | 命题优先级动态调整:`none` / `progress-graded`(按定论接近度+证明/证伪材料量重算,越接近定论越优先验证) |
|
|
423
425
|
| `provider` / `model` | 空 | 子代理模型(空 = 继承根代理) |
|
|
424
426
|
| `solverPersona` / `verifierPersona` | 空 | 注入求解器/验证器的额外要求 |
|
|
425
427
|
| `solverToolAllow` / `solverToolDeny` | `[]` | 求解器允许/禁止的工具 |
|
|
@@ -455,7 +457,7 @@ flowchart TB
|
|
|
455
457
|
- manual 模式在第一个未决关键节点暂停整条主循环。
|
|
456
458
|
|
|
457
459
|
**v2**:
|
|
458
|
-
-
|
|
460
|
+
- 安装器带**版本化自动更新**:每次 DSH 启动时对比包版本与 `<presetRoot>/.vibe-math-installed.json` 记录——版本升级会自动替换**未被手动修改**的 preset 文件(哈希一致才覆盖);你改过的文件会被保留并在日志中提示。无记录的老安装首次会一次性刷新到当前版本。想强制全量重装:删除 `~/.dsh/.agent-presets/vibe-math-v1` 与 `vibe-math-v2` 目录后重启 DSH。
|
|
459
461
|
- `flat` 裁决在辩论不一致时直接判 `0.5`;`forced` 按历史准确率+置信度加权。
|
|
460
462
|
- `never` 优先级的问题/命题**永不调度**,且不阻塞严格终止(视为主动弃权)。
|
|
461
463
|
- 两个 preset 文件互相独立、可共存;同一会话同时只能选一个预设。
|
package/installer.js
CHANGED
|
@@ -1,51 +1,149 @@
|
|
|
1
|
-
// dsh-vibe-math merged bundle installer.
|
|
1
|
+
// dsh-vibe-math merged bundle installer — VERSIONED AUTO-UPDATE.
|
|
2
2
|
// When this bundle is installed (e.g. `dsh plugin add dsh-vibe-math` or from the
|
|
3
3
|
// dsh-market), this plugin copies BOTH agent presets out of the package into the
|
|
4
4
|
// DSH preset root, so the user immediately gets two presets in the picker:
|
|
5
5
|
// vibe-math-v1/ (classic pipeline architecture)
|
|
6
6
|
// vibe-math-v2/ (new probability-driven architecture)
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
|
|
7
|
+
//
|
|
8
|
+
// UPDATE POLICY (state recorded in <presetRoot>/.vibe-math-installed.json):
|
|
9
|
+
// - baseline (no state file — e.g. upgrading from an installer that predates
|
|
10
|
+
// this mechanism): every existing owned file is refreshed to the current
|
|
11
|
+
// package version and recorded as package-owned (user policy: auto-update
|
|
12
|
+
// old installs; any manual edits made before this baseline are overwritten
|
|
13
|
+
// once — from then on edits are protected).
|
|
14
|
+
// - upgrade (recorded version != current package.json version): every owned
|
|
15
|
+
// file that is byte-identical to the previously installed copy (i.e. NOT
|
|
16
|
+
// user-edited since) is overwritten with the new version; user-edited files
|
|
17
|
+
// are preserved and reported via the logger.
|
|
18
|
+
// - same version: no-op (idempotent). Missing files are ALWAYS restored.
|
|
19
|
+
// - force a full refresh at any time: delete the preset dirs and restart DSH.
|
|
20
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'
|
|
21
|
+
import { createHash } from 'node:crypto'
|
|
10
22
|
import { homedir } from 'node:os'
|
|
11
23
|
import { dirname, join } from 'node:path'
|
|
12
24
|
import { fileURLToPath } from 'node:url'
|
|
13
25
|
|
|
14
26
|
export const name = 'vibe-math-preset-installer'
|
|
15
27
|
|
|
28
|
+
const PRESETS = [
|
|
29
|
+
{
|
|
30
|
+
src: 'vibe-math-v1',
|
|
31
|
+
dst: 'vibe-math-v1',
|
|
32
|
+
files: ['agent.cordis.yml', 'preset.yml', 'vibe-math.js', '实现方案-多代理数学问题求解与验证框架.md'],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
src: 'vibe-math-v2',
|
|
36
|
+
dst: 'vibe-math-v2',
|
|
37
|
+
files: ['agent.cordis.yml', 'preset.yml', 'vibe-math-v2.js', '实现方案.md'],
|
|
38
|
+
},
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
const STATE_FILE = '.vibe-math-installed.json'
|
|
42
|
+
|
|
43
|
+
function sha256(buf) { return createHash('sha256').update(buf).digest('hex') }
|
|
44
|
+
|
|
45
|
+
function readState(path) {
|
|
46
|
+
try {
|
|
47
|
+
const raw = readFileSync(path, 'utf8')
|
|
48
|
+
const obj = JSON.parse(raw)
|
|
49
|
+
if (obj && typeof obj === 'object' && obj.files && typeof obj.files === 'object') return obj
|
|
50
|
+
} catch (e) { /* missing or corrupt — treat as no state (baseline) */ }
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function writeState(path, state) {
|
|
55
|
+
try {
|
|
56
|
+
const tmp = path + '.tmp'
|
|
57
|
+
writeFileSync(tmp, JSON.stringify(state, null, 2) + '\n', 'utf8')
|
|
58
|
+
renameSync(tmp, path)
|
|
59
|
+
} catch (e) {
|
|
60
|
+
// best-effort: state persistence failure must not break the copy step
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
16
64
|
export function apply(ctx) {
|
|
17
65
|
const logger = ctx && ctx.logger
|
|
18
66
|
try {
|
|
19
67
|
const dshHome = process.env.DSH_HOME || join(homedir(), '.dsh')
|
|
20
68
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
69
|
+
const presetRoot = join(dshHome, '.agent-presets')
|
|
70
|
+
const stateFile = join(presetRoot, STATE_FILE)
|
|
71
|
+
|
|
72
|
+
// current package version (the source of truth for "is this an upgrade?")
|
|
73
|
+
let pkgVersion = ''
|
|
74
|
+
try { pkgVersion = String((JSON.parse(readFileSync(join(here, 'package.json'), 'utf8')).version) || '') } catch (e) { pkgVersion = '' }
|
|
75
|
+
|
|
76
|
+
const state = readState(stateFile)
|
|
77
|
+
const prevFiles = (state && state.files) || {}
|
|
78
|
+
const isUpgrade = state !== null && pkgVersion !== '' && state.version !== pkgVersion
|
|
79
|
+
const isBaseline = state === null // no recorded history → refresh everything (user policy: auto-update old installs)
|
|
80
|
+
|
|
81
|
+
const nextFiles = {}
|
|
82
|
+
let installed = 0, updated = 0, kept = 0
|
|
83
|
+
const keptList = []
|
|
84
|
+
|
|
85
|
+
for (const p of PRESETS) {
|
|
86
|
+
const srcDir = join(here, p.src)
|
|
87
|
+
const dstDir = join(presetRoot, p.dst)
|
|
88
|
+
if (!existsSync(srcDir)) continue
|
|
89
|
+
mkdirSync(dstDir, { recursive: true })
|
|
90
|
+
for (const f of p.files) {
|
|
91
|
+
const s = join(srcDir, f)
|
|
92
|
+
const d = join(dstDir, f)
|
|
93
|
+
if (!existsSync(s)) continue
|
|
94
|
+
const key = p.src + '/' + f
|
|
95
|
+
const cur = readFileSync(s)
|
|
96
|
+
const curHash = sha256(cur)
|
|
97
|
+
if (!existsSync(d)) {
|
|
98
|
+
// missing file: always restore, whatever the version
|
|
99
|
+
writeFileSync(d, cur)
|
|
100
|
+
installed += 1
|
|
101
|
+
nextFiles[key] = { hash: curHash, provenance: 'package' }
|
|
102
|
+
continue
|
|
103
|
+
}
|
|
104
|
+
const destHash = sha256(readFileSync(d))
|
|
105
|
+
if (isBaseline) {
|
|
106
|
+
// no recorded history: refresh to the current package (one-time; edits
|
|
107
|
+
// made before this mechanism are overwritten, later edits are protected)
|
|
108
|
+
if (destHash === curHash) { nextFiles[key] = { hash: curHash, provenance: 'package' } }
|
|
109
|
+
else { writeFileSync(d, cur); updated += 1; nextFiles[key] = { hash: curHash, provenance: 'package' } }
|
|
110
|
+
continue
|
|
111
|
+
}
|
|
112
|
+
const prev = prevFiles[key]
|
|
113
|
+
const prevRec = (prev && typeof prev === 'object') ? prev : { hash: prev, provenance: 'package' }
|
|
114
|
+
const prevProv = (prevRec.provenance === 'user') ? 'user' : 'package' // 未知来源按包文件处理
|
|
115
|
+
if (prevProv === 'package' && destHash === prevRec.hash) {
|
|
116
|
+
// 包文件且未被改动 → 可安全升级(内容相同则跳过写入)
|
|
117
|
+
if (destHash !== curHash) { writeFileSync(d, cur); updated += 1 }
|
|
118
|
+
nextFiles[key] = { hash: curHash, provenance: 'package' }
|
|
119
|
+
} else if (prevProv === 'user') {
|
|
120
|
+
// 用户持有 → 永不覆盖
|
|
121
|
+
kept += 1
|
|
122
|
+
if (isUpgrade) keptList.push(key + ' (用户持有)')
|
|
123
|
+
nextFiles[key] = { hash: destHash, provenance: 'user' }
|
|
124
|
+
} else {
|
|
125
|
+
// 包文件但自上次安装后已被用户改动
|
|
126
|
+
kept += 1
|
|
127
|
+
if (isUpgrade) keptList.push(key + ' (已修改)')
|
|
128
|
+
nextFiles[key] = { hash: destHash, provenance: 'user' }
|
|
129
|
+
}
|
|
43
130
|
}
|
|
44
131
|
}
|
|
45
|
-
|
|
46
|
-
|
|
132
|
+
|
|
133
|
+
writeState(stateFile, { version: pkgVersion, files: nextFiles, updatedAt: Date.now() })
|
|
134
|
+
|
|
135
|
+
if (isUpgrade) {
|
|
136
|
+
logger?.info?.('[dsh-vibe-math] preset auto-update: version ' + (state.version || '(none)') + ' → ' + pkgVersion +
|
|
137
|
+
' — 新增 ' + installed + ' 个文件,更新 ' + updated + ' 个文件' +
|
|
138
|
+
(kept > 0 ? ',保留 ' + kept + ' 个未覆盖文件(' + keptList.join('; ') + ')' : '') +
|
|
139
|
+
'。新版本 preset 将在新会话生效。')
|
|
140
|
+
} else if (isBaseline) {
|
|
141
|
+
logger?.info?.('[dsh-vibe-math] preset baseline: refreshed ' + (installed + updated) + ' file(s) to v' + pkgVersion +
|
|
142
|
+
' — 已启用自动更新(后续版本升级将自动替换未被手动修改的 preset 文件)。')
|
|
143
|
+
} else if (installed > 0) {
|
|
144
|
+
logger?.info?.('[dsh-vibe-math] restored ' + installed + ' missing preset file(s)')
|
|
47
145
|
}
|
|
48
146
|
} catch (err) {
|
|
49
|
-
logger?.warn?.('[dsh-vibe-math] preset install failed: %s', String((err && err.message) || err))
|
|
147
|
+
logger?.warn?.('[dsh-vibe-math] preset install/update failed: %s', String((err && err.message) || err))
|
|
50
148
|
}
|
|
51
149
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-vibe-math",
|
|
3
3
|
"description": "Multi-agent mathematical problem-solving & verification frameworks for DeepSeek Harness — TWO agent presets in one install: vibe-math-v1 (classic pipeline: brainstorm → solver iteration → multi-verifier debate → Verified) and vibe-math-v2 (new probability-driven architecture: qs.json + Propos knowledge base + explorer→solver→review/debate verdict). Installing this bundle auto-installs both presets into the DSH preset root.",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "installer.js",
|
|
7
7
|
"exports": {
|
|
@@ -312,6 +312,19 @@ export function apply(ctx) {
|
|
|
312
312
|
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() } }
|
|
313
313
|
async function pauseScheduler() { scheduler.running = false; logActivity('pause', 'scheduler paused'); await saveAll(); return { ok: true, message: 'scheduler paused' } }
|
|
314
314
|
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 } }
|
|
315
|
+
// auto 模式语义 = 无人值守自动通过关键节点:切回 auto 时把仍挂起的人工决策按自动策略放行
|
|
316
|
+
async function autoResolvePending() {
|
|
317
|
+
const pending = decisionQueue.filter(function (d) { return d.status === 'pending' })
|
|
318
|
+
for (let i = 0; i < pending.length; i++) {
|
|
319
|
+
const d = pending[i]
|
|
320
|
+
try {
|
|
321
|
+
if (d.node === 'spawn') { await spawnChild(d.data.label, d.data.promptText, d.data.meta); d.status = 'resolved'; d.resolution = { action: 'approve', auto: true } }
|
|
322
|
+
else if (d.node === 'verdict') { await settleVerdict(d.data.task, d.data.verdict); delete tasks[d.data.task.id]; d.status = 'resolved'; d.resolution = { action: 'approve', auto: true } }
|
|
323
|
+
else if (d.node === 'promote') { await promoteUnit(d.data.objId); d.status = 'resolved'; d.resolution = { action: 'approve', auto: true } }
|
|
324
|
+
} catch (e) { console.error('vibe-math: auto-resolve decision failed: ' + String((e && e.message) || e)) }
|
|
325
|
+
}
|
|
326
|
+
if (pending.length > 0) { scheduler.gate = null; logActivity('mode', 'switched to auto — auto-resolved ' + pending.length + ' pending decision(s)'); await saveAll(); scheduleTick() }
|
|
327
|
+
}
|
|
315
328
|
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 } }
|
|
316
329
|
|
|
317
330
|
// ================= projects =================
|
|
@@ -344,7 +357,7 @@ export function apply(ctx) {
|
|
|
344
357
|
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() }
|
|
345
358
|
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) }
|
|
346
359
|
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) }
|
|
347
|
-
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) }
|
|
360
|
+
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 } if (!parsed && !scheduler.running) { 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) }
|
|
348
361
|
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)') } }
|
|
349
362
|
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') }
|
|
350
363
|
|
|
@@ -405,6 +418,14 @@ export function apply(ctx) {
|
|
|
405
418
|
const verdict = (parsed && parsed.verdict) || 'uncertain'
|
|
406
419
|
let t = tasks['verify:' + unitId]
|
|
407
420
|
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 }
|
|
421
|
+
if (!parsed && !scheduler.running) {
|
|
422
|
+
// abort:被中断的验证器没有产出,丢弃该子代理并清理任务簿记(任务在 resume 时由 processVerification 重建)
|
|
423
|
+
delete agentRegistry[childId]
|
|
424
|
+
const ix = t.children.indexOf(childId); if (ix !== -1) t.children.splice(ix, 1)
|
|
425
|
+
delete t.childResults[childId]
|
|
426
|
+
if (t.children.length === 0 && t.id && tasks[t.id]) delete tasks[t.id]
|
|
427
|
+
return
|
|
428
|
+
}
|
|
408
429
|
if (t.children.indexOf(childId) === -1) t.children.push(childId)
|
|
409
430
|
t.childResults[childId] = { verdict: verdict, reason: (parsed && parsed.reason) || '', strictness: (parsed && parsed.strictness) || 'lenient', round: meta.round }
|
|
410
431
|
delete agentRegistry[childId]
|
|
@@ -481,7 +502,7 @@ export function apply(ctx) {
|
|
|
481
502
|
registerTool('vibe_math_abort', 'Abort the scheduler and interrupt all active children.', objParams({}), async function () { return await abortScheduler() })
|
|
482
503
|
registerTool('vibe_math_status', 'Show scheduler status, params, active agents, projects, and recent activity.', objParams({}), async function () { return await getStatus() })
|
|
483
504
|
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() })
|
|
484
|
-
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 } })
|
|
505
|
+
registerTool('vibe_math_set_mode', 'Switch between manual and auto (preset) mode. Switching to auto auto-resolves any pending manual decisions.', objParams({ mode: { type: 'string', enum: ['manual', 'auto'] } }, ['mode']), async function (args) { params.mode = args.mode; await saveAll(); if (params.mode === 'auto') await autoResolvePending(); return { ok: true, mode: params.mode } })
|
|
485
506
|
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' }, tickIntervalMs: { type: 'integer' }, activityLogCap: { type: 'integer' } }), async function (args) { params = Object.assign({}, params, args); await saveAll(); return { ok: true, params: params } })
|
|
486
507
|
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' } })
|
|
487
508
|
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() })
|
|
@@ -504,7 +525,7 @@ export function apply(ctx) {
|
|
|
504
525
|
if (cmd === 'abort') return await abortScheduler()
|
|
505
526
|
if (cmd === 'status') return await getStatus()
|
|
506
527
|
if (cmd === 'report') { await maybeWriteReport(true); return buildReport() }
|
|
507
|
-
if (cmd === 'mode') { params.mode = (args[0] === 'manual') ? 'manual' : 'auto'; await saveAll(); return { ok: true, mode: params.mode } }
|
|
528
|
+
if (cmd === 'mode') { params.mode = (args[0] === 'manual') ? 'manual' : 'auto'; await saveAll(); if (params.mode === 'auto') await autoResolvePending(); return { ok: true, mode: params.mode } }
|
|
508
529
|
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' } }
|
|
509
530
|
if (cmd === 'save') return await saveSettings()
|
|
510
531
|
if (cmd === 'template') return await createTemplate(args[0] === 'project' ? 'project' : 'global')
|
|
@@ -51,6 +51,7 @@ export function apply(ctx) {
|
|
|
51
51
|
reportMode: 'file', // file | push | both
|
|
52
52
|
promoteValueThreshold: 0.7, // Propos → qs auto-promotion threshold (价值/关键性)
|
|
53
53
|
priorityAdjust: 'none', // none | deadend-deprioritize | survival-map
|
|
54
|
+
proposPriorityAdjust: 'none', // none | progress-graded(按定论接近度+证明/证伪材料量动态调命题优先级)
|
|
54
55
|
tickIntervalMs: 2000, // 调度器心跳间隔(毫秒)
|
|
55
56
|
activityLogCap: 100, // 活动日志保留条数(report.recentActivity 最多显示 30 条)
|
|
56
57
|
maxExplorerRetries: 3, // explorer 重派生上限(拆方向失败重试次数)
|
|
@@ -135,6 +136,7 @@ export function apply(ctx) {
|
|
|
135
136
|
{ name: 'reportMode', type: 'enum', options: ['file', 'push', 'both'], description: 'file = 写报告文件;push = 推送消息让主代理主动汇报;both = 两者都做', suggestion: 'file' },
|
|
136
137
|
{ name: 'promoteValueThreshold', type: 'number', description: 'Propos 中「价值/关键性」≥ 该值且未决(0,1) 的命题自动加入 qs.json', suggestion: 0.7 },
|
|
137
138
|
{ name: 'priorityAdjust', type: 'enum', options: ['none', 'deadend-deprioritize', 'survival-map'], description: '优先级动态调整策略:none=不自动调;deadend-deprioritize=方向全死路时降优先级;survival-map=按最高方向存活率重算(存活率高越优先)', suggestion: 'none' },
|
|
139
|
+
{ name: 'proposPriorityAdjust', type: 'enum', options: ['none', 'progress-graded'], description: '命题优先级动态调整:none=不自动调;progress-graded=按「定论接近度(|布尔估计-0.5|)+ 证明/证伪材料量」重算,越接近定论越优先验证', suggestion: 'none' },
|
|
138
140
|
{ name: 'tickIntervalMs', type: 'integer', description: '调度器心跳间隔(毫秒):多久扫描一次子代理状态并推进(越小越灵敏、越大越省资源)', suggestion: 2000 },
|
|
139
141
|
{ name: 'activityLogCap', type: 'integer', description: '活动日志保留条数(影响 report.recentActivity 的细节量,报告最多显示 30 条)', suggestion: 100 },
|
|
140
142
|
{ name: 'maxExplorerRetries', type: 'integer', description: 'explorer 拆方向失败的重派生上限(达到后该问题标记为方向耗尽)', suggestion: 3 },
|
|
@@ -175,6 +177,7 @@ export function apply(ctx) {
|
|
|
175
177
|
else if (k === 'verdictMode') { out[k] = (v === 'flat' || v === 'forced') ? v : DEFAULT_PARAMS[k] }
|
|
176
178
|
else if (k === 'reportMode') { out[k] = (v === 'file' || v === 'push' || v === 'both') ? v : DEFAULT_PARAMS[k] }
|
|
177
179
|
else if (k === 'priorityAdjust') { out[k] = (v === 'none' || v === 'deadend-deprioritize' || v === 'survival-map') ? v : DEFAULT_PARAMS[k] }
|
|
180
|
+
else if (k === 'proposPriorityAdjust') { out[k] = (v === 'none' || v === 'progress-graded') ? v : DEFAULT_PARAMS[k] }
|
|
178
181
|
else { out[k] = v }
|
|
179
182
|
}
|
|
180
183
|
return out
|
|
@@ -495,14 +498,15 @@ export function apply(ctx) {
|
|
|
495
498
|
}
|
|
496
499
|
// note 4: probability-1 rules
|
|
497
500
|
async function processStatusUpdates() {
|
|
498
|
-
let changed = false
|
|
499
501
|
const qs = await getQs()
|
|
502
|
+
let qsChanged = false
|
|
500
503
|
for (let i = 0; i < qs.length; i++) {
|
|
501
504
|
const q = qs[i]
|
|
502
|
-
if (q.解法列表 && q.解法列表.some(function (s) { return s.正确概率 === 1 })) { if (!q.已解决)
|
|
505
|
+
if (q.解法列表 && q.解法列表.some(function (s) { return s.正确概率 === 1 })) { if (!q.已解决) qsChanged = true; q.已解决 = true; q.优先级 = 'never' }
|
|
503
506
|
}
|
|
504
|
-
if (
|
|
507
|
+
if (qsChanged) { await writeQs(qs); logActivity('update', 'problems marked solved by probability-1 solutions') }
|
|
505
508
|
const propos = await getPropos()
|
|
509
|
+
let closedPromoted = false
|
|
506
510
|
for (let i = 0; i < propos.length; i++) {
|
|
507
511
|
const p = propos[i]
|
|
508
512
|
let pChanged = false
|
|
@@ -511,32 +515,61 @@ export function apply(ctx) {
|
|
|
511
515
|
if (proofOne && p.布尔估计 !== 1) { p.布尔估计 = 1; pChanged = true }
|
|
512
516
|
else if (refuteOne && p.布尔估计 !== 0) { p.布尔估计 = 0; pChanged = true }
|
|
513
517
|
if ((p.布尔估计 === 1 || p.布尔估计 === 0) && p.优先级 !== 'never') { p.优先级 = 'never'; pChanged = true }
|
|
514
|
-
if (p.布尔估计 === 1 || p.布尔估计 === 0) {
|
|
518
|
+
if (p.布尔估计 === 1 || p.布尔估计 === 0) {
|
|
519
|
+
if (await writeVerifiedCardIfNeeded(p)) pChanged = true
|
|
520
|
+
// 源命题已定论 → 关闭其晋升出的"僵尸"问题(避免永远未解决)
|
|
521
|
+
const srcMarker = '由命题 ' + p.id + '('
|
|
522
|
+
for (let j = 0; j < qs.length; j++) {
|
|
523
|
+
const qj = qs[j]
|
|
524
|
+
if (!qj.已解决 && String(qj.progress || '').indexOf(srcMarker) !== -1) { qj.已解决 = true; qj.优先级 = 'never'; closedPromoted = true }
|
|
525
|
+
}
|
|
526
|
+
}
|
|
515
527
|
if (pChanged) await upsertProposition(p)
|
|
516
528
|
}
|
|
529
|
+
if (closedPromoted) { await writeQs(qs); logActivity('update', 'promoted problems closed because their source proposition resolved') }
|
|
517
530
|
}
|
|
518
531
|
async function processPriorityAdjust() {
|
|
519
532
|
const mode = params.priorityAdjust || 'none'
|
|
520
|
-
if (mode
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
533
|
+
if (mode !== 'none') {
|
|
534
|
+
const qs = await getQs()
|
|
535
|
+
let changed = false
|
|
536
|
+
for (let i = 0; i < qs.length; i++) {
|
|
537
|
+
const q = qs[i]
|
|
538
|
+
if (q.已解决 || q.优先级 === 'never') continue
|
|
539
|
+
const prog = parseProgress(q)
|
|
540
|
+
if (mode === 'deadend-deprioritize') {
|
|
541
|
+
if (prog.directions.length > 0 && prog.directions.every(function (d) { return d.status === 'dead-end' })) {
|
|
542
|
+
const cur = Number(q.优先级); if (Number.isFinite(cur) && cur < 10) { q.优先级 = 10; changed = true }
|
|
543
|
+
}
|
|
544
|
+
} else if (mode === 'survival-map') {
|
|
545
|
+
if (prog.directions.length > 0) {
|
|
546
|
+
const maxSurv = Math.max.apply(null, prog.directions.map(function (d) { return Number(d.survival) || 0 }))
|
|
547
|
+
const target = Math.round(Math.max(0, Math.min(10, 10 - 10 * maxSurv)))
|
|
548
|
+
if (q.优先级 !== target) { q.优先级 = target; changed = true }
|
|
549
|
+
}
|
|
536
550
|
}
|
|
537
551
|
}
|
|
552
|
+
if (changed) { await writeQs(qs); logActivity('priority', 'priorities auto-adjusted (' + mode + ')') }
|
|
553
|
+
}
|
|
554
|
+
const pMode = params.proposPriorityAdjust || 'none'
|
|
555
|
+
if (pMode === 'progress-graded') {
|
|
556
|
+
const propos = await getPropos()
|
|
557
|
+
const changedProps = []
|
|
558
|
+
for (let i = 0; i < propos.length; i++) {
|
|
559
|
+
const p = propos[i]
|
|
560
|
+
if (p.布尔估计 === 1 || p.布尔估计 === 0 || p.优先级 === 'never') continue
|
|
561
|
+
const closeness = Math.abs(Number(p.布尔估计) - 0.5)
|
|
562
|
+
const material = Math.min(5, (p.证明列表 || []).length + (p.证伪列表 || []).length)
|
|
563
|
+
const score = closeness * 1.2 + material * 0.08
|
|
564
|
+
const target = Math.round(Math.max(0, Math.min(10, 10 - 10 * score)))
|
|
565
|
+
const cur = Number(p.优先级)
|
|
566
|
+
if (Number.isFinite(cur) && cur !== target) { p.优先级 = target; changedProps.push(p) }
|
|
567
|
+
}
|
|
568
|
+
if (changedProps.length > 0) {
|
|
569
|
+
for (let i = 0; i < changedProps.length; i++) await upsertProposition(changedProps[i])
|
|
570
|
+
logActivity('priority', 'proposition priorities auto-adjusted (progress-graded)')
|
|
571
|
+
}
|
|
538
572
|
}
|
|
539
|
-
if (changed) { await writeQs(qs); logActivity('priority', 'priorities auto-adjusted (' + mode + ')') }
|
|
540
573
|
}
|
|
541
574
|
// note 3 + user 价值 field: promote high-value unresolved propositions into qs.json
|
|
542
575
|
async function processPromote() {
|
|
@@ -583,7 +616,7 @@ export function apply(ctx) {
|
|
|
583
616
|
for (let j = 0; j < sols.length; j++) {
|
|
584
617
|
const s = sols[j]
|
|
585
618
|
if (s.正确概率 === 1 || s.正确概率 === 0 || s.已验) continue
|
|
586
|
-
out.push({ rId: 'r-' + q.id + '-s' + j, kind: 'problem-solution', qid: q.id, 概述: q.概述, process: s.完整解法 || '', idx: j, priority: q.优先级 === 'never' ? 999 : Number(q.优先级) })
|
|
619
|
+
out.push({ rId: 'r-' + q.id + '-s' + j, kind: 'problem-solution', qid: q.id, 概述: q.概述, process: s.完整解法 || '', idx: j, prob: Number(s.正确概率) || 0, priority: q.优先级 === 'never' ? 999 : Number(q.优先级) })
|
|
587
620
|
}
|
|
588
621
|
}
|
|
589
622
|
const propos = await getPropos()
|
|
@@ -592,13 +625,13 @@ export function apply(ctx) {
|
|
|
592
625
|
if (p.布尔估计 === 1 || p.布尔估计 === 0 || p.优先级 === 'never') continue
|
|
593
626
|
const proofs = p.证明列表 || []; const refutes = p.证伪列表 || []
|
|
594
627
|
if (proofs.length === 0 && refutes.length === 0) {
|
|
595
|
-
out.push({ rId: 'r-' + p.id, kind: 'proposition', pId: p.id, 概述: p.概述, priority: p.优先级 === 'never' ? 999 : Number(p.优先级) })
|
|
628
|
+
out.push({ rId: 'r-' + p.id, kind: 'proposition', pId: p.id, 概述: p.概述, prob: Number(p.布尔估计) || 0, priority: p.优先级 === 'never' ? 999 : Number(p.优先级) })
|
|
596
629
|
} else {
|
|
597
|
-
for (let j = 0; j < proofs.length; j++) { if (proofs[j].正确概率 === 1 || proofs[j].正确概率 === 0 || proofs[j].已验) continue; out.push({ rId: 'r-' + p.id + '-pf' + j, kind: 'prop-proof', pId: p.id, 概述: p.概述, side: '证明', process: proofs[j].完整过程 || '', idx: j, priority: p.优先级 === 'never' ? 999 : Number(p.优先级) }) }
|
|
598
|
-
for (let j = 0; j < refutes.length; j++) { if (refutes[j].正确概率 === 1 || refutes[j].正确概率 === 0 || refutes[j].已验) continue; out.push({ rId: 'r-' + p.id + '-rf' + j, kind: 'prop-proof', pId: p.id, 概述: p.概述, side: '证伪', process: refutes[j].完整过程 || '', idx: j, priority: p.优先级 === 'never' ? 999 : Number(p.优先级) }) }
|
|
630
|
+
for (let j = 0; j < proofs.length; j++) { if (proofs[j].正确概率 === 1 || proofs[j].正确概率 === 0 || proofs[j].已验) continue; out.push({ rId: 'r-' + p.id + '-pf' + j, kind: 'prop-proof', pId: p.id, 概述: p.概述, side: '证明', process: proofs[j].完整过程 || '', idx: j, prob: Number(proofs[j].正确概率) || 0, priority: p.优先级 === 'never' ? 999 : Number(p.优先级) }) }
|
|
631
|
+
for (let j = 0; j < refutes.length; j++) { if (refutes[j].正确概率 === 1 || refutes[j].正确概率 === 0 || refutes[j].已验) continue; out.push({ rId: 'r-' + p.id + '-rf' + j, kind: 'prop-proof', pId: p.id, 概述: p.概述, side: '证伪', process: refutes[j].完整过程 || '', idx: j, prob: Number(refutes[j].正确概率) || 0, priority: p.优先级 === 'never' ? 999 : Number(p.优先级) }) }
|
|
599
632
|
}
|
|
600
633
|
}
|
|
601
|
-
out.sort(function (a, b) { return a.priority - b.priority })
|
|
634
|
+
out.sort(function (a, b) { if (a.priority !== b.priority) return a.priority - b.priority; return (b.prob || 0) - (a.prob || 0) })
|
|
602
635
|
return out
|
|
603
636
|
}
|
|
604
637
|
async function backfillVerifiers(t) {
|
|
@@ -686,6 +719,7 @@ export function apply(ctx) {
|
|
|
686
719
|
const prog = parseProgress(q)
|
|
687
720
|
const dir = prog.directions.find(function (d) { return d.id === dirId })
|
|
688
721
|
if (!dir) { delete agentRegistry[childId]; return }
|
|
722
|
+
if (!parsed && !scheduler.running) { delete agentRegistry[childId]; return } // abort:不把方向标记为死路,保留待 resume
|
|
689
723
|
const status = (parsed && parsed.status) || statusFromStop(stopReason)
|
|
690
724
|
dir.round = meta.round
|
|
691
725
|
if (parsed) {
|
|
@@ -742,9 +776,11 @@ export function apply(ctx) {
|
|
|
742
776
|
function statusFromStop(stopReason) { return (stopReason === 'completed' || stopReason === 'max-tokens') ? 'continue' : 'dead-end' }
|
|
743
777
|
async function addLemmaAsProposition(qid, lemma) {
|
|
744
778
|
if (!lemma || !lemma.title) return
|
|
779
|
+
let be = clamp01(lemma.布尔估计 != null ? lemma.布尔估计 : 0.6)
|
|
780
|
+
if (be >= 1) be = 0.99; else if (be <= 0) be = 0.01 // 写入时概率必须 <1 且 >0(待验证器验证)
|
|
745
781
|
const p = {
|
|
746
782
|
id: 'p-' + shortId(), 概述: lemma.statement || lemma.title,
|
|
747
|
-
布尔估计:
|
|
783
|
+
布尔估计: be,
|
|
748
784
|
细类型: (lemma.细类型 && typeof lemma.细类型 === 'object') ? lemma.细类型 : { 未分类: {} },
|
|
749
785
|
证明列表: [{ 完整过程: lemma.proof || '', 正确概率: clamp01(0.7), '支持信息/依据': '' }],
|
|
750
786
|
证伪列表: [], 优先级: (lemma.优先级 != null) ? lemma.优先级 : 1,
|
|
@@ -785,6 +821,14 @@ export function apply(ctx) {
|
|
|
785
821
|
const Reason = (parsed && parsed.Reason) || ''
|
|
786
822
|
let t = tasks['verify:' + rId]
|
|
787
823
|
if (!t) { t = { id: 'verify:' + rId, type: 'verify', r: { kind: 'proposition', pId: rId, 概述: rId }, rId: rId, status: 'debating', children: [], childResults: {}, round: 1, expectedCount: Math.max(2, params.verifierCount), createdAt: now() }; tasks[t.id] = t }
|
|
824
|
+
if (!parsed && !scheduler.running) {
|
|
825
|
+
// abort:被中断的验证器没有产出,丢弃该子代理并清理任务簿记(任务在 resume 时由 processVerify 重建)
|
|
826
|
+
delete agentRegistry[childId]
|
|
827
|
+
const ix = t.children.indexOf(childId); if (ix !== -1) t.children.splice(ix, 1)
|
|
828
|
+
delete t.childResults[childId]
|
|
829
|
+
if (t.children.length === 0 && t.id && tasks[t.id]) delete tasks[t.id]
|
|
830
|
+
return
|
|
831
|
+
}
|
|
788
832
|
if (t.children.indexOf(childId) === -1) t.children.push(childId)
|
|
789
833
|
t.childResults[childId] = { Result: Result, Reason: Reason, round: meta.round }
|
|
790
834
|
delete agentRegistry[childId]
|
|
@@ -796,6 +840,7 @@ export function apply(ctx) {
|
|
|
796
840
|
async function advanceVerification(t, round) {
|
|
797
841
|
if (round < params.debateMaxRounds && !consensus(t) && t.children.length > 0) {
|
|
798
842
|
if (!scheduler.running) { t.status = 'paused'; return } // resume will re-advance this task
|
|
843
|
+
if (scheduler.activeCount >= params.maxParallelThreshold) { t.status = 'paused'; return } // 并发门:等有空闲槽位再辩论(reconcileVerify 会重推进)
|
|
799
844
|
t.round = round + 1
|
|
800
845
|
const transcript = buildTranscript(t)
|
|
801
846
|
const nextChildren = []
|
|
@@ -905,7 +950,7 @@ export function apply(ctx) {
|
|
|
905
950
|
sol.已验 = true
|
|
906
951
|
sol.验证记录 = sol.验证记录 || []
|
|
907
952
|
sol.验证记录.push({ 结果: v, 时间: now(), 依据: strongestReason(t, v >= 0.5 ? 1 : 0) })
|
|
908
|
-
if (v === 1) { q.已解决 = true; q.优先级 = 'never' }
|
|
953
|
+
if (v === 1) { q.已解决 = true; q.优先级 = 'never'; await writeVerifiedProblemCardIfNeeded(q, sol) }
|
|
909
954
|
}
|
|
910
955
|
await writeQs(qs)
|
|
911
956
|
}
|
|
@@ -936,6 +981,16 @@ export function apply(ctx) {
|
|
|
936
981
|
await writeJson('Verified/' + cat + '_Verified.json', list)
|
|
937
982
|
return true
|
|
938
983
|
}
|
|
984
|
+
async function writeVerifiedProblemCardIfNeeded(q, sol) {
|
|
985
|
+
if (!q || !q.已解决) return false
|
|
986
|
+
const cat = '问题'
|
|
987
|
+
const list = await readVerifiedCategory(cat)
|
|
988
|
+
if (list.some(function (c) { return c.id === q.id })) return false // idempotent
|
|
989
|
+
const card = { id: q.id, 概述: q.概述, 类型: '问题', 结论: true, 概率: 1, 内容: (sol && sol.完整解法) || '', 来源: q.id, 时间: now(), 分类: cat }
|
|
990
|
+
list.push(card)
|
|
991
|
+
await writeJson('Verified/' + cat + '_Verified.json', list)
|
|
992
|
+
return true
|
|
993
|
+
}
|
|
939
994
|
|
|
940
995
|
// ================= child result dispatch =================
|
|
941
996
|
async function onChildEnd(info) {
|
|
@@ -970,15 +1025,28 @@ export function apply(ctx) {
|
|
|
970
1025
|
logActivity(fresh ? 'start' : 'resume', 'cleared ' + Object.keys(agentRegistry).length + ' agent(s) and ' + Object.keys(tasks).length + ' task(s) (' + (fresh ? 'restart' : 'stale from previous process') + ')')
|
|
971
1026
|
agentRegistry = {}; tasks = {}
|
|
972
1027
|
}
|
|
1028
|
+
scheduler.activeCount = 0 // 仅清空 registry/tasks 时归零;同进程 resume 保留存活计数(并发门才准确)
|
|
973
1029
|
}
|
|
974
1030
|
await writeJson('VibeMath_State/process_epoch.json', processEpoch)
|
|
975
|
-
|
|
1031
|
+
await saveAll()
|
|
976
1032
|
return { ok: true }
|
|
977
1033
|
}
|
|
978
1034
|
async function startScheduler(agent) { const r = await init(agent, true); 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() } }
|
|
979
1035
|
async function resumeScheduler(agent) { const r = await init(agent, false); 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() } }
|
|
980
1036
|
async function pauseScheduler() { scheduler.running = false; logActivity('pause', 'scheduler paused'); await saveAll(); return { ok: true, message: 'scheduler paused' } }
|
|
981
1037
|
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 } }
|
|
1038
|
+
// auto 模式语义 = 无人值守自动通过关键节点:切回 auto 时把仍挂起的人工决策按自动策略放行
|
|
1039
|
+
async function autoResolvePending() {
|
|
1040
|
+
const pending = decisionQueue.filter(function (d) { return d.status === 'pending' })
|
|
1041
|
+
for (let i = 0; i < pending.length; i++) {
|
|
1042
|
+
const d = pending[i]
|
|
1043
|
+
try {
|
|
1044
|
+
if (d.node === 'spawn') { await spawnChild(d.data.label, d.data.promptText, d.data.meta); d.status = 'resolved'; d.resolution = { action: 'approve', auto: true } }
|
|
1045
|
+
else if (d.node === 'verdict') { await settleVerdict(d.data.task, d.data.verdict); delete tasks[d.data.task.id]; d.status = 'resolved'; d.resolution = { action: 'approve', auto: true } }
|
|
1046
|
+
} catch (e) { console.error('vibe-math-v2: auto-resolve decision failed: ' + String((e && e.message) || e)) }
|
|
1047
|
+
}
|
|
1048
|
+
if (pending.length > 0) { scheduler.gate = null; logActivity('mode', 'switched to auto — auto-resolved ' + pending.length + ' pending decision(s)'); await saveAll(); scheduleTick() }
|
|
1049
|
+
}
|
|
982
1050
|
async function getStatus() {
|
|
983
1051
|
const qs = await getQs(); const propos = await getPropos()
|
|
984
1052
|
return {
|
|
@@ -990,7 +1058,7 @@ export function apply(ctx) {
|
|
|
990
1058
|
propositions: { total: propos.length, resolved: propos.filter(function (p) { return p.布尔估计 === 1 || p.布尔估计 === 0 }).length },
|
|
991
1059
|
pendingDecisions: decisionQueue.filter(function (d) { return d.status === 'pending' }).length,
|
|
992
1060
|
registeredAgents: Object.keys(agentRegistry).length,
|
|
993
|
-
recentActivity: activityLog.slice(-10), params: params,
|
|
1061
|
+
recentActivity: activityLog.slice(-Math.min(10, Number(params.activityLogCap) || 100)), params: params,
|
|
994
1062
|
}
|
|
995
1063
|
}
|
|
996
1064
|
|
|
@@ -1026,8 +1094,8 @@ export function apply(ctx) {
|
|
|
1026
1094
|
registerTool('vibe_math_abort', 'Abort the scheduler and interrupt all active children.', objParams({}), async function () { return await abortScheduler() })
|
|
1027
1095
|
registerTool('vibe_math_status', 'Show scheduler status, params, active agents, projects, and recent activity.', objParams({}), async function () { return await getStatus() })
|
|
1028
1096
|
registerTool('vibe_math_report', 'Return the full progress report and write it to Progress_Logs/report.json.', objParams({}), async function () { await maybeWriteReport(true); return await buildReport() })
|
|
1029
|
-
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 } })
|
|
1030
|
-
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: ['flat', 'forced'] }, reportMode: { type: 'string', enum: ['file', 'push', 'both'] }, promoteValueThreshold: { type: 'number' }, priorityAdjust: { type: 'string', enum: ['none', 'deadend-deprioritize', 'survival-map'] }, 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' }, tickIntervalMs: { type: 'integer' }, activityLogCap: { type: 'integer' }, maxExplorerRetries: { type: 'integer' } }), async function (args) { params = Object.assign({}, params, sanitizeParams(args)); await saveAll(); return { ok: true, params: params } })
|
|
1097
|
+
registerTool('vibe_math_set_mode', 'Switch between manual and auto (preset) mode. Switching to auto auto-resolves any pending manual decisions.', objParams({ mode: { type: 'string', enum: ['manual', 'auto'] } }, ['mode']), async function (args) { params.mode = args.mode; await saveAll(); if (params.mode === 'auto') await autoResolvePending(); return { ok: true, mode: params.mode } })
|
|
1098
|
+
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: ['flat', 'forced'] }, reportMode: { type: 'string', enum: ['file', 'push', 'both'] }, promoteValueThreshold: { type: 'number' }, priorityAdjust: { type: 'string', enum: ['none', 'deadend-deprioritize', 'survival-map'] }, proposPriorityAdjust: { type: 'string', enum: ['none', 'progress-graded'] }, 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' }, tickIntervalMs: { type: 'integer' }, activityLogCap: { type: 'integer' }, maxExplorerRetries: { type: 'integer' } }), async function (args) { params = Object.assign({}, params, sanitizeParams(args)); await saveAll(); return { ok: true, params: params } })
|
|
1031
1099
|
registerTool('vibe_math_setup', 'Return the interactive parameter schema 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' } })
|
|
1032
1100
|
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() })
|
|
1033
1101
|
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') })
|
|
@@ -1054,7 +1122,7 @@ export function apply(ctx) {
|
|
|
1054
1122
|
if (cmd === 'abort') return await abortScheduler()
|
|
1055
1123
|
if (cmd === 'status') return await getStatus()
|
|
1056
1124
|
if (cmd === 'report') { await maybeWriteReport(true); return await buildReport() }
|
|
1057
|
-
if (cmd === 'mode') { params.mode = (args[0] === 'manual') ? 'manual' : 'auto'; await saveAll(); return { ok: true, mode: params.mode } }
|
|
1125
|
+
if (cmd === 'mode') { params.mode = (args[0] === 'manual') ? 'manual' : 'auto'; await saveAll(); if (params.mode === 'auto') await autoResolvePending(); return { ok: true, mode: params.mode } }
|
|
1058
1126
|
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' } }
|
|
1059
1127
|
if (cmd === 'save') return await saveSettings()
|
|
1060
1128
|
if (cmd === 'template') return await createTemplate(args[0] === 'project' ? 'project' : 'global')
|