dsh-vibe-math 1.3.7 → 1.3.8

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
@@ -58,7 +58,9 @@
58
58
  >
59
59
  > 🔧 **v1.3.6 修复 v4 preset 选择后跳回原 preset**:v4 插件的 `apply` 读取 `ctx.subagents/agents/fs/tools/commands` 却**未声明 `inject`**,被 DSH 守卫以"未声明依赖"拒绝 → 组合无法挂载 → 选择后自动回退。已补 `export const inject = [...]`,并把心跳定时器从全局 `setTimeout/clearTimeout`(插件沙箱里不存在)改为 **`timer` 服务(`ctx.timeout`)**。
60
60
  >
61
- > 🔧 **v1.3.7 哲学回归(清晰提示词 + 真实交流群 + 直接写文件)**:常驻每轮统一注入 `contextBrief`(背景/使命、工作模式、你负责的文件与格式、可用工具及功能、规则),并明确"只写自己、可读任何人的库、应主动读别人对齐事实";会议把其他常驻实际发言(input)转给每个人看(能补充/反驳/表决),常驻日常轮的 `input` 经 `relayToGroup` 转发到其它常驻邮箱(像群聊);常驻**直接用 fs 写自己的 md**(按格式),`vibe_v4_*` 记录工具只是可选便利;去掉"typical actions"式固定约束,改为"你自己决定做什么;优先级/分工由团队讨论涌现"。详见 §20。
61
+ > 🔧 **v1.3.7 哲学回归(清晰提示词 + 真实交流群 + 直接写文件)**:常驻首轮注入完整 `contextBrief`(背景/使命、工作模式、文件与格式、工具、规则);会议把其他常驻实际发言(input)转给每个人看,常驻日常轮的 `input` 经 `relayToGroup` 转发到其它常驻邮箱(像群聊);常驻**直接用 fs 写自己的 md**;去固定约束改为团队讨论涌现。详见 §20。
62
+ >
63
+ > 🔧 **v1.3.8 基于真实测试的诊断修复**:**① 共识验证真正"全体一致"**——`finalizeVerify` 现在要求**全体在册常驻都投了票**才可判"一致",否则辩论或保留为未定论(实测曾出现 2/4 投票却被判"全体一致为真",已修);**② 会议必须全体发言**——`continueMeetingRound` 按"是否已发言"收口,`allSolved`(stop)要求全员发言+全票 true,杜绝缺席成员被带偏;**③ 背景只在首轮讲一次**——`contextBrief` 只在 `brainstormPrompt`(首轮)注入完整版,后续 normal/meeting/verify/CHECKPOINT 用极简当前状态,不再每轮重复长背景(省上下文);**④ 验证目标按提出者精确定位**(`targetOwner` + `findSourceRel` 优先提出者库,避免同名 id 撞车);**⑤ 主代理放权**——persona 明确"让常驻自组织(hands-off)",不注议程/优先级/分工/验证决定,只 read status/report,用户明确要求或明显僵死时才 message/meeting 且只促成不决定。详见 §21。
62
64
 
63
65
  ---
64
66
 
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.7",
4
+ "version": "1.3.8",
5
5
  "type": "module",
6
6
  "main": "installer.js",
7
7
  "exports": {
@@ -36,6 +36,14 @@
36
36
  team genuinely discusses/debates), convenes and records meetings, exposes a shared task
37
37
  board, and stops the run only when the whole team agrees the problem is solved.
38
38
 
39
+ **YOUR ROLE — LET THEM SELF-ORGANIZE (hands-off):** you are NOT a moderator/coordinator.
40
+ Do NOT inject agendas, priorities, division-of-labor, or verification decisions, and do
41
+ NOT direct the residents' work. After `vibe_v4_start`, stay passive: read `vibe_v4_status`
42
+ / `vibe_v4_report` and summarize in plain language when asked. Use `vibe_v4_message` /
43
+ `vibe_v4_meeting` ONLY when the user explicitly asks you to intervene, or when the group
44
+ is visibly deadlocked (all idle & nothing progressing for a long time) — and even then,
45
+ only relay/nudge the group to decide, never decide for them.
46
+
39
47
  Data lives under {{cwd}}/VibeMath/Projects/<project>/:
40
48
  Problems/ (original problem card), Progress/<resident>/ (each resident's progress),
41
49
  Propos/<resident>/ (each resident's propositions, "- ID: p-<id>" / "- 概率:" / "- 价值程度:" ...),
@@ -169,23 +169,18 @@ export function apply(ctx) {
169
169
  }
170
170
  async function normalPrompt(r){
171
171
  return (params.residentPersona?params.residentPersona+'\n':'')
172
- +contextBrief(r,'recap')+'\n'
173
- +'## 你的第 '+r.rounds+' 轮\n'
174
- +'这一轮你自己决定做什么:推进你的方向、验证你的结论、给某常驻发消息、提议/认领任务、或发起会议/验证。**一切由你和团队讨论决定,没有外部派活。**\n'
175
- +'动手前先**读读别人的库**(Read-only)知道大家做到哪、对齐事实、避免重复;然后**直接把你的进展/新结论用 fs 写进你自己的文件**。\n'
176
- +'若你有话要对团队说(想让大家看到、讨论),在回复的 "input" 里写出来——它会被转发给其他常驻(如同一场群聊)。\n'
172
+ +'Resident researcher '+r.rId+' — 第 '+r.rounds+' 轮。一切由你和团队讨论决定。动手前先**读别人的库**对齐事实、避免重复;把新进展/结论**直接用 fs 写进你自己的文件**;想对团队说的话放 "input"(会转给其他常驻)。\n'
177
173
  +'\n团队成员:\n'+banner()+'\n'
178
174
  +'New items:\n'+ (await inboxText(r.rId)) +'\n'
179
175
  +'Reply with ONLY a JSON object:\n'
180
- +'{"summary":"<what you did / decided this round, 1-3 sentences>","input":"<optional: a message to the whole team, or empty string>","solved":false,"propose_verify":"<id|null>","propose_meeting":"<agenda|null>","propose_task":"<task title|null>","claim_task":"<task id|null>","task_done":"<task id|null>","contextPct":40}'
176
+ +'{"summary":"<what you did / decided this round, 1-3 sentences>","input":"<optional: a message to the whole team, or \\"\\">","solved":false,"propose_verify":"<id|null>","propose_meeting":"<agenda|null>","propose_task":"<task title|null>","claim_task":"<task id|null>","task_done":"<task id|null>","contextPct":40}'
181
177
  }
182
178
  function meetingPrompt(r, st){
183
179
  const prior=Object.entries(st.inputs).filter(([k])=>k!==r.rId).map(([k,iv])=>' ['+k+'] '+String(iv.input||iv.summary||'')).join('\n')
184
180
  return (params.residentPersona?params.residentPersona+'\n':'')
185
- +contextBrief(r,'recap')+'\n'
186
- +'## 会议进行中 —— A meeting is in progress (agenda: '+st.agenda+').'
187
- +(st.type==='verify'?('\n团队正在验证对象:'+st.targetId+'('+st.targetType+')。请先看他人意见,再给独立判断。'):'')
188
- +'\n这是一场**真实讨论**:下面已有人发言(框架把各常驻的 input 转给你),请先看,然后**加入讨论/补充/反驳/表决**。'
181
+ +'Resident '+r.rId+' — 团队会议进行中。 A meeting is in progress (agenda: '+st.agenda+').'
182
+ +(st.type==='verify'?('\n团队正在验证对象:'+st.targetId+'('+st.targetType+',提出者 '+st.targetOwner+')。请先看他人意见,再给独立判断。'):'')
183
+ +'\n这是一场真实讨论:下面已有人发言(转给你),请先看,然后**加入讨论/补充/反驳/表决**。'
189
184
  +(prior?('\n\n### 已有发言(他人 input,已转发给你)\n'+prior):'\n(目前还没人发言,你先说。)')
190
185
  +'\n\n你可以:提议任务(propose_task)、认领开放任务(claim_task)、提议验证对象(propose_verify)、或对"原问题是否已解决"表决(voteSolved)。请把**你的实际发言**写进 "input"。'
191
186
  +'\nReply with ONLY a JSON object:\n'
@@ -194,8 +189,7 @@ export function apply(ctx) {
194
189
  function verifyPrompt(r, vs){
195
190
  const others=Object.entries(vs.verdicts).map(([k,v])=>'- '+k+': '+v.verdict+' ('+v.confidence+') '+v.reason).join('\n')
196
191
  return (params.residentPersona?params.residentPersona+'\n':'')
197
- +contextBrief(r,'recap')+'\n'
198
- +'## 团队验证 —— The group is verifying object '+vs.targetId+'('+vs.targetType+')。\n'
192
+ +'Resident '+r.rId+' — 团队验证。 The group is verifying object '+vs.targetId+'('+vs.targetType+',提出者 '+vs.targetOwner+')。\n'
199
193
  +'只有**全体常驻一致判真(或一致判假)**才算数。请给出你**诚实独立的判断**'
200
194
  +(vs.stage==='debate'?',并参考他人意见:\n':'。\n')
201
195
  +(vs.stage==='debate'&&others?('### 他人意见(已转发给你)\n'+others+'\n'):'')
@@ -294,15 +288,16 @@ export function apply(ctx) {
294
288
  }
295
289
  async function continueMeetingRound(){
296
290
  if(!meetingState) return
297
- const ids=Array.from(residents.keys()); const allAsked=ids.every(id=>meetingState.asked.indexOf(id)!==-1)
298
- if(allAsked){ await finalizeMeeting(); return }
299
- // only wake IDLE residents (never double-wake a busy one; in-flight ones re-trigger this on end)
300
- const id=ids.find(x=>meetingState.asked.indexOf(x)===-1 && !busy.has(x)); if(!id) return
301
- meetingState.asked.push(id); const r=residents.get(id)
291
+ const ids=Array.from(residents.keys()); const allSpoke=ids.every(id=>meetingState.inputs[id]!==undefined)
292
+ if(allSpoke){ await finalizeMeeting(); return }
293
+ // only wake IDLE un-spoken residents; in-flight ones re-trigger this on end.
294
+ const id=ids.find(x=>meetingState.inputs[x]===undefined && !busy.has(x)); if(!id) return
295
+ const r=residents.get(id)
302
296
  await wakeResident(r, meetingPrompt(r,meetingState), 'meeting'); await saveAll()
303
297
  }
304
298
  async function finalizeMeeting(){
305
299
  const st=meetingState
300
+ const ids=Array.from(residents.keys()); const allSpoke=ids.length>0 && ids.every(id=>st.inputs[id]!==undefined)
306
301
  const lines=['# 会议 '+st.id+'|'+fmtTime(),'','**议程**:'+st.agenda,'']
307
302
  for(const [id,iv] of Object.entries(st.inputs)){ lines.push('### '+id); lines.push(iv.input||''); lines.push('') }
308
303
  await writeText('Shared/meetings/'+st.id+'.md', lines.join('\n'))
@@ -315,8 +310,8 @@ export function apply(ctx) {
315
310
  if(iv.propose_verify) pendingVerify={targetId:iv.propose_verify,targetType:guessTargetType(iv.propose_verify),proposer:id,at:now()}
316
311
  }
317
312
  const votes=Object.values(st.inputs).map(x=>x.voteSolved).filter(v=>typeof v==='boolean')
318
- const allSolved=votes.length>0 && votes.every(v=>v===true)
319
- logActivity('meeting', 'concluded'+(allSolved?' → ALL agree solved':''))
313
+ const allSolved = allSpoke && votes.length>0 && votes.every(v=>v===true)
314
+ logActivity('meeting', 'concluded'+(allSolved?' → ALL agree solved':' (not all spoke / not unanimous)'))
320
315
  if(allSolved){ running=false; autoDone=true; phase='done'; logActivity('stop','all residents agree: problem solved'); await saveAll(); return }
321
316
  meetingState=null; wakeKind.clear(); await saveAll(); await scheduleNext()
322
317
  }
@@ -325,33 +320,34 @@ export function apply(ctx) {
325
320
  async function beginVerify(pv){
326
321
  clearHeartbeat()
327
322
  pendingVerify=null
328
- verifyState={targetId:pv.targetId,targetType:pv.targetType,stage:'independent',round:0,asked:[],verdicts:{},transcript:[]}
323
+ verifyState={targetId:pv.targetId,targetType:pv.targetType,targetOwner:pv.proposer||'',stage:'independent',round:0,asked:[],verdicts:{},transcript:[]}
329
324
  logActivity('verify','debate begin: '+pv.targetId+' ('+pv.targetType+')'); await saveAll(); await scheduleNext()
330
325
  }
331
326
  async function continueVerifyRound(){
332
327
  if(!verifyState) return
333
- const ids=Array.from(residents.keys()); const allAsked=ids.every(id=>verifyState.asked.indexOf(id)!==-1)
334
- if(allAsked){ await finalizeVerify(); return }
335
- // only wake IDLE residents (never double-wake; in-flight ones re-trigger this on end)
336
- const id=ids.find(x=>verifyState.asked.indexOf(x)===-1 && !busy.has(x)); if(!id) return
337
- verifyState.asked.push(id); const r=residents.get(id)
328
+ const ids=Array.from(residents.keys()); const allVoted=ids.every(id=>verifyState.verdicts[id]!==undefined)
329
+ if(allVoted){ await finalizeVerify(); return }
330
+ const id=ids.find(x=>verifyState.verdicts[x]===undefined && !busy.has(x)); if(!id) return
331
+ const r=residents.get(id)
338
332
  await wakeResident(r, verifyPrompt(r,verifyState), verifyState.stage==='independent'?'verif-ind':'verif-deb'); await saveAll()
339
333
  }
340
334
  async function finalizeVerify(){
341
- const vs=verifyState; const vals=Object.values(vs.verdicts)
342
- const allTrue=vals.length>0 && vals.every(x=>x.verdict==='TRUE')
343
- const allFalse=vals.length>0 && vals.every(x=>x.verdict==='FALSE')
335
+ const vs=verifyState; const expected=Array.from(residents.keys()).length
336
+ const allVoted = expected>0 && Object.keys(vs.verdicts).length>=expected
337
+ const vals=Object.values(vs.verdicts)
338
+ const allTrue = allVoted && vals.every(x=>x.verdict==='TRUE')
339
+ const allFalse = allVoted && vals.every(x=>x.verdict==='FALSE')
344
340
  if(allTrue||allFalse){ await closeVerify(vs,allTrue); return }
345
341
  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 }
346
342
  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
347
- await writeDebateDoc(vs,false,avg); await rewriteSourceProb(vs.targetId, avg); logActivity('verify',vs.targetId+' NOT unanimous → kept unverified (avg '+avg.toFixed(2)+')')
343
+ await writeDebateDoc(vs,false,avg); await rewriteSourceProb(vs.targetId, avg, vs.targetOwner); logActivity('verify',vs.targetId+' NOT unanimous → kept unverified (avg '+avg.toFixed(2)+')')
348
344
  verifyState=null; wakeKind.clear(); await saveAll(); await scheduleNext()
349
345
  }
350
346
  async function closeVerify(vs,isTrue){
351
347
  await writeDebateDoc(vs,true,isTrue?1:0)
352
348
  const target=vs.targetId
353
349
  await writeVerifiedCard(vs,isTrue)
354
- await rewriteSource(target,isTrue)
350
+ await rewriteSource(target,isTrue,vs.targetOwner)
355
351
  logActivity('verify',target+' → Verified ('+(isTrue?'真':'假')+') by unanimous consensus')
356
352
  verifyState=null; wakeKind.clear(); await saveAll(); await scheduleNext()
357
353
  }
@@ -367,27 +363,27 @@ export function apply(ctx) {
367
363
  const text='# 已验证|'+vs.targetId+'\n- ID: '+vs.targetId+'\n- 类型: '+type+'\n- 结论: '+(isTrue?'真':'假')+'\n- 概率: '+(isTrue?1:0)+'\n- 来源: 全体常驻一致\n## 陈述\n参见来源卡。\n'
368
364
  await writeText('Verified/'+dir+'/'+vs.targetId+'.md', text)
369
365
  }
370
- async function findSourceRel(target){
371
- let rel=null
372
- for(const [rid] of residents){
366
+ async function findSourceRel(target, owner){
367
+ // Prefer the OWNER's library (avoids id collisions across residents), then others.
368
+ const order = owner ? [owner, ...Array.from(residents.keys()).filter(k=>k!==owner)] : Array.from(residents.keys())
369
+ for(const rid of order){
373
370
  for(const base of ['Propos','Methods','Subproblems']){
374
- const cand=base+'/'+rid+'/'+target+'.md'; if((await readText(cand))!==undefined){ rel=cand; break }
371
+ const cand=base+'/'+rid+'/'+target+'.md'; if((await readText(cand))!==undefined){ return cand }
375
372
  }
376
- if(rel) break
377
373
  }
378
- return rel || ('Propos/'+target+'.md')
374
+ return 'Propos/'+target+'.md'
379
375
  }
380
376
  // non-unanimous verification: keep the object in its library but write back the
381
377
  // average probability (design §8: "留库附概率"), so the card reflects the consensus estimate.
382
- async function rewriteSourceProb(target,prob){
383
- const rel=await findSourceRel(target)
378
+ async function rewriteSourceProb(target,prob,owner){
379
+ const rel=await findSourceRel(target,owner)
384
380
  let text=(await readText(rel))||''
385
381
  text=text.replace(/(^|\n)- 概率:.*/m,'$1- 概率: '+Number(prob).toFixed(2))
386
382
  await writeText(rel,text)
387
383
  }
388
- async function rewriteSource(target,isTrue){
384
+ async function rewriteSource(target,isTrue,owner){
389
385
  // find & update the source card status/prob; best effort across per-resident libs
390
- const rel=await findSourceRel(target)
386
+ const rel=await findSourceRel(target,owner)
391
387
  let text=(await readText(rel))||''
392
388
  text=text.replace(/(^|\n)- 状态:.*/m,'$1'+(isTrue?'- 状态: 已验证·真':'- 状态: 已验证·假'))
393
389
  .replace(/(^|\n)- 概率:.*/m,'$1'+(isTrue?'- 概率: 1':'- 概率: 0'))
@@ -402,8 +398,7 @@ export function apply(ctx) {
402
398
  // of infinite token-burning, matching the "framework never assigns work" philosophy.
403
399
  function heartbeatPrompt(r){
404
400
  return (params.residentPersona?params.residentPersona+'\n':'')
405
- +contextBrief(r,'recap')+'\n'
406
- +'## CHECKPOINT(空闲)—— 团队在等待方向。\n'
401
+ +'Resident researcher '+r.rId+' — CHECKPOINT(空闲):团队在等待方向。'
407
402
  +'请用一句话说明你下一步做什么;若你已无产出、或认为问题已解决/接近解决,请**提议开会(propose_meeting)**、**提议验证(propose_verify)**、或**声明 solved=true**,让团队能做出决定——不要产出填充性工作。\n'
408
403
  +'Reply with ONLY a JSON object:\n'
409
404
  +'{"summary":"<what you do next or a declaration>","solved":false,"propose_verify":"<id|null>","propose_meeting":"<agenda|null>","claim_task":"<id|null>"}'
@@ -423,5 +423,27 @@ VibeMath/Projects/<project>/
423
423
  ### 4. 自组织优先,非固定模式约束
424
424
  删除"typical actions"式枚举,改为"你自己决定做什么;优先级/分工由团队讨论决定"。仍保留 JSON 回复里 `propose_verify/propose_meeting/claim_task/voteSolved/solved` 作为框架可执行的**控制通道**,但讨论内容走 `input`/会议转发。
425
425
 
426
- > 测试:`selfdrive-v4.mjs` 21/21;`e2e-v4-fixes.test.mjs` 14/14(新增 T5 提示词完整性、T6 群聊转发)。
426
+ > 测试:`selfdrive-v4.mjs` 21/21;`e2e-v4-fixes.test.mjs` 17/17(T5 提示词完整性、T6 群聊转发、T7 背景只讲一次)。
427
+
428
+ ---
429
+
430
+ ## 21. 基于真实测试的诊断修复(v1.3.8)
431
+
432
+ 对一次真实 4 常驻 run(HRT 猜想)的会话记录做了全面诊断,修复以下违反哲学的问题:
433
+
434
+ ### 1. 共识验证真正做到"全体一致"
435
+ 此前 `finalizeVerify` 只看"投过票的人是否一致"——实测 `p-r1-04` 只有 r-1/r-3 两人投票(r-2/r-4 未投)却被判"全体一致为真"(2/4)。现改为:**只有当全体在册常驻都投了票(`verdicts` 覆盖全部 resident)才可能判"一致"**;否则进入辩论,或超轮后**保留为未定论(不回写 Verified)**。若某常驻一直忙/未投,则该对象**不会**被记为 Verified·真/假。
436
+
437
+ ### 2. 会议必须全体发言
438
+ `continueMeetingRound` 改为按"是否已发言(`inputs` 覆盖全部 resident)"判定收口,而非按"是否被问过"。**只要还有常驻没发言,会议就继续等它**;`allSolved`(全票 solved→停止)也要求**全员发言且全票 true**。这样分工/停止表决不会被"缺席成员"带偏。
439
+
440
+ ### 3. 背景只在第一轮讲一次(不再每轮重复,省上下文)
441
+ `contextBrief`(背景/使命/工作模式/文件与格式/工具/规则)现在**只在 `brainstormPrompt`(首轮)注入完整版一次**;之后的 normal/meeting/verify/CHECKPOINT 只用**极简的当前状态**(researcher id + 轮次 + 团队成员 + 通知 + JSON 回复),**不再重复那段长背景**,显著减少上下文占用。
442
+
443
+ ### 4. 验证目标按"提出者"精确定位
444
+ `pendingVerify` 记录 `proposer`,`beginVerify` 存入 `targetOwner`;`findSourceRel` **优先在提出者的库里找**源卡(避免同名 id 跨常驻撞车,例如 r-3 的 `p-004` vs r-1 的 `p-r1-04`);验证提示里标注"(提出者 <owner>)"让常驻明确在验哪个对象。
445
+
446
+ ### 5. 主代理放权(减少"主持人"指挥)
447
+ `agent.cordis.yml` persona 明确:**你的角色是让常驻自组织(hands-off)**。不要注入议程/优先级/分工/验证决定,不要指挥常驻;运行 `start` 后只读 `status/report`、在用户明确要求或团队明显僵死时才 `message`/`meeting`,且只做"促成",不做"决定"。
448
+
427
449