dsh-vibe-math 1.3.1 → 1.3.3
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
CHANGED
|
@@ -47,6 +47,10 @@
|
|
|
47
47
|
**一句话流水线**:起始产生 N 个**常驻子代理**(continuable,持久上下文)先各自头脑风暴、产出初始见解/方向 → 此后**所有任务安排由它们互相留言 + 集体开会自主决定**(框架只做消息总线/会议/任务板/产物沉淀,**绝不分配任务**);每个常驻把有价值的产物按**价值程度 / 动机用途计划 / 自身概率估计**沉淀到**自己**的 `Progress/<id>/`、`Propos/<id>/`、`Methods/<id>/`、`Subproblems/<id>/` 库,并**可互相阅读**;验证由它们**自行商议**发起,**仅当全体常驻一致(真或假)**才写入 `Verified/`,否则留库附概率;常驻上下文量达阈值(默认 66%)自动 `/compact`;**仅当全体一致认为原问题已解决**才停止;可随时人工干预/增开/关闭常驻,支持断点续跑。
|
|
48
48
|
|
|
49
49
|
> 说明:V4 去掉 v3 的中央规划器与确定性角色(explorer/solver/verifier/planner/method-keeper),把"研究者"本身作为主体。详见 `vibe-math-v4/实现方案.md`。
|
|
50
|
+
>
|
|
51
|
+
> 🔧 **v1.3.2 审计修复**:修复上下文压缩按占比失效(`contextPct` 单位错配,现按百分比保存)、常驻工具按"调用者身份"路由(各常驻库归属正确)、`vibe_v4_read_progress` 真正返回文本、跨进程断点续跑重建常驻、`message(all)` 广播真正投递、`addMember` 增开无 id 碰撞。详见 `vibe-math-v4/实现方案.md` §17。
|
|
52
|
+
>
|
|
53
|
+
> 🔧 **v1.3.3 审计修复**:非全票验证把平均概率写回源卡(兑现"留库附概率")、方法型验证标 `类型: 方法`(不再误标命题)、同进程 abort→resume 重建常驻、记录命题也触发自动同步会议、唤醒信号改用 `activityTimeoutMs`、`verdictMaxRounds`/`meetingKeepEvery` 可调且展示。详见 §18。
|
|
50
54
|
|
|
51
55
|
---
|
|
52
56
|
|
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 — FOUR 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), 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), and vibe-math-v4 (FOURTH-generation: persistent self-organizing resident subagents that message & meet to decide all tasks, verify only by unanimous consensus, /compact at a context threshold, and stop only when all agree the problem is solved). Installing this bundle auto-installs all four presets into the DSH preset root.",
|
|
4
|
-
"version": "1.3.
|
|
4
|
+
"version": "1.3.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "installer.js",
|
|
7
7
|
"exports": {
|
|
@@ -36,7 +36,7 @@ export function apply(ctx) {
|
|
|
36
36
|
let problemText = '', problemId = 'problem', runId = 'run-' + shortId()
|
|
37
37
|
let meetingState = null, verifyState = null, pendingVerify = null
|
|
38
38
|
let busy = new Set(), wakeKind = new Map(), currentResident = ''
|
|
39
|
-
let lastActivityAt = now(), artifactCount = 0, lastSyncMeetingAt = 0
|
|
39
|
+
let lastActivityAt = now(), artifactCount = 0, lastSyncMeetingAt = 0, persistedEpoch = ''
|
|
40
40
|
const activityLogCap = 200
|
|
41
41
|
|
|
42
42
|
// ---- utils ----
|
|
@@ -46,6 +46,9 @@ export function apply(ctx) {
|
|
|
46
46
|
function clamp01(v){ const n=Number(v); if(!Number.isFinite(n)) return 0.5; return Math.max(0,Math.min(1,n)) }
|
|
47
47
|
function fmtTime(ts){ try { return new Date(ts||now()).toISOString().replace('T',' ').slice(0,19) } catch(e){ return String(ts||'') } }
|
|
48
48
|
function cl(x){ return clamp01(Number(x)) }
|
|
49
|
+
// contextPct is a PERCENT (0-100); never clamp to 0-1 or the compactThreshold
|
|
50
|
+
// comparison (e.g. 66) becomes `1.0 >= 66` and never fires.
|
|
51
|
+
function clPct(x){ const n=Number(x); if(!Number.isFinite(n)) return 0; return Math.max(0,Math.min(100,n)) }
|
|
49
52
|
function textBlock(t){ return { type:'text', text:String(t) } }
|
|
50
53
|
function blocksToText(b){ if(!b) return ''; let out=''; for(const x of b){ if(x&&x.type==='text'&&typeof x.text==='string') out+=x.text+'\n' } return out.trim() }
|
|
51
54
|
function logActivity(event,detail){ activityLog.push({at:now(),event,detail:String(detail||'')}); if(activityLog.length>activityLogCap) activityLog.shift() }
|
|
@@ -83,10 +86,10 @@ export function apply(ctx) {
|
|
|
83
86
|
await writeJson('State/mailboxes.json', Object.fromEntries(mailboxes))
|
|
84
87
|
await writeJson('State/taskboard.json', taskboard)
|
|
85
88
|
await writeJson('State/decisions.json', decisions)
|
|
86
|
-
await writeJson('State/session.json', {running,autoDone,phase,problemId,problemText,runId,meetings,reports,lastActivityAt,activityLog})
|
|
89
|
+
await writeJson('State/session.json', {running,autoDone,phase,problemId,problemText,runId,meetings,reports,lastActivityAt,activityLog,processEpoch,artifactCount})
|
|
87
90
|
}
|
|
88
91
|
async function loadAll(){
|
|
89
|
-
const s=await readJson('State/session.json'); if(s){ running=!!s.running; autoDone=!!s.autoDone; phase=s.phase||'idle'; problemId=s.problemId||problemId; problemText=s.problemText||problemText; runId=s.runId||runId; meetings=s.meetings||[]; reports=s.reports||[]; lastActivityAt=s.lastActivityAt||now(); activityLog=s.activityLog||activityLog }
|
|
92
|
+
const s=await readJson('State/session.json'); if(s){ running=!!s.running; autoDone=!!s.autoDone; phase=s.phase||'idle'; problemId=s.problemId||problemId; problemText=s.problemText||problemText; runId=s.runId||runId; meetings=s.meetings||[]; reports=s.reports||[]; lastActivityAt=s.lastActivityAt||now(); activityLog=s.activityLog||activityLog; persistedEpoch=s.processEpoch||''; artifactCount=s.artifactCount||0 }
|
|
90
93
|
const rm=await readJson('State/residents.json'); if(rm&&typeof rm==='object') residents=new Map(Object.entries(rm))
|
|
91
94
|
const mb=await readJson('State/mailboxes.json'); if(mb&&typeof mb==='object') mailboxes=new Map(Object.entries(mb))
|
|
92
95
|
const tb=await readJson('State/taskboard.json'); if(Array.isArray(tb)) taskboard=tb
|
|
@@ -143,9 +146,10 @@ export function apply(ctx) {
|
|
|
143
146
|
}
|
|
144
147
|
|
|
145
148
|
// ---- resident lifecycle ----
|
|
146
|
-
|
|
149
|
+
let residentSeq = 0
|
|
150
|
+
function newResident(dir){ const rId='r-'+(++residentSeq); return {rId,childId:'',direction:dir||'',status:'brainstorm',rounds:0,roundsSinceCompact:0,lastActiveAt:now(),insight:'',contextPct:0,contextSeed:'',needCompact:false} }
|
|
147
151
|
async function spawnResident(r){
|
|
148
|
-
const started=await subagents.startContinuable({provider:pickProvider(),label:r.rId,request:{prompt:[textBlock(brainstormPrompt(r))],parent:rootAgent,agentOptions:{}},signal:makeSignal(60000)})
|
|
152
|
+
const started=await subagents.startContinuable({provider:pickProvider(),label:r.rId,request:{prompt:[textBlock(brainstormPrompt(r))],parent:rootAgent,agentOptions:{}},signal:makeSignal(params.activityTimeoutMs||60000)})
|
|
149
153
|
r.childId=started.childId; r.status='brainstorm'; r.lastActiveAt=now()
|
|
150
154
|
childOwner.set(started.childId,sessionId); busy.add(r.rId); wakeKind.set(r.rId,'normal'); currentResident=r.rId
|
|
151
155
|
residents.set(r.rId,r); await saveAll(); logActivity('spawn',r.rId+' ('+(r.direction||'brainstorm')+')')
|
|
@@ -164,19 +168,24 @@ export function apply(ctx) {
|
|
|
164
168
|
'Set "contextPct": 15 (your post-compact usage) and "compacted": true in the reply so the framework records the condensed seed.]\n\n' + promptText
|
|
165
169
|
r.needCompact = true
|
|
166
170
|
}
|
|
167
|
-
try { await subagents.followup(rootAgent,r.childId,[textBlock(prompt)],{source:{kind:'user'},signal:makeSignal(60000)}); return true }
|
|
171
|
+
try { await subagents.followup(rootAgent,r.childId,[textBlock(prompt)],{source:{kind:'user'},signal:makeSignal(params.activityTimeoutMs||60000)}); return true }
|
|
168
172
|
catch(e){ console.error('vibe-v4 wake '+r.rId+' failed: '+String((e&&e.message)||e)); busy.delete(r.rId); return false }
|
|
169
173
|
}
|
|
170
174
|
function byChild(childId){ for(const [,r] of residents){ if(r.childId===childId) return r } return undefined }
|
|
171
175
|
|
|
172
176
|
// ---- artifact writers (resident-facing) ----
|
|
173
177
|
async function publishProgress(rId,content){ const rel='Progress/'+rId+'/progress.md'; const prev=(await readText(rel))||''; await writeText(rel, prev+'\n### '+fmtTime()+'|'+rId+'\n'+String(content||'')+'\n'); return {ok:true} }
|
|
174
|
-
async function recordProposition(rId,o){ const id=o.id||('p-'+shortId()); const lines=['# 命题|'+(o.title||id),'- 标题: '+(o.title||id),'- ID: '+id,'- 类型: 命题','- 状态: 未定论','- 概率: '+cl(o.prob!=null?o.prob:0.5),'- 价值程度: '+cl(o.value!=null?o.value:0.5),'- 动机用途计划: '+(o.motivation||''),'- 依赖: []','','## 陈述',String(o.statement||''),'','## 证明尝试','','## 证伪尝试','']; await writeText('Propos/'+rId+'/'+id+'.md',lines.join('\n')); logActivity('record',rId+' 命题 '+id); return {ok:true,id,file:'Propos/'+rId+'/'+id+'.md'} }
|
|
178
|
+
async function recordProposition(rId,o){ const id=o.id||('p-'+shortId()); const lines=['# 命题|'+(o.title||id),'- 标题: '+(o.title||id),'- ID: '+id,'- 类型: 命题','- 状态: 未定论','- 概率: '+cl(o.prob!=null?o.prob:0.5),'- 价值程度: '+cl(o.value!=null?o.value:0.5),'- 动机用途计划: '+(o.motivation||''),'- 依赖: []','','## 陈述',String(o.statement||''),'','## 证明尝试','','## 证伪尝试','']; await writeText('Propos/'+rId+'/'+id+'.md',lines.join('\n')); logActivity('record',rId+' 命题 '+id); bumpArtifacts(); return {ok:true,id,file:'Propos/'+rId+'/'+id+'.md'} }
|
|
175
179
|
async function recordMethod(rId,o){ const id=o.id||('m-'+shortId()); const lines=['# 方法|'+(o.title||id),'- 标题: '+(o.title||id),'- ID: '+id,'- 类型: '+(o.type||'方法'),'- 状态: 经验','- 可信断言: []','- 价值程度: '+cl(o.value!=null?o.value:0.5),'- 动机用途计划: '+(o.motivation||''),'','## 核心内容',String(o.content||''),'','## 定义与记号',String(o.notation||''),'','## 应用记录','## 改进历史','']; await writeText('Methods/'+rId+'/'+id+'.md',lines.join('\n')); logActivity('record',rId+' 方法 '+id); bumpArtifacts(); return {ok:true,id,file:'Methods/'+rId+'/'+id+'.md'} }
|
|
176
180
|
async function recordSubproblem(rId,o){ const id=o.id||('s-'+shortId()); const lines=['# 子问题|'+(o.title||id),'- 标题: '+(o.title||id),'- ID: '+id,'- 状态: 求解中','- 价值程度: '+cl(o.value!=null?o.value:0.5),'- 动机用途计划: '+(o.motivation||''),'- 依赖: []','','## 陈述',String(o.statement||''),'','## 进度','']; await writeText('Subproblems/'+rId+'/'+id+'.md',lines.join('\n')); logActivity('record',rId+' 子问题 '+id); bumpArtifacts(); return {ok:true,id,file:'Subproblems/'+rId+'/'+id+'.md'} }
|
|
177
181
|
// auto-sync meeting: every meetingKeepEvery new artifacts, convene a general coordination meeting
|
|
178
182
|
function bumpArtifacts(){ artifactCount+=1; if(!meetingState && !verifyState && Number(params.meetingKeepEvery)>0 && artifactCount % Number(params.meetingKeepEvery)===0){ startMeeting('定期同步:分工/进展/是否需要验证','general',null).catch(()=>{}) } }
|
|
179
183
|
function listResidents(){ return Array.from(residents.values()).map(r=>({id:r.rId,direction:r.direction,status:r.status,rounds:r.rounds,contextPct:r.contextPct,insight:r.insight?r.insight.slice(0,80):''})) }
|
|
184
|
+
// identify WHICH resident is calling a resident-facing tool: match the caller's
|
|
185
|
+
// subagent id to a resident's childId. Fall back to the last-woken resident when
|
|
186
|
+
// the caller is the host/assistant (or an unknown agent). This makes per-resident
|
|
187
|
+
// libraries correct under concurrency (e.g. all brainstorm residents in flight).
|
|
188
|
+
function residentOfAgent(agent){ try { const id=agent&&agent.id?String(agent.id):''; if(!id) return ''; for(const [,r] of residents){ if(r.childId===id) return r.rId } } catch(e){} return '' }
|
|
180
189
|
|
|
181
190
|
// ---- task board (residents propose / claim / complete; framework wakes the claimer) ----
|
|
182
191
|
async function writeTaskboard(){ const lines=['# 任务板','']; for(const t of taskboard){ lines.push('- ['+t.status+'] '+t.title+(t.claimer?('(认领:'+t.claimer+')'):'')+(t.proposer?('(提议:'+t.proposer+')'):'')+(t.description?(':'+t.description):'')) } await writeText('Shared/taskboard.md',lines.join('\n')) }
|
|
@@ -188,7 +197,7 @@ export function apply(ctx) {
|
|
|
188
197
|
async function taskDone(id,claimer){ const t=taskboard.find(x=>x.id===id); if(!t) return {ok:false}; t.status='done'; t.doneBy=claimer; await saveTaskboard(); await writeTaskboard(); logActivity('task','done '+id); return {ok:true} }
|
|
189
198
|
async function saveTaskboard(){ await writeJson('State/taskboard.json',taskboard); await writeTaskboard() }
|
|
190
199
|
function listTasks(){ return taskboard.filter(t=>t.status!=='done') }
|
|
191
|
-
async function reportContext(rId,pct){ const r=residents.get(rId); if(r){ r.contextPct=
|
|
200
|
+
async function reportContext(rId,pct){ const r=residents.get(rId); if(r){ r.contextPct=clPct(pct); if(Number(pct)<30) r.needCompact=false; } return {ok:true} }
|
|
192
201
|
|
|
193
202
|
// ---- messaging ----
|
|
194
203
|
async function postMessage(from,to,content){
|
|
@@ -200,6 +209,11 @@ export function apply(ctx) {
|
|
|
200
209
|
}
|
|
201
210
|
const mb=mailboxes.get(to)||[]; mb.push({from,at:now(),content}); mailboxes.set(to,mb); await saveAll(); logActivity('message',from+'→'+to+' (queued)'); return {ok:true}
|
|
202
211
|
}
|
|
212
|
+
async function broadcast(content){
|
|
213
|
+
let n=0
|
|
214
|
+
for(const [,r] of residents){ const res=await postMessage('facilitator',r.rId,content); if(res&&res.ok) n++ }
|
|
215
|
+
logActivity('broadcast','to '+n+' resident(s)'); await saveAll(); return {ok:true,message:'broadcast to '+n+' resident(s)'}
|
|
216
|
+
}
|
|
203
217
|
|
|
204
218
|
// ---- meeting ----
|
|
205
219
|
async function startMeeting(agenda,type,targetId){
|
|
@@ -258,7 +272,7 @@ export function apply(ctx) {
|
|
|
258
272
|
if(allTrue||allFalse){ await closeVerify(vs,allTrue); return }
|
|
259
273
|
if(vs.round+1<params.verdictMaxRounds){ vs.stage='debate'; vs.round+=1; vs.asked=[]; logActivity('verify',vs.targetId+' round '+vs.round+' → debate'); await saveAll(); await scheduleNext(); return }
|
|
260
274
|
const avg=vals.length? vals.reduce((a,x)=>a+(x.verdict==='TRUE'?x.confidence:x.verdict==='FALSE'?1-x.confidence:0.5),0)/vals.length : 0.5
|
|
261
|
-
await writeDebateDoc(vs,false,avg); logActivity('verify',vs.targetId+' NOT unanimous → kept unverified (avg '+avg.toFixed(2)+')')
|
|
275
|
+
await writeDebateDoc(vs,false,avg); await rewriteSourceProb(vs.targetId, avg); logActivity('verify',vs.targetId+' NOT unanimous → kept unverified (avg '+avg.toFixed(2)+')')
|
|
262
276
|
verifyState=null; wakeKind.clear(); await saveAll(); await scheduleNext()
|
|
263
277
|
}
|
|
264
278
|
async function closeVerify(vs,isTrue){
|
|
@@ -275,12 +289,13 @@ export function apply(ctx) {
|
|
|
275
289
|
await writeText('Shared/debates/'+vs.targetId+'.md', lines.join('\n'))
|
|
276
290
|
}
|
|
277
291
|
async function writeVerifiedCard(vs,isTrue){
|
|
278
|
-
const
|
|
279
|
-
const
|
|
292
|
+
const isSub=vs.targetType==='subproblem'
|
|
293
|
+
const dir= isSub?'问题':'命题'
|
|
294
|
+
const type= isSub?'问题': vs.targetType==='method'?'方法':'命题'
|
|
295
|
+
const text='# 已验证|'+vs.targetId+'\n- ID: '+vs.targetId+'\n- 类型: '+type+'\n- 结论: '+(isTrue?'真':'假')+'\n- 概率: '+(isTrue?1:0)+'\n- 来源: 全体常驻一致\n## 陈述\n参见来源卡。\n'
|
|
280
296
|
await writeText('Verified/'+dir+'/'+vs.targetId+'.md', text)
|
|
281
297
|
}
|
|
282
|
-
async function
|
|
283
|
-
// find & update the source card status/prob; best effort across per-resident libs
|
|
298
|
+
async function findSourceRel(target){
|
|
284
299
|
let rel=null
|
|
285
300
|
for(const [rid] of residents){
|
|
286
301
|
for(const base of ['Propos','Methods','Subproblems']){
|
|
@@ -288,7 +303,19 @@ export function apply(ctx) {
|
|
|
288
303
|
}
|
|
289
304
|
if(rel) break
|
|
290
305
|
}
|
|
291
|
-
|
|
306
|
+
return rel || ('Propos/'+target+'.md')
|
|
307
|
+
}
|
|
308
|
+
// non-unanimous verification: keep the object in its library but write back the
|
|
309
|
+
// average probability (design §8: "留库附概率"), so the card reflects the consensus estimate.
|
|
310
|
+
async function rewriteSourceProb(target,prob){
|
|
311
|
+
const rel=await findSourceRel(target)
|
|
312
|
+
let text=(await readText(rel))||''
|
|
313
|
+
text=text.replace(/(^|\n)- 概率:.*/m,'$1- 概率: '+Number(prob).toFixed(2))
|
|
314
|
+
await writeText(rel,text)
|
|
315
|
+
}
|
|
316
|
+
async function rewriteSource(target,isTrue){
|
|
317
|
+
// find & update the source card status/prob; best effort across per-resident libs
|
|
318
|
+
const rel=await findSourceRel(target)
|
|
292
319
|
let text=(await readText(rel))||''
|
|
293
320
|
text=text.replace(/(^|\n)- 状态:.*/m,'$1'+(isTrue?'- 状态: 已验证·真':'- 状态: 已验证·假'))
|
|
294
321
|
.replace(/(^|\n)- 概率:.*/m,'$1'+(isTrue?'- 概率: 1':'- 概率: 0'))
|
|
@@ -350,7 +377,7 @@ export function apply(ctx) {
|
|
|
350
377
|
if(r.status==='brainstorm'){ r.insight=parsed.summary||output; r.status='active' }
|
|
351
378
|
if(typeof parsed.solved==='boolean') reports.push({rId:r.rId,solved:parsed.solved,summary:parsed.summary||'',at:now()})
|
|
352
379
|
// context / compact: record the condensed seed + post-compact usage, clear the flag
|
|
353
|
-
if(typeof parsed.contextPct==='number'){ r.contextPct=
|
|
380
|
+
if(typeof parsed.contextPct==='number'){ r.contextPct=clPct(parsed.contextPct) }
|
|
354
381
|
if(parsed.compacted===true || (r.needCompact && parsed.summary)){ r.contextSeed=String(parsed.summary||''); r.contextPct=Math.min(r.contextPct||15,25); r.roundsSinceCompact=0; r.needCompact=false; logActivity('compact',r.rId+' consolidated context') }
|
|
355
382
|
if(parsed.propose_verify) pendingVerify={targetId:parsed.propose_verify,targetType:guessTargetType(parsed.propose_verify),proposer:r.rId,at:now()}
|
|
356
383
|
// task actions via reply (a resident may propose or claim a task in its round)
|
|
@@ -371,21 +398,27 @@ export function apply(ctx) {
|
|
|
371
398
|
if(residentCount) params.residentCount=Number(residentCount)||4
|
|
372
399
|
running=true; autoDone=false; phase='brainstorm'
|
|
373
400
|
await writeText('Problems/'+problemId+'.md','# 问题|'+problemId+'\n- ID: '+problemId+'\n- 类型: 问题\n- 状态: 求解中\n- 优先级: 1\n- 依赖: []\n\n## 陈述\n'+problemText+'\n')
|
|
374
|
-
residents=new Map(); mailboxes=new Map(); taskboard=[]; decisions=[]; meetings=[]; reports=[]; verifyState=null; meetingState=null; pendingVerify=null
|
|
401
|
+
residents=new Map(); mailboxes=new Map(); taskboard=[]; decisions=[]; meetings=[]; reports=[]; verifyState=null; meetingState=null; pendingVerify=null; residentSeq=0; artifactCount=0
|
|
375
402
|
const dirs=Array.isArray(seedDirections)?seedDirections.slice(0,params.residentCount):[]
|
|
376
403
|
for(let i=0;i<params.residentCount;i++){ const r=newResident(dirs[i]||''); await spawnResident(r) }
|
|
377
404
|
await saveAll(); return {ok:true,message:'v4 started: '+params.residentCount+' resident(s) brainstorming',project:currentProject}
|
|
378
405
|
}
|
|
379
406
|
async function resume(){
|
|
380
407
|
currentProject=await readCurrentProject(); await ensureDirs(); await loadAll()
|
|
381
|
-
if(phase==='idle' && !running) return {ok:false,message:'nothing to resume'}
|
|
408
|
+
if(phase==='idle' && !running && residents.size===0) return {ok:false,message:'nothing to resume'}
|
|
409
|
+
// If the persisted State came from a DIFFERENT process (crash/restart), the saved
|
|
410
|
+
// childIds are stale; clear them so residents re-spawn (their libraries persist on
|
|
411
|
+
// disk and re-seed the resumed run). Same-process pause→resume keeps continuable ids.
|
|
412
|
+
const crossProcess = persistedEpoch !== processEpoch
|
|
413
|
+
if(crossProcess){ for(const [,r] of residents){ r.childId=''; r.status='brainstorm'; r.roundsSinceCompact=0 } }
|
|
382
414
|
for(const [,r] of residents){ if(!r.childId){ await spawnResident(r) } }
|
|
383
415
|
if(!running){ running=true; autoDone=false; if(phase==='idle') phase='active' }
|
|
384
|
-
|
|
416
|
+
if(crossProcess && phase==='active') phase='brainstorm' // let re-spawned residents re-bootstrap together
|
|
417
|
+
logActivity('resume','restarted'+(crossProcess?' (cross-process: re-spawned)':'')); await saveAll(); await scheduleNext(); return {ok:true,message:'resumed',project:currentProject}
|
|
385
418
|
}
|
|
386
419
|
function status(){ return { ok:true, running, phase, autoDone, project:currentProject, residentCount:residents.size,
|
|
387
420
|
residents:listResidents(), busy:[...busy], taskboard:taskboard.length,
|
|
388
|
-
params:['residentCount','compactAfterRounds','compactThreshold','maxParallel','activityTimeoutMs'].map(k=>k+'='+params[k]).join(', ') } }
|
|
421
|
+
params:['residentCount','compactAfterRounds','compactThreshold','maxParallel','activityTimeoutMs','meetingKeepEvery','verdictMaxRounds'].map(k=>k+'='+params[k]).join(', ') } }
|
|
389
422
|
function report(){ return { ok:true, running, phase, autoDone, project:currentProject, problem:problemText,
|
|
390
423
|
residents:listResidents(), taskboard:taskboard.filter(t=>t.status!=='done'),
|
|
391
424
|
verify: verifyState?{target:verifyState.targetId,stage:verifyState.stage}:null, meetings:meetings.length,
|
|
@@ -393,14 +426,15 @@ export function apply(ctx) {
|
|
|
393
426
|
async function addMember(direction){ const r=newResident(direction||''); await spawnResident(r); return {ok:true,id:r.rId,direction:r.direction} }
|
|
394
427
|
async function removeMember(id){ const r=residents.get(id); if(!r) return {ok:false}; if(r.childId){ try{ subagents.interrupt(r.childId,{kind:'ancestor',agent:rootAgent}) }catch(e){} } residents.delete(id); busy.delete(id); mailboxes.delete(id); await saveAll(); return {ok:true} }
|
|
395
428
|
function setParams(upd){ for(const k of Object.keys(upd||{})){ if(k in params) params[k]=upd[k] } return {ok:true} }
|
|
396
|
-
function initAbort(){ running=false; phase='idle'; autoDone=false; for(const [,r] of residents){ if(r.childId){ try{ subagents.interrupt(r.childId,{kind:'ancestor',agent:rootAgent}) }catch(e){} } }; return {ok:true,message:'aborted'} }
|
|
429
|
+
async function initAbort(){ running=false; phase='idle'; autoDone=false; for(const [,r] of residents){ if(r.childId){ try{ subagents.interrupt(r.childId,{kind:'ancestor',agent:rootAgent}) }catch(e){} } r.childId=''; r.lastActiveAt=0; r.roundsSinceCompact=0 } await saveAll(); return {ok:true,message:'aborted'} }
|
|
397
430
|
function setPause(){ running=false; return {ok:true,message:'paused'} }
|
|
398
431
|
|
|
399
432
|
return {
|
|
400
433
|
sessionId, running:()=>running, autoDone:()=>autoDone, phase:()=>phase,
|
|
401
434
|
onResidentEnd, start, resume, status, report, addMember, removeMember, setParams,
|
|
402
|
-
setPause, initAbort, postMessage, startMeeting, saveAll,
|
|
435
|
+
setPause, initAbort, postMessage, startMeeting, saveAll, broadcast,
|
|
403
436
|
currentResident:()=>currentResident,
|
|
437
|
+
residentIdOf:(agent)=>{ const m=residentOfAgent(agent); return m||currentResident },
|
|
404
438
|
useResident:(id)=>{ currentResident=id },
|
|
405
439
|
publishProgress, recordProposition, recordMethod, recordSubproblem, listResidents, reportContext,
|
|
406
440
|
proposeTask, claimTask, taskDone, listTasks,
|
|
@@ -417,7 +451,7 @@ export function apply(ctx) {
|
|
|
417
451
|
tools.register({ name, description, parameters,
|
|
418
452
|
output:{ schema:{ type:'string' }, render:(_a,v)=>[{type:'text',text:String(v)}] },
|
|
419
453
|
execute: async (args, exec)=>{
|
|
420
|
-
try { const s=getSession(exec&&exec.agent); if(!s) return JSON.stringify({ok:false,error:'no session'}); return JSON.stringify(await fn(s,args||{})) }
|
|
454
|
+
try { const s=getSession(exec&&exec.agent); if(!s) return JSON.stringify({ok:false,error:'no session'}); return JSON.stringify(await fn(s,args||{},exec&&exec.agent)) }
|
|
421
455
|
catch(e){ return JSON.stringify({ok:false,error:String((e&&e.message)||e)}) }
|
|
422
456
|
} })
|
|
423
457
|
}
|
|
@@ -428,27 +462,28 @@ export function apply(ctx) {
|
|
|
428
462
|
registerTool('vibe_v4_abort','Abort V4 and interrupt residents.',objParams({}),(s)=>s.initAbort())
|
|
429
463
|
registerTool('vibe_v4_status','Show V4 status.',objParams({}),(s)=>s.status())
|
|
430
464
|
registerTool('vibe_v4_report','Return the V4 progress report.',objParams({}),(s)=>s.report())
|
|
431
|
-
registerTool('vibe_v4_message','Inject a message to a resident (or all).',objParams({to:{type:'string'},content:{type:'string'}},['to','content']),(s,a)=>{ const to=a.to||'all'; if(to==='all')
|
|
465
|
+
registerTool('vibe_v4_message','Inject a message to a resident (or all).',objParams({to:{type:'string'},content:{type:'string'}},['to','content']),(s,a)=>{ const to=a.to||'all'; if(to==='all') return s.broadcast(a.content); return s.postMessage('facilitator',to,a.content) })
|
|
432
466
|
registerTool('vibe_v4_meeting','Start a meeting (coordinate / allocate / propose verification).',objParams({agenda:{type:'string'}},['agenda']),(s,a)=>s.startMeeting(a.agenda))
|
|
433
467
|
registerTool('vibe_v4_list_members','List residents.',objParams({}),(s)=>({ok:true,residents:s.listResidents()}))
|
|
434
468
|
registerTool('vibe_v4_add_member','Add a resident.',objParams({direction:{type:'string'}}),(s,a)=>s.addMember(a.direction))
|
|
435
469
|
registerTool('vibe_v4_remove_member','Close a resident.',objParams({id:{type:'string'}},['id']),(s,a)=>s.removeMember(a.id))
|
|
436
|
-
registerTool('vibe_v4_set','Set V4 parameters.',objParams({residentCount:{type:'integer'},compactAfterRounds:{type:'integer'},compactThreshold:{type:'integer'},meetingKeepEvery:{type:'integer'},maxParallel:{type:'integer'},activityTimeoutMs:{type:'integer'}}),(s,a)=>{ s.setParams(a); return {ok:true} })
|
|
437
|
-
// resident-facing tools: route to the
|
|
438
|
-
|
|
439
|
-
registerTool('
|
|
440
|
-
registerTool('
|
|
441
|
-
registerTool('
|
|
442
|
-
registerTool('
|
|
443
|
-
registerTool('
|
|
470
|
+
registerTool('vibe_v4_set','Set V4 parameters.',objParams({residentCount:{type:'integer'},compactAfterRounds:{type:'integer'},compactThreshold:{type:'integer'},meetingKeepEvery:{type:'integer'},maxParallel:{type:'integer'},activityTimeoutMs:{type:'integer'},verdictMaxRounds:{type:'integer'}}),(s,a)=>{ s.setParams(a); return {ok:true} })
|
|
471
|
+
// resident-facing tools: route to the CALLING resident (exec.agent.id === childId);
|
|
472
|
+
// fall back to the last-woken resident when called by the host/assistant.
|
|
473
|
+
registerTool('vibe_v4_send_message','(resident) Send a message to another resident.',objParams({to:{type:'string'},content:{type:'string'}},['to','content']),(s,a,x)=>s.postMessage(s.residentIdOf(x),a.to,a.content))
|
|
474
|
+
registerTool('vibe_v4_publish_progress','(resident) Append to your own progress markdown.',objParams({content:{type:'string'}},['content']),(s,a,x)=>s.publishProgress(s.residentIdOf(x),a.content))
|
|
475
|
+
registerTool('vibe_v4_record_proposition','(resident) Record a proposition to your library.',objParams({id:{type:'string'},title:{type:'string'},statement:{type:'string'},prob:{type:'number'},value:{type:'number'},motivation:{type:'string'}}),(s,a,x)=>s.recordProposition(s.residentIdOf(x),a))
|
|
476
|
+
registerTool('vibe_v4_record_method','(resident) Record a method/theory to your library.',objParams({id:{type:'string'},title:{type:'string'},type:{type:'string'},content:{type:'string'},notation:{type:'string'},value:{type:'number'},motivation:{type:'string'}}),(s,a,x)=>s.recordMethod(s.residentIdOf(x),a))
|
|
477
|
+
registerTool('vibe_v4_record_subproblem','(resident) Record a sub-problem to your library.',objParams({id:{type:'string'},title:{type:'string'},statement:{type:'string'},value:{type:'number'},motivation:{type:'string'}}),(s,a,x)=>s.recordSubproblem(s.residentIdOf(x),a))
|
|
478
|
+
registerTool('vibe_v4_read_progress','(resident) Read another resident\'s progress (read-only).',objParams({id:{type:'string'}},['id']),async (s,a)=>{ const rp=await s.readProgress(a.id); return {ok:true,text:(rp&&rp.text)||''} })
|
|
444
479
|
registerTool('vibe_v4_list_residents','(resident) List fellow residents.',objParams({}),(s)=>({ok:true,residents:s.listResidents()}))
|
|
445
480
|
// task board (residents; board is the residents' own allocation mechanism)
|
|
446
|
-
registerTool('vibe_v4_propose_task','(resident) Propose a task to the shared task board.',objParams({title:{type:'string'},description:{type:'string'}},['title']),(s,a)=>s.proposeTask(a.title,a.description,s.
|
|
447
|
-
registerTool('vibe_v4_claim_task','(resident) Claim an open task from the board (framework then wakes you to work it).',objParams({id:{type:'string'}},['id']),(s,a)=>s.claimTask(a.id,s.
|
|
448
|
-
registerTool('vibe_v4_task_done','(resident) Mark a claimed task done.',objParams({id:{type:'string'},claimer:{type:'string'}},['id']),(s,a)=>s.taskDone(a.id,a.claimer||s.
|
|
481
|
+
registerTool('vibe_v4_propose_task','(resident) Propose a task to the shared task board.',objParams({title:{type:'string'},description:{type:'string'}},['title']),(s,a,x)=>s.proposeTask(a.title,a.description,s.residentIdOf(x)))
|
|
482
|
+
registerTool('vibe_v4_claim_task','(resident) Claim an open task from the board (framework then wakes you to work it).',objParams({id:{type:'string'}},['id']),(s,a,x)=>s.claimTask(a.id,s.residentIdOf(x)))
|
|
483
|
+
registerTool('vibe_v4_task_done','(resident) Mark a claimed task done.',objParams({id:{type:'string'},claimer:{type:'string'}},['id']),(s,a,x)=>s.taskDone(a.id,a.claimer||s.residentIdOf(x)))
|
|
449
484
|
registerTool('vibe_v4_list_tasks','(resident) List open tasks.',objParams({}),(s)=>({ok:true,tasks:s.listTasks()}))
|
|
450
485
|
// context / compact (resident reports its context usage so the framework can /compact-equivalent)
|
|
451
|
-
registerTool('vibe_v4_report_context','(resident) Report your context usage %; the framework compacts (self-summary) when it reaches compactThreshold.',objParams({pct:{type:'number'}},['pct']),(s,a)=>s.reportContext(s.
|
|
486
|
+
registerTool('vibe_v4_report_context','(resident) Report your context usage %; the framework compacts (self-summary) when it reaches compactThreshold.',objParams({pct:{type:'number'}},['pct']),(s,a,x)=>s.reportContext(s.residentIdOf(x),a.pct))
|
|
452
487
|
registerTool('vibe_v4_claim_write','Reserved: shared-file write lock (framework-managed).',objParams({target:{type:'string'}},['target']),(s,a)=>({ok:true,key:a.target}))
|
|
453
488
|
registerTool('vibe_v4_release_write','Reserved: shared-file write lock release.',objParams({target:{type:'string'}},['target']),(s,a)=>({ok:true,key:a.target}))
|
|
454
489
|
|
|
@@ -462,7 +497,7 @@ export function apply(ctx) {
|
|
|
462
497
|
if(cmd==='start') r=await s.start({problem:rest.join(' ')})
|
|
463
498
|
else if(cmd==='resume') r=await s.resume()
|
|
464
499
|
else if(cmd==='pause') r=s.setPause()
|
|
465
|
-
else if(cmd==='abort') r=s.initAbort()
|
|
500
|
+
else if(cmd==='abort') r=await s.initAbort()
|
|
466
501
|
else if(cmd==='status') r=s.status()
|
|
467
502
|
else if(cmd==='report') r=s.report()
|
|
468
503
|
else if(cmd==='meeting') r=await s.startMeeting(rest.join(' '))
|
|
@@ -339,3 +339,39 @@ VibeMath/Projects/<project>/
|
|
|
339
339
|
- **自动同步会议**:每积累 `meetingKeepEvery`(默认 5) 个新产物,框架自动发起"分工/进展/是否需要验证"同步会议。
|
|
340
340
|
- 会议输入可含 `propose_task/claim_task/propose_verify/voteSolved`,结束统一落任务板、触发验证、记停止表决;仍"全体一致 voteSolved=true 才停止"。
|
|
341
341
|
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## 17. 深度审计修复(v1.3.2,自驱动 20/20)
|
|
345
|
+
|
|
346
|
+
一次对 v4 的全面审计发现并修复了以下真实缺陷(`vibe-math-v4.js`):
|
|
347
|
+
|
|
348
|
+
| 缺陷 | 说明 | 修复 |
|
|
349
|
+
|---|---|---|
|
|
350
|
+
| **上下文压缩按占比失效** | `contextPct` 被 `cl()` 压成 `[0,1]`,与 `compactThreshold`(66 百分比)比较变成 `1.0>=66` 恒假,导致"达到占比自动 `/compact`"从不触发;压缩后复位也被同样钳死。 | `contextPct` 按 **0–100 百分比**保存(新增 `clPct`),比较与复位随之修正。 |
|
|
351
|
+
| **常驻工具按全局槽路由** | 所有常驻工具用共享的 `currentResident`(一个可变全局),并发下(尤其 brainstorm 阶段 N 个常驻同时在途)所有产物/留言都归到"最后一个被唤醒者",破坏每常驻独立库。 | 工具 handler 接收 `exec.agent`,按 `childId === agent.id` 解析"调用者常驻"(`residentIdOf`);未知调用者再回落 `currentResident`。 |
|
|
352
|
+
| **`vibe_v4_read_progress` 失效** | handler 返回 `text: s.readProgress(...)`(一个未 await 的 Promise),`JSON.stringify` 后变 `{}`,常驻读不到他人进展。 | 改为 `await` 并返回 `{ok,text:<string>}`。 |
|
|
353
|
+
| **跨进程断点不重建常驻** | `resume()` 只在 `childId` 为空时 re-spawn;崩溃/重启后持久化的 `childId` 是陈旧值,导致不重建且对死链 followup;且 resume 从不 `scheduleNext()`。 | `session.json` 持久化 `processEpoch`;`resume()` 检测跨进程(epoch 不同)→ 清空陈旧 `childId` 强制 re-spawn,且末尾 `scheduleNext()` 重启调度。 |
|
|
354
|
+
| **`message(all)` 广播是空壳** | `to==='all'` 只返回一句 `broadcast: ...` 但什么都不投递。 | 新增 `broadcast()`:对每个常驻 `postMessage`(空闲唤醒/忙则入信箱),返回"投递到 N 个常驻"。 |
|
|
355
|
+
| **`addMember` 出现 id 碰撞** | `newResident` 用 `'r-'+(residents.size+1)`;移除某常驻后再增开会用与现存常驻重复的 id。 | 改用会话级单调 `residentSeq`(`start()` 时清零),增开永不复用旧 id。 |
|
|
356
|
+
|
|
357
|
+
> **未改(保留为已知边界)**:`maxParallel`/`activityTimeoutMs` 目前为声明参数但未实际限流/门控(保持"常驻持续推进"的收敛行为,未做心跳超时门控以免阻塞自组织推进);`claim_write`/`release_write` 仍为占位(常驻专属目录内天然无写冲突,共享文件由框架独占写);真实 DSH `/compact` API 仍为 TODO(等效层已修复单位错配后可正常触发)。
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## 18. 第二轮深度审计修复(v1.3.3,自驱动 21/21 + 独立修复测试 8/8)
|
|
362
|
+
|
|
363
|
+
针对 v1.3.2 之后仍存在的缺陷做第二轮审计并修复:
|
|
364
|
+
|
|
365
|
+
| 缺陷 | 说明 | 修复 |
|
|
366
|
+
|---|---|---|
|
|
367
|
+
| **非全票验证不写回平均概率** | `finalizeVerify` 算出 `avg` 只写进辩论录,源卡 `- 概率:` 保持原值,未兑现设计 §8"留库附概率"。 | 新增 `findSourceRel`/`rewriteSourceProb`,非全票时把 `avg` 写回源卡 `- 概率:`(状态仍 `未定论`)。 |
|
|
368
|
+
| **方法型验证被误标为"命题"** | `writeVerifiedCard` 把方法目标写进 `Verified/命题/` 且类型=命题。 | 按 `targetType` 标 `类型: 方法`(子问题→问题,方法→方法,其余→命题),来源卡标 `已验证·真/假`。 |
|
|
369
|
+
| **同进程 abort→resume 不重建常驻** | `processEpoch` 进程级,同进程 abort(interrupt 杀掉常驻)后 resume 判非跨进程→保留死 childId;且 `phase=idle && !running` 时 resume 直接拒绝。 | `initAbort` 清空 `childId` 并 `saveAll()`;`resume` 守卫改为 `phase==='idle' && !running && residents.size===0`(有常驻即可 重建)。 |
|
|
370
|
+
| **自动同步会议计数不一致** | `bumpArtifacts` 只在记录方法/子问题时调用,`recordProposition`/`publish_progress` 不计;`artifactCount` 未持久化。 | `recordProposition` 也 `bumpArtifacts()`;`artifactCount` 持久化到 `session.json` 并在 `start()` 时清零。 |
|
|
371
|
+
| **唤醒信号硬编码 60s** | `spawnResident`/`wakeResident` 用 `makeSignal(60000)`,比 `activityTimeoutMs`(120s) 短。 | 改用 `makeSignal(params.activityTimeoutMs||60000)`。 |
|
|
372
|
+
| **`verdictMaxRounds`/`meetingKeepEvery` 不可调/不展示** | `verdictMaxRounds` 不在 `vibe_v4_set` schema;两者都不在 `status()` 参数串。 | `vibe_v4_set` 增加 `verdictMaxRounds`;`status()` 参数串补 `meetingKeepEvery` 与 `verdictMaxRounds`。 |
|
|
373
|
+
|
|
374
|
+
> 测试:`selfdrive-v4.mjs` 21/21(新增 B1 参数可见性断言);新增 `e2e-v4-fixes.test.mjs` 8/8(每项修复用独立 mock 宿主驱动:A1 非全票写回、A2 方法标 `类型: 方法`、A3 同进程 abort→resume 重建、B2 记录命题自动开会)。
|
|
375
|
+
|
|
376
|
+
> **已知边界(未改)**:`maxParallel`/`activityTimeoutMs` 仍未实际限流/心跳门控(避免破坏"持续推进→收敛");`claim_write`/`release_write` 仍未落地为真锁;真实 DSH `/compact` API 为 TODO。
|
|
377
|
+
|