dsh-vibe-math 1.1.0 → 1.2.1
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 +8 -3
- package/package.json +2 -2
- package/vibe-math-v3/vibe-math-v3.js +220 -15
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
## 🧩 架构图(v1 + v2 + v3)
|
|
21
21
|
|
|
22
|
-
> 静态架构图;完整流程说明见 [docs/架构图.md](docs/架构图.md);可编辑生成脚本:[v1](docs/generate_framework_diagram.py) / [v2](docs/generate_framework_diagram_v2.py)。
|
|
22
|
+
> 静态架构图;完整流程说明见 [docs/架构图.md](docs/架构图.md);可编辑生成脚本:[v1](docs/generate_framework_diagram.py) / [v2](docs/generate_framework_diagram_v2.py) / [v3](docs/generate_framework_diagram_v3.py)。
|
|
23
23
|
|
|
24
24
|
### Vibe Math V1(经典流水线)⚠️ 早期架构 · 将弃用 · 不建议新项目使用
|
|
25
25
|
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
|
|
36
36
|
### Vibe Math V3(论文式 md + 规划代理 + 方法库)✅ 主推
|
|
37
37
|
|
|
38
|
+

|
|
39
|
+
|
|
38
40
|
**一句话流水线**:全部知识以 **Markdown 论文/研究报告式**存储与续写(`Problems/` 问题清单含依赖/后生问题来源动机、`Progress/` 研究日志按方向按轮续写、`Propos/` 命题库、`Methods/` 通用理论发明库、`Verified/` 绝对可信)→ 调度前调度器构造状态简报并调用**规划代理**,规划代理一次性安排接下来 N 步(spawn solver/verifier/explorer/method-keeper、interrupt、promote、wait),代码校验后执行(超出并发的动作排队跨 tick 消费;规划失败自动回退 v2 式启发式)→ 验证器独立审查→辩论→**近共识裁决**(同侧且均值 ≥0.85/≤0.15 取均值,修复 v2 flat 误判)→ 概率=1 收口并生成 `Verified/` 卡 → 求解器的 `methods_used`/`new_inventions` 上报由 **Method Keeper** 沉淀/完善方法库(可组成体系层级、跨项目复用)。
|
|
39
41
|
|
|
40
42
|
**一句话流水线**:全部知识以 **Markdown 论文/研究报告式**存储与续写(`Problems/` 问题清单含依赖/后生问题来源动机、`Progress/` 研究日志按方向按轮续写、`Propos/` 命题库、`Methods/` 通用理论发明库、`Verified/` 绝对可信)→ 调度前调度器构造状态简报并调用**规划代理**,规划代理一次性安排接下来 N 步(spawn solver/verifier/explorer/method-keeper、interrupt、promote、wait),代码校验后执行(超出并发的动作排队跨 tick 消费;规划失败自动回退 v2 式启发式)→ 验证器独立审查→辩论→**近共识裁决**(同侧且均值 ≥0.85/≤0.15 取均值,修复 v2 flat 误判)→ 概率=1 收口并生成 `Verified/` 卡 → 求解器的 `methods_used`/`new_inventions` 上报由 **Method Keeper** 沉淀/完善方法库(可组成体系层级、跨项目复用)。
|
|
@@ -217,7 +219,8 @@ dsh plugin --profile <你的 profile> add github:ChongCyrus/Vibe-Mathematics
|
|
|
217
219
|
├─ vibe_math_setting.json # 该项目默认参数
|
|
218
220
|
├─ Problems/<id>.md # 问题清单:每问题一个 md(软规范锚点:ID/类型/状态/优先级/依赖/被依赖/来源/计划
|
|
219
221
|
│ # + ## 陈述 / ## 来源与动机(后生问题:产生流程/动机/回填计划)/ ## 解法候选)
|
|
220
|
-
├─ Progress/<id>.md #
|
|
222
|
+
├─ Progress/<id>.md # 聚合研究日志索引(各方向摘要 + 引理索引 + 各轮记录)
|
|
223
|
+
├─ Progress/<id>/<方向id>.md # 每方向一个独立文件(代理自组织直接写,方向间无并发冲突)
|
|
221
224
|
├─ Propos/<分类>/<id>.md # 命题库:每命题一个 md(陈述/证明尝试/证伪尝试,软规范锚点 + 自由叙述)
|
|
222
225
|
├─ Methods/<id>.md # 【通用理论发明库】方法卡:理论体系/框架/工具/方法/思想(含应用记录/改进历史/体系层级)
|
|
223
226
|
├─ Verified/命题/<id>.md # 绝对可信:调度器生成的已验证命题卡(只读)
|
|
@@ -231,7 +234,7 @@ dsh plugin --profile <你的 profile> add github:ChongCyrus/Vibe-Mathematics
|
|
|
231
234
|
```
|
|
232
235
|
|
|
233
236
|
**铁律(v1/v2 通用)**:调度器是**唯一文件写者**(子代理只返回结构化 JSON,从不写文件)。
|
|
234
|
-
**v3 铁律**:只有 `Verified/` 与验证器判真/假的对象**绝对可信**;其余 md
|
|
237
|
+
**v3 铁律**:只有 `Verified/` 与验证器判真/假的对象**绝对可信**;其余 md(未定论命题、研究日志、方法库未验证断言)仅作经验参考;调度器只解析软规范锚点行与条目标题行,从不解析正文散文。**v3 支持代理直接写 md**(自组织定位各自归属文件,如求解器写 `Progress/<id>/<方向id>.md`、新引理写 `Propos/<分类>/<id>.md`、方法整理代理写 `Methods/<id>.md`);并发安全靠**写锁**——写任何文件前调 `vibe_math_claim_write`、写完 `vibe_math_release_write`(同一文件同一时刻只允许一个代理写),内容留在 md,轻量元数据经 `vibe_math_sync_meta` 上报给调度器。
|
|
235
238
|
|
|
236
239
|
---
|
|
237
240
|
|
|
@@ -276,6 +279,8 @@ dsh plugin --profile <你的 profile> add github:ChongCyrus/Vibe-Mathematics
|
|
|
276
279
|
| `vibe_math_index`(v3) | 从 md 知识库重建机器索引(`State/index.json`) |
|
|
277
280
|
| `vibe_math_method_add` / `vibe_math_method_list`(v3) | 手动添加 / 列出方法卡(项目 + 全局) |
|
|
278
281
|
| `vibe_math_lock_status`(v3) | 查看项目锁占用 |
|
|
282
|
+
| `vibe_math_claim_write` / `vibe_math_release_write`(v3) | 申请 / 释放某个 md 文件的**写锁**(代理直接写文件前调用;同一文件同一时刻只允许一个代理写,防并发冲突) |
|
|
283
|
+
| `vibe_math_sync_meta`(v3) | 代理把内容写进 md 后上报**轻量元数据**(方向状态/存活率/引理 id/方法卡 id/新发明),让调度器同步索引——内容留在 md,不进 JSON |
|
|
279
284
|
|
|
280
285
|
斜杠命令(与工具等价):`/vibe start|resume|pause|abort|status|report|mode <auto|manual>|setup|save|template [global|project]|add <id> <desc>|add-proposition <id> <概述>|list-propositions|project [list|new <name>|<name>]|decisions|agents`(v3 另有 `methods|index|plan|lock`)
|
|
281
286
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-vibe-math",
|
|
3
|
-
"description": "Multi-agent mathematical problem-solving & verification frameworks for DeepSeek Harness — THREE agent presets in one install: vibe-math-v1 (classic pipeline), vibe-math-v2 (probability-driven: qs.json + Propos knowledge base + explorer→solver→review/debate verdict), and vibe-math-v3 (THIRD-generation, recommended: paper-style Markdown knowledge base with Problems/Progress/Propos/Methods/Verified + planner-agent scheduling that decides the next N actions + universal theory/method invention library). Installing this bundle auto-installs all three presets into the DSH preset root.",
|
|
4
|
-
"version": "1.1
|
|
3
|
+
"description": "Multi-agent mathematical problem-solving & verification frameworks for DeepSeek Harness — THREE agent presets in one install: vibe-math-v1 (classic pipeline), vibe-math-v2 (probability-driven: qs.json + Propos knowledge base + explorer→solver→review/debate verdict), and vibe-math-v3 (THIRD-generation, recommended: paper-style Markdown knowledge base with Problems/Progress/Propos/Methods/Verified + planner-agent scheduling that decides the next N actions + universal theory/method invention library + agents write their own Markdown directly via a per-file write lock). Installing this bundle auto-installs all three presets into the DSH preset root.",
|
|
4
|
+
"version": "1.2.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "installer.js",
|
|
7
7
|
"exports": {
|
|
@@ -59,6 +59,7 @@ export function apply(ctx) {
|
|
|
59
59
|
// ================= per-session registry =================
|
|
60
60
|
const sessions = new Map() // rootAgentId -> Session
|
|
61
61
|
const childOwner = new Map() // childId -> rootAgentId (route subagent/end back to its session)
|
|
62
|
+
const fileOwner = {} // 进程级写锁:fileKey -> { childId, sessionId, at } —— 防任何代理(跨会话)并发写同一 md 文件
|
|
62
63
|
// Process epoch: PROCESS-level (one per apply, shared by every session), written to
|
|
63
64
|
// State/process_epoch.json at init; a DIFFERENT persisted epoch means a previous DSH
|
|
64
65
|
// process wrote this state (in-flight children are gone), while an equal epoch means
|
|
@@ -567,7 +568,27 @@ export function apply(ctx) {
|
|
|
567
568
|
async function saveProposition(p) { await writeText('Propos/' + categoryOf(p) + '/' + p.id + '.md', composePropositionMd(p)) }
|
|
568
569
|
async function saveMethod(m, global) { if (global) { await writeTextAbs(vibeRoot() + '/Methods/' + m.id + '.md', composeMethodMd(m)) } else { await writeText('Methods/' + m.id + '.md', composeMethodMd(m)) } }
|
|
569
570
|
async function saveVerified(card) { await writeText('Verified/' + (card.类型 === '问题' ? '问题' : '命题') + '/' + card.id + '.md', composeVerifiedMd(card)) }
|
|
570
|
-
|
|
571
|
+
async function ensureProgressDir(qid) { const base = frameworkRoot(); return await runShell('New-Item -Force -ItemType Directory -Path ' + psQuote(base + '/Progress/' + qid) + ' | Out-Null') }
|
|
572
|
+
// 单个方向的完整日志文本(供"每方向一个文件"与聚合复用)
|
|
573
|
+
function directionMdText(qid, d, standalone) {
|
|
574
|
+
const lines = []
|
|
575
|
+
if (standalone) { lines.push('# 研究方向日志|' + qid + ' / ' + d.id); lines.push('') }
|
|
576
|
+
lines.push('- 方向: ' + d.title)
|
|
577
|
+
lines.push('- 存活率: ' + (d.survival != null ? d.survival : '?') + ';状态: ' + d.status + (d.round ? ';轮次: ' + d.round : ''))
|
|
578
|
+
if (d.method) lines.push('- 方法: ' + d.method)
|
|
579
|
+
if (d.core_assumption) lines.push('- 核心假设: ' + d.core_assumption)
|
|
580
|
+
if (d.dead_end_reason) lines.push('- 死路原因: ' + d.dead_end_reason)
|
|
581
|
+
if (d.lemmas && d.lemmas.length) lines.push('- 引理索引: ' + d.lemmas.map(function (l) { return '「' + l.title + '」(' + l.id + ')' }).join(';'))
|
|
582
|
+
lines.push('')
|
|
583
|
+
for (const j of (d.journal || [])) {
|
|
584
|
+
lines.push('### 第 ' + j.round + ' 轮|' + (j.agent || '') + '|' + (j.at || ''))
|
|
585
|
+
lines.push(j.prose || '')
|
|
586
|
+
lines.push('')
|
|
587
|
+
}
|
|
588
|
+
return lines.join('\n').trimEnd() + '\n'
|
|
589
|
+
}
|
|
590
|
+
// 研究日志 = 每方向一个独立文件(由代理直接写:Progress/<qid>/<dirId>.md,方向间无并发冲突)
|
|
591
|
+
// + 聚合索引(Progress/<qid>.md,由调度器从 dirState 汇总,不覆盖各方向文件)
|
|
571
592
|
async function writeJournal(qid) {
|
|
572
593
|
const dirs = dirState.get(qid) || []
|
|
573
594
|
const lines = []
|
|
@@ -579,9 +600,10 @@ export function apply(ctx) {
|
|
|
579
600
|
lines.push('## 方向 ' + d.id + '|' + d.title + '|存活率' + (d.survival != null ? d.survival : '?') + '|状态' + d.status)
|
|
580
601
|
if (d.core_assumption) { lines.push(''); lines.push('**核心假设**:' + d.core_assumption) }
|
|
581
602
|
if (d.method) { lines.push('**方法**:' + d.method) }
|
|
582
|
-
if (d.
|
|
583
|
-
|
|
584
|
-
if (d.lemmas && d.lemmas.length) { lines.push('
|
|
603
|
+
if (d.dead_end_reason) { lines.push('**死路原因**:' + d.dead_end_reason) }
|
|
604
|
+
// 引理只在摘要给"索引"(id+标题),完整叙述在各方向文件 Progress/<qid>/<dirId>.md(由代理或调度器写入)
|
|
605
|
+
if (d.lemmas && d.lemmas.length) { lines.push('**引理索引**:' + d.lemmas.map(function (l) { return '「' + l.title + '」(' + l.id + ')' }).join(';')) }
|
|
606
|
+
lines.push('**完整叙述**:见 `Progress/' + qid + '/' + d.id + '.md`')
|
|
585
607
|
for (const j of (d.journal || [])) {
|
|
586
608
|
lines.push('')
|
|
587
609
|
lines.push('### 第 ' + j.round + ' 轮|' + (j.agent || '') + '|' + (j.at || ''))
|
|
@@ -591,6 +613,24 @@ export function apply(ctx) {
|
|
|
591
613
|
}
|
|
592
614
|
await writeText('Progress/' + qid + '.md', lines.join('\n').trimEnd() + '\n')
|
|
593
615
|
}
|
|
616
|
+
// 代理直接写内容:模拟/落盘代理声称写的文件(__writes),真实环境代理用 write 工具自己写,此处为调度器兜底落盘
|
|
617
|
+
async function applyAgentWrites(writes) {
|
|
618
|
+
if (!Array.isArray(writes)) return { ok: true, applied: 0 }
|
|
619
|
+
let applied = 0
|
|
620
|
+
for (const w of writes) {
|
|
621
|
+
if (!w || !w.path) continue
|
|
622
|
+
const safe = String(w.path).replace(/\\/g, '/').replace(/\.\./g, '')
|
|
623
|
+
if (!/^(Problems|Progress|Propos|Methods|Notes)\//.test(safe)) continue // 只允许知识库路径,防越界
|
|
624
|
+
const content = (w.content != null) ? String(w.content) : ''
|
|
625
|
+
// Progress/<qid>/<dir>.md 需要 Progress/<qid>/ 子目录
|
|
626
|
+
const m = /^Progress\/([^/]+)\//.exec(safe)
|
|
627
|
+
if (m) { const base = frameworkRoot(); await runShell('New-Item -Force -ItemType Directory -Path ' + psQuote(base + '/Progress/' + m[1]) + ' | Out-Null') }
|
|
628
|
+
const t = await fs.resolve(safe, { cwd: frameworkRoot() })
|
|
629
|
+
if (await fs.stat(t) !== undefined) { await fs.writeText(t, content, undefined, undefined, getPolicy()); applied += 1 }
|
|
630
|
+
else { await writeText(safe, content); applied += 1 }
|
|
631
|
+
}
|
|
632
|
+
return { ok: true, applied: applied }
|
|
633
|
+
}
|
|
594
634
|
// 把将被替换的旧方向归档为"已归档方向"段(保留论文式历史,重派生不丢记录)
|
|
595
635
|
async function archiveDirections(qid, oldDirs) {
|
|
596
636
|
const withJournal = oldDirs.filter(function (d) { return d.journal && d.journal.length > 0 })
|
|
@@ -863,8 +903,13 @@ export function apply(ctx) {
|
|
|
863
903
|
'- 方法卡 Methods/<id>.md:{ ID, 类型:方法, 状态:经验|应用验证|含已验证断言, 可信断言:[](只允许已进 Verified/ 的 ID), 上级体系/子方法/相关, 适用场景, ## 核心内容, ## 定义与记号, ## 应用记录, ## 改进历史 }。\n' +
|
|
864
904
|
'- 收口规则:某个解法/证明/证伪 概率=1 → 问题已解决 / 命题已验证(状态/概率锚点由调度器改写)。\n' +
|
|
865
905
|
'\n3) FOLDERS:Problems/ 问题清单;Progress/ 研究日志(每问题一个 md,按方向按轮续写);Propos/ 命题库;Methods/ 理论发明库;Verified/ 绝对可信(只读);Reliable/ 可信参考文献(只读);Notes/ 自由笔记;Logs/ 审计;State/ 调度器私有——不要读也不要改。\n' +
|
|
866
|
-
'\n4) METHOD LIBRARY RULES:开工前先查 Methods/(含全局 VibeMath/Methods/),有可复用方法/体系则引用其 ID;用后必须在 methods_used 上报(含效果与改进建议);本轮新发明/经验性总结必须在 new_inventions 上报(类型:理论体系|框架|工具|方法|思想|范式|技巧)——若与某张已有方法卡同类,在内容描述里注明"可并入 m-xxx"以便 Method Keeper
|
|
867
|
-
'\n5) OUTPUT REQUIREMENTS:完整性、不断章取义——任何输出的问题/命题/结论都要给出完整陈述并补全所依赖的对象/环境/背景定义;引用必须给出处(文件路径 + ID + 锚点/节),事实只引 Verified/;若结论依赖临时假设 p,必须显式写「若 <p 完整陈述> 成立,则:…」;只输出规定的 JSON(```json 围栏内),JSON 之外不写任何内容。'
|
|
906
|
+
'\n4) METHOD LIBRARY RULES:开工前先查 Methods/(含全局 VibeMath/Methods/),有可复用方法/体系则引用其 ID;用后必须在 methods_used 上报(含效果与改进建议);本轮新发明/经验性总结必须在 new_inventions 上报(类型:理论体系|框架|工具|方法|思想|范式|技巧)——若与某张已有方法卡同类,在内容描述里注明"可并入 m-xxx"以便 Method Keeper 合并而非重复建卡。**重要区分**:methods_used 只能填**已存在方法卡的 ID**(形如 m-abc12345,来自 AVAILABLE METHODS 列表);你自己刚想出的新方法/新技巧不属于 methods_used,请如实填入 new_inventions(由 Method Keeper 蒸馏建卡);千万不要把方法名/标题文字当 id 填进 methods_used。\n' +
|
|
907
|
+
'\n5) OUTPUT REQUIREMENTS:完整性、不断章取义——任何输出的问题/命题/结论都要给出完整陈述并补全所依赖的对象/环境/背景定义;引用必须给出处(文件路径 + ID + 锚点/节),事实只引 Verified/;若结论依赖临时假设 p,必须显式写「若 <p 完整陈述> 成立,则:…」;只输出规定的 JSON(```json 围栏内),JSON 之外不写任何内容。' +
|
|
908
|
+
'\n\n6) WRITE-INTO-MD WORKFLOW(推荐,代理自组织直接写 md):框架允许你**直接写 Markdown**,把研究内容落到对应路径的 md 文件里,而不是全部塞进 JSON。做法(你拥有 file 工具):\n' +
|
|
909
|
+
'- 每个角色有明确的"归属文件":求解器写该方向的完整叙述到 `Progress/<问题id>/<方向id>.md`;新引理写一张完整命题卡到 `Propos/<分类>/<p-id>.md`(含锚点 `# 命题|标题`、`- ID/状态/概率/优先级` 与 `## 陈述`/`## 证明尝试`);方法整理代理写 `Methods/<m-id>.md`(锚点 `- ID/类型/状态/可信断言/适用场景` + `## 核心内容`/`## 应用记录`/`## 改进历史`)。\n' +
|
|
910
|
+
'- **并发写安全**:写任何文件前先 `vibe_math_claim_write({target:"<相对项目根的路径>"})` 申请写锁(同一文件同一时刻只允许一个代理写;若返回 busy 请稍后重试),写完 `vibe_math_release_write({target})`。不同方向是不同文件,天然不冲突。\n' +
|
|
911
|
+
'- 写完内容后,用 `vibe_math_sync_meta({meta:{kind:"solver|directions|methods", ...}})` 上报**轻量元数据**(方向状态/存活率/引理 id 及**证明 proof**/方法卡 id/新发明清单),让调度器更新索引与调度——内容留在 md,只有调度元数据与**待验证的证明**才进机读接口(证明是验证必需,必须随 lemmas 上报,否则验证器无法核验)。\n' +
|
|
912
|
+
'- 若你所在环境无法写文件(工具不可用/被拒),再回退到"把内容放进下面的 JSON 字段"由调度器落盘。两种方式二选一即可,不要重复。'
|
|
868
913
|
}
|
|
869
914
|
function knowledgeContextText() { const k = params.knowledgeContext ? String(params.knowledgeContext) : defaultKnowledgeContext(); return k ? ('\n' + k + '\n') : '' }
|
|
870
915
|
function capabilitiesText(role) {
|
|
@@ -953,7 +998,8 @@ export function apply(ctx) {
|
|
|
953
998
|
head += '\nIMPORTANT — PROBABILITY RULES FOR NEW RESULTS: any 概率 / solution_probability / survival_probability you output for NEW results must be strictly BETWEEN 0 and 1 (they await independent verifier confirmation). NEVER mark your own fresh lemma or solution as 1 or 0 — that is the verifiers\' job. Only facts already recorded in Verified/ count as certain.\n'
|
|
954
999
|
head += '\nIf you obtain a COMPLETE solution: adversarially self-check (construct counterexamples, test boundary conditions) BEFORE declaring success; put the full solution text in "solution".\n'
|
|
955
1000
|
head += '\nRespond with ONLY a single JSON object wrapped in a ```json code fence — no prose:\n' +
|
|
956
|
-
'{"status":"continue|success|dead-end","solution":"complete solution text, or null","solution_probability":0.85,"lemmas":[{"title":"...","statement":"...","proof":"...","细类型":{"
|
|
1001
|
+
'{"status":"continue|success|dead-end","solution":"complete solution text, or null","solution_probability":0.85,"lemmas":[{"title":"...","statement":"...","proof":"...","细类型":{"分析":{}},"布尔估计":0.6,"价值/关键性":0.5,"优先级":1}],"routes":[{"title":"...","progress":"...","feasibility_signal":"...","blocker":"..."}],"lessons":["..."],"survival_probability":0.5,"dead_end_reason":"... or null","sub_questions":[{"q_sub_title":"...","q_sub_statement":"完整问题陈述(含所有对象/定义)","assumption_title":"p_{q-tmp} 标题","assumption_statement":"完整假设陈述(含所有定义)"}],"methods_used":[{"id":"<已有方法卡的ID,形如 m-abc12345,必须是 AVAILABLE METHODS 中出现的 id>","效果":"...","建议":"..."}],"new_inventions":[{"类型":"...","标题":"...","内容描述":"...","是否已入库":false}]}\n' +
|
|
1002
|
+
'区分规则:methods_used 只能填**已存在的方法卡 ID**(m-…,来自 AVAILABLE METHODS 列表)——引用你自己刚想出的新方法/新技巧不属于 methods_used,请如实填入 new_inventions(它会由 Method Keeper 蒸馏建卡);不要把方法名/标题当 id 填进 methods_used。'
|
|
957
1003
|
return head
|
|
958
1004
|
}
|
|
959
1005
|
function verifierTargetText(r) {
|
|
@@ -1557,6 +1603,12 @@ export function apply(ctx) {
|
|
|
1557
1603
|
async function handleExplorer(childId, meta, output) {
|
|
1558
1604
|
delete agentRegistry[childId]
|
|
1559
1605
|
const parsed = parseJson(output)
|
|
1606
|
+
// 新协议(代理直接写 md + sync_meta):__writes 落盘,meta.kind==='directions' 走元数据同步
|
|
1607
|
+
if (parsed && ((Array.isArray(parsed.__writes) && parsed.__writes.length) || (parsed.meta && parsed.meta.kind === 'directions'))) {
|
|
1608
|
+
await applyAgentWrites(parsed.__writes)
|
|
1609
|
+
if (parsed.meta && parsed.meta.kind === 'directions') await syncMeta(parsed.meta, { id: childId })
|
|
1610
|
+
await saveAll(); return
|
|
1611
|
+
}
|
|
1560
1612
|
const dirs = (parsed && parsed.directions) || []
|
|
1561
1613
|
await consumeMethodFeedback(parsed, { qid: meta.qid })
|
|
1562
1614
|
if (dirs.length === 0) { logActivity('explorer', 'problem ' + meta.qid + ' returned no directions (output head: ' + String(output || '').slice(0, 200) + ')'); await saveAll(); return }
|
|
@@ -1575,6 +1627,13 @@ export function apply(ctx) {
|
|
|
1575
1627
|
async function handleSolver(childId, meta, output, stopReason) {
|
|
1576
1628
|
const qid = meta.qid; const dirId = meta.direction
|
|
1577
1629
|
const parsed = parseJson(output)
|
|
1630
|
+
// 新协议(代理直接写 md + sync_meta):__writes 落盘,meta.kind==='solver' 走元数据同步
|
|
1631
|
+
if (parsed && ((Array.isArray(parsed.__writes) && parsed.__writes.length) || (parsed.meta && parsed.meta.kind === 'solver'))) {
|
|
1632
|
+
delete agentRegistry[childId]
|
|
1633
|
+
await applyAgentWrites(parsed.__writes)
|
|
1634
|
+
if (parsed.meta && parsed.meta.kind === 'solver') await syncMeta(parsed.meta, { id: childId })
|
|
1635
|
+
await saveAll(); return
|
|
1636
|
+
}
|
|
1578
1637
|
const q = problems.get(qid); if (!q) { delete agentRegistry[childId]; return }
|
|
1579
1638
|
const dirs = getDirState(qid)
|
|
1580
1639
|
const dir = dirs.find(function (d) { return d.id === dirId })
|
|
@@ -1715,7 +1774,12 @@ export function apply(ctx) {
|
|
|
1715
1774
|
for (const mu of parsed.methods_used) {
|
|
1716
1775
|
if (!mu || !mu.id) continue
|
|
1717
1776
|
const m = methods.get(mu.id) || globalMethods.get(mu.id)
|
|
1718
|
-
if (!m) {
|
|
1777
|
+
if (!m) {
|
|
1778
|
+
// 未知 id:solver 引用了一个尚未入卡的方法/技巧 → 作为待沉淀发明记录,防引用丢失(Method Keeper 将据此建卡)
|
|
1779
|
+
methodLog.pendingInventions.push({ at: now(), 来源: ctx.qid ? ('问题 ' + ctx.qid + (ctx.dirId ? ' 方向 ' + ctx.dirId : '')) : '', 类型: '方法', 标题: String(mu.id), 内容描述: (mu.效果 || '') + (mu.建议 ? ';建议:' + mu.建议 : '') })
|
|
1780
|
+
logActivity('method', 'methods_used referenced unknown method ' + mu.id + ' → queued as pending invention')
|
|
1781
|
+
continue
|
|
1782
|
+
}
|
|
1719
1783
|
if (methods.has(mu.id)) {
|
|
1720
1784
|
m.applications = m.applications || []
|
|
1721
1785
|
m.applications.push({ at: fmtTime(), 问题: ctx.qid || '', 方向: ctx.dirId || '', text: (mu.效果 || '') + (mu.建议 ? ';建议:' + mu.建议 : '') })
|
|
@@ -1753,21 +1817,32 @@ export function apply(ctx) {
|
|
|
1753
1817
|
logActivity('method', 'method keeper spawned (' + why + '), pending inventions: ' + methodLog.pendingInventions.length)
|
|
1754
1818
|
}
|
|
1755
1819
|
async function buildMethodDigest() {
|
|
1820
|
+
// 精简摘要:只给标题/类型/来源 + 一句摘要,避免 pending 全文压垮 Method Keeper
|
|
1756
1821
|
const lines = []
|
|
1757
|
-
lines.push('- 待沉淀发明 ' + methodLog.pendingInventions.length + '
|
|
1758
|
-
for (const inv of methodLog.pendingInventions.slice(-
|
|
1759
|
-
|
|
1760
|
-
|
|
1822
|
+
lines.push('- 待沉淀发明 ' + methodLog.pendingInventions.length + ' 条(仅列标题/类型/来源):')
|
|
1823
|
+
for (const inv of methodLog.pendingInventions.slice(-16)) {
|
|
1824
|
+
const desc = String(inv.内容描述 || '').slice(0, 60)
|
|
1825
|
+
lines.push(' * [' + (inv.类型 || '') + '] ' + (inv.标题 || '') + (inv.来源 ? '(' + inv.来源 + ')' : '') + (desc ? ':' + desc + '…' : ''))
|
|
1826
|
+
}
|
|
1827
|
+
const recentProps = allPropos().slice(-6).map(function (p) { return p.id + '「' + p.标题 + '」概率=' + p.概率 })
|
|
1828
|
+
if (recentProps.length) lines.push('- 最近命题:' + recentProps.join(';'))
|
|
1761
1829
|
const recentDirs = []
|
|
1762
1830
|
for (const [qid, dirs] of dirState) {
|
|
1763
|
-
for (const d of dirs.slice(-
|
|
1831
|
+
for (const d of dirs.slice(-1)) if (d.lessons && d.lessons.length) recentDirs.push(qid + '/' + d.id + '「' + d.title + '」教训摘要:' + d.lessons.join(';').slice(0, 80))
|
|
1764
1832
|
}
|
|
1765
|
-
if (recentDirs.length) { lines.push('-
|
|
1833
|
+
if (recentDirs.length) { lines.push('- 最近方向教训(摘要):'); lines.push.apply(lines, recentDirs) }
|
|
1766
1834
|
return lines.join('\n')
|
|
1767
1835
|
}
|
|
1768
1836
|
async function handleMethodKeeper(childId, meta, output) {
|
|
1769
1837
|
delete agentRegistry[childId]
|
|
1770
1838
|
const parsed = parseJson(output)
|
|
1839
|
+
// 新协议(Method Keeper 直接写方法卡 md + sync_meta):__writes 落盘,meta.kind==='methods' 登记
|
|
1840
|
+
if (parsed && ((Array.isArray(parsed.__writes) && parsed.__writes.length) || (parsed.meta && parsed.meta.kind === 'methods'))) {
|
|
1841
|
+
await applyAgentWrites(parsed.__writes)
|
|
1842
|
+
if (parsed.meta && parsed.meta.kind === 'methods') await syncMeta(parsed.meta, { id: childId })
|
|
1843
|
+
if (parsed.meta && Array.isArray(parsed.meta.created) && parsed.meta.created.length > 0) methodLog.pendingInventions = []
|
|
1844
|
+
await saveAll(); return
|
|
1845
|
+
}
|
|
1771
1846
|
if (!parsed) { logActivity('method', 'method keeper returned nothing usable'); await saveAll(); return }
|
|
1772
1847
|
let created = 0
|
|
1773
1848
|
if (Array.isArray(parsed.new_methods)) {
|
|
@@ -1801,7 +1876,24 @@ export function apply(ctx) {
|
|
|
1801
1876
|
// 消费已沉淀的发明:Method Keeper 有有效输出(新建或改进)即视为已处理本轮 pending。
|
|
1802
1877
|
// 修复:仅返回 improvements 而无可新建方法时也必须清空,否则 pending 永不归零导致反复整理。
|
|
1803
1878
|
const hasOutput = (Array.isArray(parsed.new_methods) && parsed.new_methods.length > 0) || (Array.isArray(parsed.improvements) && parsed.improvements.length > 0)
|
|
1804
|
-
|
|
1879
|
+
// 兜底建卡:Method Keeper 未产出(输出不合规/空对象/模型未能蒸馏)时,把 pending 发明直接建成草稿方法卡,
|
|
1880
|
+
// 保证"发明不因一次梳理失败而永久滞留";草稿卡状态=经验、来源=草稿沉淀,后续可被 Method Keeper 再整理。
|
|
1881
|
+
let fallback = 0
|
|
1882
|
+
if (!hasOutput && methodLog.pendingInventions.length > 0) {
|
|
1883
|
+
for (const inv of methodLog.pendingInventions.slice(-20)) {
|
|
1884
|
+
if (!inv || !inv.标题) continue
|
|
1885
|
+
const id = 'm-' + shortId()
|
|
1886
|
+
const m = {
|
|
1887
|
+
id: id, 标题: String(inv.标题), 类型: inv.类型 || '方法', 状态: '经验',
|
|
1888
|
+
可信断言: [], 上级体系: [], 子方法: [], 相关: [],
|
|
1889
|
+
适用场景: '', 核心内容: String(inv.内容描述 || ''), 定义与记号: '',
|
|
1890
|
+
applications: [], improvements: [{ v: 1, 原因: '草稿沉淀', text: 'Method Keeper 未产出,按发明清单兜底建卡(来源:' + (inv.来源 || '') + ')' }], 来源: '草稿沉淀(' + (inv.来源 || '') + ')',
|
|
1891
|
+
}
|
|
1892
|
+
if (!methods.has(id)) { methods.set(id, m); await saveMethod(m, false); fallback += 1 }
|
|
1893
|
+
}
|
|
1894
|
+
if (fallback > 0) logActivity('method', 'method keeper 未产出,兜底建 ' + fallback + ' 张草稿方法卡(防发明滞留)')
|
|
1895
|
+
}
|
|
1896
|
+
if (hasOutput || fallback > 0) methodLog.pendingInventions = []
|
|
1805
1897
|
methodLog.keepCount += 1
|
|
1806
1898
|
methodLog.lastKeepAt = now()
|
|
1807
1899
|
await saveAll()
|
|
@@ -2021,6 +2113,9 @@ export function apply(ctx) {
|
|
|
2021
2113
|
|
|
2022
2114
|
// ================= child result dispatch =================
|
|
2023
2115
|
async function onChildEnd(info) {
|
|
2116
|
+
// 子代理结束/中断:自动释放它持有的所有写锁(防锁残留导致文件被永久锁住)
|
|
2117
|
+
const endedId = String(info.id)
|
|
2118
|
+
for (const k of Object.keys(fileOwner)) { if (String(fileOwner[k].childId) === endedId) delete fileOwner[k] }
|
|
2024
2119
|
const meta = agentRegistry[info.id]
|
|
2025
2120
|
if (meta === undefined) return
|
|
2026
2121
|
scheduler.activeCount = Math.max(0, scheduler.activeCount - 1)
|
|
@@ -2180,6 +2275,113 @@ export function apply(ctx) {
|
|
|
2180
2275
|
registerTool('vibe_math_method_add', 'Manually add a method card to Methods/ (creates Methods/<id>.md).', objParams({ id: { type: 'string' }, 标题: { type: 'string' }, 类型: { type: 'string' }, 核心内容: { type: 'string' }, 适用场景: { type: 'string' } }, ['id', '标题']), async function (args) { if (methods.has(args.id)) return { ok: false, message: 'method id already exists' }; const m = { id: args.id, 标题: args.标题, 类型: args.类型 || '方法', 状态: '经验', 可信断言: [], 上级体系: [], 子方法: [], 相关: [], 适用场景: args.适用场景 || '', 核心内容: args.核心内容 || '', 定义与记号: '', applications: [], improvements: [], 来源: 'user' }; methods.set(m.id, m); await saveMethod(m, false); await rebuildIndex(); return { ok: true, method: m, file: 'Methods/' + m.id + '.md' } })
|
|
2181
2276
|
registerTool('vibe_math_method_list', 'List methods from Methods/ (+ global VibeMath/Methods/): id, 标题, 类型, 状态, 可信断言, applications count.', objParams({}), async function () { const all = Array.from(methods.values()); const g = Array.from(globalMethods.values()); return { ok: true, count: all.length, globalCount: g.length, methods: all.map(function (m) { return { id: m.id, 标题: m.标题, 类型: m.类型, 状态: m.状态, 可信断言: m.可信断言 || [], applications: (m.applications || []).length, global: false } }).concat(g.map(function (m) { return { id: m.id, 标题: m.标题, 类型: m.类型, 状态: m.状态, 可信断言: m.可信断言 || [], applications: (m.applications || []).length, global: true } })) } })
|
|
2182
2277
|
registerTool('vibe_math_lock_status', 'Show the project lock occupancy.', objParams({}), async function () { return { ok: true, project: currentProject, lock: projectLock } })
|
|
2278
|
+
registerTool('vibe_math_claim_write', 'Acquire the write lock for one target file (relative to the project root). Call before writing a Markdown file directly; a file may only be written by ONE agent at a time. Returns the display path you may write (VibeMath/Projects/<project>/<target>) and a hint.', objParams({ target: { type: 'string' } }, ['target']), async function (args, agent) { return await claimWrite(String(args.target || ''), agent) })
|
|
2279
|
+
registerTool('vibe_math_release_write', 'Release the write lock for one target file (relative to the project root). Call after you finished writing it.', objParams({ target: { type: 'string' } }, ['target']), async function (args, agent) { return await releaseWrite(String(args.target || ''), agent) })
|
|
2280
|
+
registerTool('vibe_math_sync_meta', 'Report lightweight scheduling metadata after you wrote content to Markdown files (direction status/survival, registered lemma ids, methods_used/new_inventions, new method cards). Content itself stays in the md files; this only keeps the scheduler index/state in sync.', objParams({ meta: { type: 'object' } }, ['meta']), async function (args, agent) { return await syncMeta(args.meta || {}, agent) })
|
|
2281
|
+
|
|
2282
|
+
// ---- 代理直接写 md 的写锁 + 轻元数据同步(任务2:代理自组织写各自对应路径的 md,避免并发写同一文件) ----
|
|
2283
|
+
async function claimWrite(target, agent) {
|
|
2284
|
+
const childId = (agent && agent.id) ? String(agent.id) : 'scheduler'
|
|
2285
|
+
const key = String(target || '').replace(/\\/g, '/')
|
|
2286
|
+
if (!key) return { ok: false, message: 'target required' }
|
|
2287
|
+
const owner = fileOwner[key]
|
|
2288
|
+
if (owner && owner.childId !== childId && (now() - (owner.at || 0)) < 60000) {
|
|
2289
|
+
return { ok: false, busy: owner.childId, message: '文件 "' + key + '" 正被其他代理写入,请稍后(写锁)' }
|
|
2290
|
+
}
|
|
2291
|
+
// 确保目标父目录存在(如 Progress/<qid>/ 供方向文件写入)
|
|
2292
|
+
const pm = /^(Progress|Propos|Methods)\/([^/]+)\//.exec(key)
|
|
2293
|
+
if (pm) { const base = frameworkRoot(); await runShell('New-Item -Force -ItemType Directory -Path ' + psQuote(base + '/' + pm[1] + '/' + pm[2]) + ' | Out-Null') }
|
|
2294
|
+
fileOwner[key] = { childId: childId, sessionId: sessionId, at: now() }
|
|
2295
|
+
logActivity('write-lock', 'claim ' + key + ' by ' + childId)
|
|
2296
|
+
return { ok: true, key: key, path: frameworkRoot() + '/' + key, hint: '现在可写入 ' + frameworkRoot() + '/' + key + ';写完请 release_write' }
|
|
2297
|
+
}
|
|
2298
|
+
async function releaseWrite(target, agent) {
|
|
2299
|
+
const childId = (agent && agent.id) ? String(agent.id) : 'scheduler'
|
|
2300
|
+
const key = String(target || '').replace(/\\/g, '/')
|
|
2301
|
+
const owner = fileOwner[key]
|
|
2302
|
+
if (owner && owner.childId !== childId) return { ok: false, message: '写锁不属于此代理,无法释放' }
|
|
2303
|
+
delete fileOwner[key]
|
|
2304
|
+
logActivity('write-lock', 'release ' + key + ' by ' + childId)
|
|
2305
|
+
return { ok: true, key: key }
|
|
2306
|
+
}
|
|
2307
|
+
// 元数据同步:代理把内容写进 md 后,用极简字段让调度器更新索引/状态(content 不进 JSON)
|
|
2308
|
+
async function syncMeta(meta, agent) {
|
|
2309
|
+
if (!meta || typeof meta !== 'object') return { ok: false, message: 'meta object required' }
|
|
2310
|
+
const kind = String(meta.kind || '')
|
|
2311
|
+
if (kind === 'directions') {
|
|
2312
|
+
// explorer 写好了方向定义:更新 dirState 元数据(id/title/存活率/状态)
|
|
2313
|
+
const qid = String(meta.qid || '')
|
|
2314
|
+
if (qid && Array.isArray(meta.directions)) {
|
|
2315
|
+
// 重派生替换方向前:把旧方向的 journal 归档到日志(与旧 JSON handleExplorer 一致)
|
|
2316
|
+
const oldDirs = dirState.get(qid)
|
|
2317
|
+
if (oldDirs && oldDirs.length > 0) await archiveDirections(qid, oldDirs)
|
|
2318
|
+
const list = (meta.directions || []).map(function (d) {
|
|
2319
|
+
const old = (getDirState(qid) || []).find(function (x) { return x.id === d.id })
|
|
2320
|
+
return { id: d.id || ('d_' + shortId()), title: d.title || '', method: d.method || old?.method || '', core_assumption: d.core_assumption || old?.core_assumption || '', feasibility: clamp01(d.feasibility != null ? d.feasibility : (old ? old.survival : 0.5)), status: 'active', round: old ? old.round : 0, survival: clamp01(d.survival != null ? d.survival : (old ? old.survival : 0.5)), routes: old?.routes || [], lessons: old?.lessons || [], blockers: old?.blockers || [], lemmas: old?.lemmas || [], journal: old?.journal || [], dead_end_reason: '' }
|
|
2321
|
+
})
|
|
2322
|
+
dirState.set(qid, list)
|
|
2323
|
+
await saveDirState(); await writeJournal(qid)
|
|
2324
|
+
await consumeMethodFeedback(meta, { qid: qid })
|
|
2325
|
+
logActivity('explorer', 'problem ' + qid + ' → ' + list.length + ' directions (meta sync)')
|
|
2326
|
+
}
|
|
2327
|
+
await saveAll()
|
|
2328
|
+
return { ok: true }
|
|
2329
|
+
}
|
|
2330
|
+
if (kind === 'solver') {
|
|
2331
|
+
const qid = String(meta.qid || ''); const dirId = String(meta.dirId || '')
|
|
2332
|
+
const q = problems.get(qid)
|
|
2333
|
+
const dirs = getDirState(qid)
|
|
2334
|
+
const dir = dirs.find(function (d) { return d.id === dirId })
|
|
2335
|
+
if (q && dir) {
|
|
2336
|
+
if (typeof meta.survival === 'number') dir.survival = clamp01(meta.survival)
|
|
2337
|
+
if (meta.status) dir.status = String(meta.status)
|
|
2338
|
+
if (meta.dead_end_reason) dir.dead_end_reason = String(meta.dead_end_reason)
|
|
2339
|
+
if (meta.round) dir.round = Number(meta.round)
|
|
2340
|
+
// 引理注册(id 由代理在命题卡里自定)
|
|
2341
|
+
if (Array.isArray(meta.lemmas)) {
|
|
2342
|
+
for (const l of meta.lemmas) {
|
|
2343
|
+
if (!l || (!l.id && !l.title)) continue
|
|
2344
|
+
const pid = l.id || ('p-' + shortId())
|
|
2345
|
+
if (!propos.has(pid)) {
|
|
2346
|
+
const pn = { id: pid, 标题: l.title || pid, 状态: '未定论', 概率: clamp01(l.prob != null ? l.prob : 0.6), 优先级: l.优先级 != null ? l.优先级 : 1, 依赖: [], 价值关键性: clamp01(l['价值/关键性'] != null ? l['价值/关键性'] : 0.5), 分类: l.分类 || '未分类', 陈述: l.statement || l.title || '', proofs: [], refutes: [], 来源问题: qid, 在问题清单: false }
|
|
2347
|
+
// 引理证明文本(验证必需)由代理在 sync_meta 的 l.proof 上报(结构化,非长叙述);无则验证器只能验裸命题
|
|
2348
|
+
if (l.proof) { pn.proofs = [{ title: (l.title || pid) + '(证明)', prob: clamp01(l.prob != null ? l.prob : 0.7), status: '未定论', text: String(l.proof) }] }
|
|
2349
|
+
propos.set(pid, pn)
|
|
2350
|
+
// 若代理已直接写了该命题卡,保留其内容(不覆盖);否则写一张标准卡兜底(保证可被索引/验证)
|
|
2351
|
+
const rel = 'Propos/' + categoryOf(pn) + '/' + pid + '.md'
|
|
2352
|
+
if ((await readText(rel)) === undefined) await saveProposition(pn)
|
|
2353
|
+
}
|
|
2354
|
+
if (!(dir.lemmas || []).some(function (x) { return x.id === pid })) { dir.lemmas = dir.lemmas || []; dir.lemmas.push({ id: pid, title: l.title || pid }) }
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
// 解法上报(prob 由代理写进 Problems/<qid>.md;这里只登记)
|
|
2358
|
+
if (meta.solution_prob != null && meta.solution_text) {
|
|
2359
|
+
q.solutions = q.solutions || []
|
|
2360
|
+
const p = clamp01(meta.solution_prob)
|
|
2361
|
+
q.solutions.push({ title: '解法 ' + (q.solutions.length + 1), prob: p >= 1 ? 0.99 : (p <= 0 ? 0.01 : p), status: '未定论', text: String(meta.solution_text).slice(0, 2000) })
|
|
2362
|
+
}
|
|
2363
|
+
await saveProblem(q); await saveDirState(); await writeJournal(qid)
|
|
2364
|
+
await consumeMethodFeedback(meta, { qid: qid, dirId: dirId })
|
|
2365
|
+
logActivity('solver', qid + '/' + dirId + ' meta sync (status=' + (meta.status || '') + ', survival=' + dir.survival + ')')
|
|
2366
|
+
}
|
|
2367
|
+
await saveAll()
|
|
2368
|
+
return { ok: true }
|
|
2369
|
+
}
|
|
2370
|
+
if (kind === 'methods') {
|
|
2371
|
+
if (Array.isArray(meta.used)) for (const mu of meta.used) await consumeMethodFeedback({ methods_used: mu ? [mu] : [] }, { qid: '', dirId: '' })
|
|
2372
|
+
if (Array.isArray(meta.created)) for (const mid of meta.created) {
|
|
2373
|
+
if (!methods.has(mid)) {
|
|
2374
|
+
const mm = { id: mid, 标题: mid, 类型: '方法', 状态: '经验', 可信断言: [], 上级体系: [], 子方法: [], 相关: [], 适用场景: '', 核心内容: '', 定义与记号: '', applications: [], improvements: [], 来源: 'agent-written' }
|
|
2375
|
+
methods.set(mid, mm)
|
|
2376
|
+
// 若代理已直接写了方法卡文件则保留其内容;否则写一张标准卡兜底
|
|
2377
|
+
if ((await readText('Methods/' + mid + '.md')) === undefined) await saveMethod(mm, false)
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
await saveAll()
|
|
2381
|
+
return { ok: true }
|
|
2382
|
+
}
|
|
2383
|
+
return { ok: false, message: 'unknown meta kind: ' + kind }
|
|
2384
|
+
}
|
|
2183
2385
|
|
|
2184
2386
|
// ================= slash command /vibe =================
|
|
2185
2387
|
async function dispatchVibeCommand(cmd, args) {
|
|
@@ -2268,6 +2470,9 @@ export function apply(ctx) {
|
|
|
2268
2470
|
registerTool('vibe_math_method_add', 'Manually add a method card to Methods/ (creates Methods/<id>.md).', objParams({ id: { type: 'string' }, 标题: { type: 'string' }, 类型: { type: 'string' }, 核心内容: { type: 'string' }, 适用场景: { type: 'string' } }, ['id', '标题']), 'vibe_math_method_add')
|
|
2269
2471
|
registerTool('vibe_math_method_list', 'List methods from Methods/ (+ global VibeMath/Methods/).', objParams({}), 'vibe_math_method_list')
|
|
2270
2472
|
registerTool('vibe_math_lock_status', 'Show the project lock occupancy.', objParams({}), 'vibe_math_lock_status')
|
|
2473
|
+
registerTool('vibe_math_claim_write', 'Acquire the write lock for one target file (relative to the project root). Call before writing a Markdown file directly.', objParams({ target: { type: 'string' } }, ['target']), 'vibe_math_claim_write')
|
|
2474
|
+
registerTool('vibe_math_release_write', 'Release the write lock for one target file (relative to the project root).', objParams({ target: { type: 'string' } }, ['target']), 'vibe_math_release_write')
|
|
2475
|
+
registerTool('vibe_math_sync_meta', 'Report lightweight scheduling metadata after writing content to Markdown files.', objParams({ meta: { type: 'object' } }, ['meta']), 'vibe_math_sync_meta')
|
|
2271
2476
|
|
|
2272
2477
|
// /vibe slash command (registered once; routed per session)
|
|
2273
2478
|
ctx.effect(() => commands.register({
|