dsh-vibe-math 1.3.0 → 1.3.2
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,8 @@
|
|
|
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。
|
|
50
52
|
|
|
51
53
|
---
|
|
52
54
|
|
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.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "installer.js",
|
|
7
7
|
"exports": {
|
|
@@ -26,6 +26,7 @@ export function apply(ctx) {
|
|
|
26
26
|
const DEFAULT_PARAMS = {
|
|
27
27
|
residentCount: 4, compactThreshold: 66, compactAfterRounds: 8,
|
|
28
28
|
maxParallel: 3, activityTimeoutMs: 120000, verdictMaxRounds: 3,
|
|
29
|
+
meetingKeepEvery: 5, // 每积累 N 个新产物自动触发一次同步会议
|
|
29
30
|
provider: '', model: '', residentPersona: '',
|
|
30
31
|
}
|
|
31
32
|
let params = Object.assign({}, DEFAULT_PARAMS)
|
|
@@ -35,7 +36,7 @@ export function apply(ctx) {
|
|
|
35
36
|
let problemText = '', problemId = 'problem', runId = 'run-' + shortId()
|
|
36
37
|
let meetingState = null, verifyState = null, pendingVerify = null
|
|
37
38
|
let busy = new Set(), wakeKind = new Map(), currentResident = ''
|
|
38
|
-
let lastActivityAt = now()
|
|
39
|
+
let lastActivityAt = now(), artifactCount = 0, lastSyncMeetingAt = 0, persistedEpoch = ''
|
|
39
40
|
const activityLogCap = 200
|
|
40
41
|
|
|
41
42
|
// ---- utils ----
|
|
@@ -45,6 +46,9 @@ export function apply(ctx) {
|
|
|
45
46
|
function clamp01(v){ const n=Number(v); if(!Number.isFinite(n)) return 0.5; return Math.max(0,Math.min(1,n)) }
|
|
46
47
|
function fmtTime(ts){ try { return new Date(ts||now()).toISOString().replace('T',' ').slice(0,19) } catch(e){ return String(ts||'') } }
|
|
47
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)) }
|
|
48
52
|
function textBlock(t){ return { type:'text', text:String(t) } }
|
|
49
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() }
|
|
50
54
|
function logActivity(event,detail){ activityLog.push({at:now(),event,detail:String(detail||'')}); if(activityLog.length>activityLogCap) activityLog.shift() }
|
|
@@ -82,10 +86,10 @@ export function apply(ctx) {
|
|
|
82
86
|
await writeJson('State/mailboxes.json', Object.fromEntries(mailboxes))
|
|
83
87
|
await writeJson('State/taskboard.json', taskboard)
|
|
84
88
|
await writeJson('State/decisions.json', decisions)
|
|
85
|
-
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})
|
|
86
90
|
}
|
|
87
91
|
async function loadAll(){
|
|
88
|
-
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||'' }
|
|
89
93
|
const rm=await readJson('State/residents.json'); if(rm&&typeof rm==='object') residents=new Map(Object.entries(rm))
|
|
90
94
|
const mb=await readJson('State/mailboxes.json'); if(mb&&typeof mb==='object') mailboxes=new Map(Object.entries(mb))
|
|
91
95
|
const tb=await readJson('State/taskboard.json'); if(Array.isArray(tb)) taskboard=tb
|
|
@@ -111,22 +115,24 @@ export function apply(ctx) {
|
|
|
111
115
|
+'This is your round (#'+r.rounds+'). You decide what to do — there is NO external assignment. Typical actions:\n'
|
|
112
116
|
+'- advance your direction; verify your own claims; record valuable artifacts to YOUR library (vibe_v4_publish_progress / record_proposition / record_method / record_subproblem), each with 价值程度 / 动机用途计划 / 自身概率估计;\n'
|
|
113
117
|
+'- message a specific resident (vibe_v4_send_message) or broadcast;\n'
|
|
118
|
+
+'- propose / claim / complete a shared task (vibe_v4_propose_task / vibe_v4_claim_task / vibe_v4_task_done / vibe_v4_list_tasks) — the task board is how you coordinate work;\n'
|
|
114
119
|
+'- call a meeting (vibe_v4_meeting) to coordinate / allocate tasks / propose a verification;\n'
|
|
115
|
-
+'- propose an object for unanimous verification (set propose_verify in your reply)
|
|
120
|
+
+'- propose an object for unanimous verification (set propose_verify in your reply);\n'
|
|
121
|
+
+'- report your context usage (vibe_v4_report_context) so the framework compacts you when needed.\n'
|
|
116
122
|
+'You may READ any other resident\'s Progress/Propos/Methods/Subproblems (read-only via vibe_v4_read_progress / fs); you only WRITE your own '+r.rId+' library.\n'
|
|
117
123
|
+'Rules:\n- Only Verified/ is established. Verification requires ALL residents unanimous; you trust only unanimous results.\n'
|
|
118
124
|
+'- If the ORIGINAL problem is solved, set solved=true (we stop only when ALL residents agree).\n'
|
|
119
125
|
+'New items:\n'+ (await inboxText(r.rId))
|
|
120
126
|
+'\nReply with ONLY a JSON object in a ```json fence (no prose outside):\n'
|
|
121
|
-
+'{"summary":"<what you did this round, 1-3 sentences>","solved":false,"propose_verify":"<
|
|
127
|
+
+'{"summary":"<what you did this round, 1-3 sentences>","solved":false,"propose_verify":"<id|null>","propose_task":"<task title|null>","claim_task":"<task id|null>","contextPct":40}'
|
|
122
128
|
}
|
|
123
129
|
function meetingPrompt(r, st){
|
|
124
130
|
return (params.residentPersona?params.residentPersona+'\n':'')
|
|
125
131
|
+'You are resident '+r.rId+'. A meeting is in progress (agenda: '+st.agenda+').'
|
|
126
132
|
+(st.type==='verify'?('\nThe group is verifying object: '+st.targetId+'. Give your independent verdict.'):'')
|
|
127
|
-
+'\nGive your input.
|
|
133
|
+
+'\nGive your input. You may: propose a task (propose_task), claim an open task (claim_task), propose an object for unanimous verification (propose_verify), or vote on whether the original problem is solved (voteSolved).\n'
|
|
128
134
|
+'Reply with ONLY a JSON object (```json fence):\n'
|
|
129
|
-
+'{"input":"<your contribution>","
|
|
135
|
+
+'{"input":"<your contribution>","propose_task":"<task title or null>","task_desc":"...","claim_task":"<task id or null>","propose_verify":"<id or null>","voteSolved":true}'
|
|
130
136
|
}
|
|
131
137
|
function verifyPrompt(r, vs){
|
|
132
138
|
const others=Object.entries(vs.verdicts).map(([k,v])=>'- '+k+': '+v.verdict+' ('+v.confidence+') '+v.reason).join('\n')
|
|
@@ -140,7 +146,8 @@ export function apply(ctx) {
|
|
|
140
146
|
}
|
|
141
147
|
|
|
142
148
|
// ---- resident lifecycle ----
|
|
143
|
-
|
|
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} }
|
|
144
151
|
async function spawnResident(r){
|
|
145
152
|
const started=await subagents.startContinuable({provider:pickProvider(),label:r.rId,request:{prompt:[textBlock(brainstormPrompt(r))],parent:rootAgent,agentOptions:{}},signal:makeSignal(60000)})
|
|
146
153
|
r.childId=started.childId; r.status='brainstorm'; r.lastActiveAt=now()
|
|
@@ -151,7 +158,17 @@ export function apply(ctx) {
|
|
|
151
158
|
if(!r.childId) return false
|
|
152
159
|
busy.add(r.rId); wakeKind.set(r.rId,kind||'normal'); currentResident=r.rId
|
|
153
160
|
r.lastActiveAt=now(); r.rounds+=1; r.roundsSinceCompact+=1
|
|
154
|
-
|
|
161
|
+
// context / /compact: if the resident reports high context (or reached the round proxy),
|
|
162
|
+
// prepend a compact directive — it condenses its working state to a self-summary that the
|
|
163
|
+
// framework uses as the next context seed (equivalent to /compact's "consolidate & forget").
|
|
164
|
+
let prompt = promptText
|
|
165
|
+
if(r.needCompact || (Number(r.contextPct)>=Number(params.compactThreshold)) || (r.roundsSinceCompact>=Number(params.compactAfterRounds))){
|
|
166
|
+
prompt = '[CONTEXT COMPACT — your conversation is at/near the limit. Do NOT re-derive history.\n' +
|
|
167
|
+
'Condense your current working state into ONE tight self-summary (findings so far, active direction, key artifacts you recorded, next concrete steps, open questions), then answer this round in the normal JSON format as usual.\n' +
|
|
168
|
+
'Set "contextPct": 15 (your post-compact usage) and "compacted": true in the reply so the framework records the condensed seed.]\n\n' + promptText
|
|
169
|
+
r.needCompact = true
|
|
170
|
+
}
|
|
171
|
+
try { await subagents.followup(rootAgent,r.childId,[textBlock(prompt)],{source:{kind:'user'},signal:makeSignal(60000)}); return true }
|
|
155
172
|
catch(e){ console.error('vibe-v4 wake '+r.rId+' failed: '+String((e&&e.message)||e)); busy.delete(r.rId); return false }
|
|
156
173
|
}
|
|
157
174
|
function byChild(childId){ for(const [,r] of residents){ if(r.childId===childId) return r } return undefined }
|
|
@@ -159,9 +176,28 @@ export function apply(ctx) {
|
|
|
159
176
|
// ---- artifact writers (resident-facing) ----
|
|
160
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} }
|
|
161
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); return {ok:true,id,file:'Propos/'+rId+'/'+id+'.md'} }
|
|
162
|
-
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); return {ok:true,id,file:'Methods/'+rId+'/'+id+'.md'} }
|
|
163
|
-
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); return {ok:true,id,file:'Subproblems/'+rId+'/'+id+'.md'} }
|
|
164
|
-
|
|
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'} }
|
|
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'} }
|
|
181
|
+
// auto-sync meeting: every meetingKeepEvery new artifacts, convene a general coordination meeting
|
|
182
|
+
function bumpArtifacts(){ artifactCount+=1; if(!meetingState && !verifyState && Number(params.meetingKeepEvery)>0 && artifactCount % Number(params.meetingKeepEvery)===0){ startMeeting('定期同步:分工/进展/是否需要验证','general',null).catch(()=>{}) } }
|
|
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 '' }
|
|
189
|
+
|
|
190
|
+
// ---- task board (residents propose / claim / complete; framework wakes the claimer) ----
|
|
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')) }
|
|
192
|
+
async function proposeTask(title,description,proposer){ const id='t-'+shortId(); taskboard.push({id,title:String(title),description:String(description||''),status:'open',proposer:proposer||'',claimer:'',source:''}); await saveTaskboard(); logActivity('task','proposed '+id+'「'+title+'」'); return {ok:true,id} }
|
|
193
|
+
async function claimTask(id,claimer){ const t=taskboard.find(x=>x.id===id); if(!t) return {ok:false,message:'no such task'}; if(t.status!=='open') return {ok:false,message:'task already '+t.status}; t.status='claimed'; t.claimer=claimer; await saveTaskboard(); logActivity('task',claimer+' claimed '+id);
|
|
194
|
+
// wake the claimer to work on it (framework moves the task, resident decides how)
|
|
195
|
+
const r=residents.get(claimer); if(r && !busy.has(claimer)){ currentResident=claimer; await wakeResident(r, (await normalPrompt(r))+'\n\n[YOU CLAIMED TASK '+id+'] '+t.title+' — '+t.description,'normal'); await saveAll() }
|
|
196
|
+
return {ok:true} }
|
|
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} }
|
|
198
|
+
async function saveTaskboard(){ await writeJson('State/taskboard.json',taskboard); await writeTaskboard() }
|
|
199
|
+
function listTasks(){ return taskboard.filter(t=>t.status!=='done') }
|
|
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} }
|
|
165
201
|
|
|
166
202
|
// ---- messaging ----
|
|
167
203
|
async function postMessage(from,to,content){
|
|
@@ -173,6 +209,11 @@ export function apply(ctx) {
|
|
|
173
209
|
}
|
|
174
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}
|
|
175
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
|
+
}
|
|
176
217
|
|
|
177
218
|
// ---- meeting ----
|
|
178
219
|
async function startMeeting(agenda,type,targetId){
|
|
@@ -196,6 +237,12 @@ export function apply(ctx) {
|
|
|
196
237
|
await writeText('Shared/meetings/'+st.id+'.md', lines.join('\n'))
|
|
197
238
|
meetings.push({id:st.id,agenda:st.agenda,at:now(),inputs:st.inputs})
|
|
198
239
|
logDecision('meeting',st.agenda)
|
|
240
|
+
// handle what the meeting produced: task proposals/claims, verify targets, stop vote
|
|
241
|
+
for(const [id,iv] of Object.entries(st.inputs)){
|
|
242
|
+
if(iv.propose_task) await proposeTask(iv.propose_task, iv.task_desc||'', id)
|
|
243
|
+
if(iv.claim_task) await claimTask(iv.claim_task, id)
|
|
244
|
+
if(iv.propose_verify) pendingVerify={targetId:iv.propose_verify,targetType:guessTargetType(iv.propose_verify),proposer:id,at:now()}
|
|
245
|
+
}
|
|
199
246
|
const votes=Object.values(st.inputs).map(x=>x.voteSolved).filter(v=>typeof v==='boolean')
|
|
200
247
|
const allSolved=votes.length>0 && votes.every(v=>v===true)
|
|
201
248
|
logActivity('meeting', 'concluded'+(allSolved?' → ALL agree solved':''))
|
|
@@ -303,7 +350,7 @@ export function apply(ctx) {
|
|
|
303
350
|
const parsed=parseReply(output)
|
|
304
351
|
const kind=wakeKind.get(r.rId)||'normal'
|
|
305
352
|
if(kind==='meeting' && meetingState){
|
|
306
|
-
meetingState.inputs[r.rId]={input:parsed.input||parsed.summary||'',voteSolved:typeof parsed.voteSolved==='boolean'?parsed.voteSolved:null,propose_verify:parsed.propose_verify||null}
|
|
353
|
+
meetingState.inputs[r.rId]={input:parsed.input||parsed.summary||'',voteSolved:typeof parsed.voteSolved==='boolean'?parsed.voteSolved:null,propose_verify:parsed.propose_verify||null,propose_task:parsed.propose_task||null,task_desc:parsed.task_desc||'',claim_task:parsed.claim_task||null}
|
|
307
354
|
if(parsed.propose_verify) pendingVerify={targetId:parsed.propose_verify,targetType:guessTargetType(parsed.propose_verify),proposer:r.rId,at:now()}
|
|
308
355
|
await saveAll(); await continueMeetingRound(); return
|
|
309
356
|
}
|
|
@@ -316,7 +363,14 @@ export function apply(ctx) {
|
|
|
316
363
|
// normal turn
|
|
317
364
|
if(r.status==='brainstorm'){ r.insight=parsed.summary||output; r.status='active' }
|
|
318
365
|
if(typeof parsed.solved==='boolean') reports.push({rId:r.rId,solved:parsed.solved,summary:parsed.summary||'',at:now()})
|
|
366
|
+
// context / compact: record the condensed seed + post-compact usage, clear the flag
|
|
367
|
+
if(typeof parsed.contextPct==='number'){ r.contextPct=clPct(parsed.contextPct) }
|
|
368
|
+
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') }
|
|
319
369
|
if(parsed.propose_verify) pendingVerify={targetId:parsed.propose_verify,targetType:guessTargetType(parsed.propose_verify),proposer:r.rId,at:now()}
|
|
370
|
+
// task actions via reply (a resident may propose or claim a task in its round)
|
|
371
|
+
if(parsed.propose_task) await proposeTask(parsed.propose_task, parsed.task_desc||'', r.rId)
|
|
372
|
+
if(parsed.claim_task) await claimTask(parsed.claim_task, r.rId)
|
|
373
|
+
if(parsed.task_done) await taskDone(parsed.task_done, r.rId)
|
|
320
374
|
// a resident may self-trigger a meeting (resident-driven coordination, closest to the philosophy)
|
|
321
375
|
if(parsed.propose_meeting && !meetingState){ await startMeeting(String(parsed.propose_meeting),'general',null); await saveAll(); return }
|
|
322
376
|
await saveAll(); await scheduleNext()
|
|
@@ -331,7 +385,7 @@ export function apply(ctx) {
|
|
|
331
385
|
if(residentCount) params.residentCount=Number(residentCount)||4
|
|
332
386
|
running=true; autoDone=false; phase='brainstorm'
|
|
333
387
|
await writeText('Problems/'+problemId+'.md','# 问题|'+problemId+'\n- ID: '+problemId+'\n- 类型: 问题\n- 状态: 求解中\n- 优先级: 1\n- 依赖: []\n\n## 陈述\n'+problemText+'\n')
|
|
334
|
-
residents=new Map(); mailboxes=new Map(); taskboard=[]; decisions=[]; meetings=[]; reports=[]; verifyState=null; meetingState=null; pendingVerify=null
|
|
388
|
+
residents=new Map(); mailboxes=new Map(); taskboard=[]; decisions=[]; meetings=[]; reports=[]; verifyState=null; meetingState=null; pendingVerify=null; residentSeq=0
|
|
335
389
|
const dirs=Array.isArray(seedDirections)?seedDirections.slice(0,params.residentCount):[]
|
|
336
390
|
for(let i=0;i<params.residentCount;i++){ const r=newResident(dirs[i]||''); await spawnResident(r) }
|
|
337
391
|
await saveAll(); return {ok:true,message:'v4 started: '+params.residentCount+' resident(s) brainstorming',project:currentProject}
|
|
@@ -339,9 +393,15 @@ export function apply(ctx) {
|
|
|
339
393
|
async function resume(){
|
|
340
394
|
currentProject=await readCurrentProject(); await ensureDirs(); await loadAll()
|
|
341
395
|
if(phase==='idle' && !running) return {ok:false,message:'nothing to resume'}
|
|
396
|
+
// If the persisted State came from a DIFFERENT process (crash/restart), the saved
|
|
397
|
+
// childIds are stale; clear them so residents re-spawn (their libraries persist on
|
|
398
|
+
// disk and re-seed the resumed run). Same-process pause→resume keeps continuable ids.
|
|
399
|
+
const crossProcess = persistedEpoch !== processEpoch
|
|
400
|
+
if(crossProcess){ for(const [,r] of residents){ r.childId=''; r.status='brainstorm'; r.roundsSinceCompact=0 } }
|
|
342
401
|
for(const [,r] of residents){ if(!r.childId){ await spawnResident(r) } }
|
|
343
402
|
if(!running){ running=true; autoDone=false; if(phase==='idle') phase='active' }
|
|
344
|
-
|
|
403
|
+
if(crossProcess && phase==='active') phase='brainstorm' // let re-spawned residents re-bootstrap together
|
|
404
|
+
logActivity('resume','restarted'+(crossProcess?' (cross-process: re-spawned)':'')); await saveAll(); await scheduleNext(); return {ok:true,message:'resumed',project:currentProject}
|
|
345
405
|
}
|
|
346
406
|
function status(){ return { ok:true, running, phase, autoDone, project:currentProject, residentCount:residents.size,
|
|
347
407
|
residents:listResidents(), busy:[...busy], taskboard:taskboard.length,
|
|
@@ -359,10 +419,12 @@ export function apply(ctx) {
|
|
|
359
419
|
return {
|
|
360
420
|
sessionId, running:()=>running, autoDone:()=>autoDone, phase:()=>phase,
|
|
361
421
|
onResidentEnd, start, resume, status, report, addMember, removeMember, setParams,
|
|
362
|
-
setPause, initAbort, postMessage, startMeeting, saveAll,
|
|
422
|
+
setPause, initAbort, postMessage, startMeeting, saveAll, broadcast,
|
|
363
423
|
currentResident:()=>currentResident,
|
|
424
|
+
residentIdOf:(agent)=>{ const m=residentOfAgent(agent); return m||currentResident },
|
|
364
425
|
useResident:(id)=>{ currentResident=id },
|
|
365
|
-
publishProgress, recordProposition, recordMethod, recordSubproblem, listResidents,
|
|
426
|
+
publishProgress, recordProposition, recordMethod, recordSubproblem, listResidents, reportContext,
|
|
427
|
+
proposeTask, claimTask, taskDone, listTasks,
|
|
366
428
|
readProgress: async (rid)=>({text:(await readText('Progress/'+rid+'/progress.md'))||''}),
|
|
367
429
|
frameworkRoot:frameworkRoot, currentProject:()=>currentProject, problemText:()=>problemText,
|
|
368
430
|
residentCount:()=>residents.size,
|
|
@@ -376,7 +438,7 @@ export function apply(ctx) {
|
|
|
376
438
|
tools.register({ name, description, parameters,
|
|
377
439
|
output:{ schema:{ type:'string' }, render:(_a,v)=>[{type:'text',text:String(v)}] },
|
|
378
440
|
execute: async (args, exec)=>{
|
|
379
|
-
try { const s=getSession(exec&&exec.agent); if(!s) return JSON.stringify({ok:false,error:'no session'}); return JSON.stringify(await fn(s,args||{})) }
|
|
441
|
+
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)) }
|
|
380
442
|
catch(e){ return JSON.stringify({ok:false,error:String((e&&e.message)||e)}) }
|
|
381
443
|
} })
|
|
382
444
|
}
|
|
@@ -387,20 +449,28 @@ export function apply(ctx) {
|
|
|
387
449
|
registerTool('vibe_v4_abort','Abort V4 and interrupt residents.',objParams({}),(s)=>s.initAbort())
|
|
388
450
|
registerTool('vibe_v4_status','Show V4 status.',objParams({}),(s)=>s.status())
|
|
389
451
|
registerTool('vibe_v4_report','Return the V4 progress report.',objParams({}),(s)=>s.report())
|
|
390
|
-
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')
|
|
452
|
+
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) })
|
|
391
453
|
registerTool('vibe_v4_meeting','Start a meeting (coordinate / allocate / propose verification).',objParams({agenda:{type:'string'}},['agenda']),(s,a)=>s.startMeeting(a.agenda))
|
|
392
454
|
registerTool('vibe_v4_list_members','List residents.',objParams({}),(s)=>({ok:true,residents:s.listResidents()}))
|
|
393
455
|
registerTool('vibe_v4_add_member','Add a resident.',objParams({direction:{type:'string'}}),(s,a)=>s.addMember(a.direction))
|
|
394
456
|
registerTool('vibe_v4_remove_member','Close a resident.',objParams({id:{type:'string'}},['id']),(s,a)=>s.removeMember(a.id))
|
|
395
|
-
registerTool('vibe_v4_set','Set V4 parameters.',objParams({residentCount:{type:'integer'},compactAfterRounds:{type:'integer'},compactThreshold:{type:'integer'},maxParallel:{type:'integer'},activityTimeoutMs:{type:'integer'}}),(s,a)=>{ s.setParams(a); return {ok:true} })
|
|
396
|
-
// resident-facing tools: route to the
|
|
397
|
-
|
|
398
|
-
registerTool('
|
|
399
|
-
registerTool('
|
|
400
|
-
registerTool('
|
|
401
|
-
registerTool('
|
|
402
|
-
registerTool('
|
|
457
|
+
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} })
|
|
458
|
+
// resident-facing tools: route to the CALLING resident (exec.agent.id === childId);
|
|
459
|
+
// fall back to the last-woken resident when called by the host/assistant.
|
|
460
|
+
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))
|
|
461
|
+
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))
|
|
462
|
+
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))
|
|
463
|
+
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))
|
|
464
|
+
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))
|
|
465
|
+
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)||''} })
|
|
403
466
|
registerTool('vibe_v4_list_residents','(resident) List fellow residents.',objParams({}),(s)=>({ok:true,residents:s.listResidents()}))
|
|
467
|
+
// task board (residents; board is the residents' own allocation mechanism)
|
|
468
|
+
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)))
|
|
469
|
+
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)))
|
|
470
|
+
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)))
|
|
471
|
+
registerTool('vibe_v4_list_tasks','(resident) List open tasks.',objParams({}),(s)=>({ok:true,tasks:s.listTasks()}))
|
|
472
|
+
// context / compact (resident reports its context usage so the framework can /compact-equivalent)
|
|
473
|
+
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))
|
|
404
474
|
registerTool('vibe_v4_claim_write','Reserved: shared-file write lock (framework-managed).',objParams({target:{type:'string'}},['target']),(s,a)=>({ok:true,key:a.target}))
|
|
405
475
|
registerTool('vibe_v4_release_write','Reserved: shared-file write lock release.',objParams({target:{type:'string'}},['target']),(s,a)=>({ok:true,key:a.target}))
|
|
406
476
|
|
|
@@ -325,3 +325,34 @@ VibeMath/Projects/<project>/
|
|
|
325
325
|
- `verdictQuorum` 是否始终为 `all`(哲学强调一致,默认不改)。
|
|
326
326
|
- `perResidentLibs`(每常驻独立目录 vs 共享库+署名)——默认独立。
|
|
327
327
|
- DSH `/compact` 的精确触发 API(实现 P1 时确认)。
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## 16. 实现现状备注(P5/P6 已落地,v1.3.x)
|
|
332
|
+
|
|
333
|
+
**任务板认领(P5)**:`vibe_v4_propose_task / claim_task / task_done / list_tasks`。常驻可在回复或会议中提议/认领任务;认领后框架**唤醒认领者**带任务工作;任务板落盘 `Shared/taskboard.md` + `State/taskboard.json`;会议结束落任务板/触发验证目标。
|
|
334
|
+
|
|
335
|
+
**上下文 / /compact(P5)**:DSH 打包未暴露"子代理上下文占比 + `/compact`" RPC,故实现**等效真实压缩**——常驻用 `vibe_v4_report_context {pct}` 上报占比;达 `compactThreshold`(66) 或 `compactAfterRounds` 轮数时,框架在下次唤醒前注入"[CONTEXT COMPACT — 浓缩自述]"指令,常驻把工作状态浓缩为一段自述并在回复里带 `contextPct`(低)、`compacted:true`;框架记录**浓缩种子**为后续上下文并复位 `roundsSinceCompact/needCompact`。`compactThreshold/compactAfterRounds/meetingKeepEvery` 均可调。
|
|
336
|
+
|
|
337
|
+
**会议主动触发(P6)**:
|
|
338
|
+
- 常驻 normal 回复里 `propose_meeting` 自触发会议;助手亦可 `vibe_v4_meeting`。
|
|
339
|
+
- **自动同步会议**:每积累 `meetingKeepEvery`(默认 5) 个新产物,框架自动发起"分工/进展/是否需要验证"同步会议。
|
|
340
|
+
- 会议输入可含 `propose_task/claim_task/propose_verify/voteSolved`,结束统一落任务板、触发验证、记停止表决;仍"全体一致 voteSolved=true 才停止"。
|
|
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
|
+
|