dsh-vibe-math 0.3.0 → 0.3.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-vibe-math",
3
3
  "description": "Multi-agent mathematical problem-solving & verification frameworks for DeepSeek Harness — TWO agent presets in one install: vibe-math-v1 (classic pipeline: brainstorm → solver iteration → multi-verifier debate → Verified) and vibe-math-v2 (new probability-driven architecture: qs.json + Propos knowledge base + explorer→solver→review/debate verdict). Installing this bundle auto-installs both presets into the DSH preset root.",
4
- "version": "0.3.0",
4
+ "version": "0.3.1",
5
5
  "type": "module",
6
6
  "main": "installer.js",
7
7
  "exports": {
@@ -64,6 +64,10 @@ export function apply(ctx) {
64
64
  let reportDirty = false
65
65
  let tickInFlight = false
66
66
  let explorerRetries = {}
67
+ // Process epoch: written to state at init; a DIFFERENT persisted epoch means a
68
+ // previous DSH process wrote this state (in-flight children are gone), while an
69
+ // equal epoch means same-process pause→resume (children may still be alive).
70
+ const processEpoch = String(Date.now()) + '-' + Math.random().toString(36).slice(2, 8)
67
71
 
68
72
  // ================= helpers =================
69
73
  function textBlock(t) { return { type: 'text', text: String(t) } }
@@ -374,6 +378,7 @@ export function apply(ctx) {
374
378
  return 'id ' + d.id + '「' + d.title + '」method=' + d.method + ' | round=' + d.round + ' status=' + d.status +
375
379
  ' survival=' + d.survival +
376
380
  (d.routes && d.routes.length ? ' | routes: ' + d.routes.map(function (r) { return r.title + '[' + (r.feasibility_signal || '') + ']' }).join('; ') : '') +
381
+ (d.lessons && d.lessons.length ? ' | lessons: ' + d.lessons.join('; ') : '') +
377
382
  (d.blockers && d.blockers.length ? ' | blockers: ' + d.blockers.join('; ') : '')
378
383
  }
379
384
  function solverPrompt(q, dir, round, progressText) {
@@ -384,11 +389,12 @@ export function apply(ctx) {
384
389
  head += '\nStart from the last recorded node of direction ' + dir.id + ' (inherit progress, or branch a sub-route under it). Each round you MUST produce, even if incomplete:\n' +
385
390
  '- new lemmas / intermediate conclusions WITH full proofs (these go to the Propos/ knowledge base);\n' +
386
391
  '- each concrete sub-route tried, its progress overview, an EXPLICIT feasibility signal (e.g. "unremovable singularity", "conflicts with known theorem X"), and any blocker;\n' +
392
+ '- lessons learned from failed attempts (what to avoid, what did not work and why);\n' +
387
393
  '- an updated survival probability for this direction.\n'
388
394
  head += '\nIf you encounter an EXTREMELY complex auxiliary conjecture/sub-problem q_sub: list it in "sub_questions", TEMPORARILY ASSUME it holds, and continue the main line — every later proposition MUST then be stated as "若 <q_sub 标题> 成立,则:..." so the dependency is explicit.\n'
389
395
  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'
390
396
  head += '\nRespond with ONLY a single JSON object wrapped in a ```json code fence — no prose and no braces { } outside the JSON:\n' +
391
- '{"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":"..."}],"survival_probability":0.5,"dead_end_reason":"... or null","sub_questions":[{"title":"...","statement":"..."}]}'
397
+ '{"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":[{"title":"...","statement":"..."}]}'
392
398
  return head
393
399
  }
394
400
  function verifierReviewPrompt(r) {
@@ -595,7 +601,12 @@ export function apply(ctx) {
595
601
  const ids = Object.keys(tasks)
596
602
  for (let i = 0; i < ids.length; i++) {
597
603
  const t = tasks[ids[i]]
598
- if (t.type !== 'verify' || t.status !== 'spawning') continue
604
+ if (t.type !== 'verify') continue
605
+ if (t.status === 'paused') {
606
+ const allReported = t.children.length > 0 && t.children.every(function (cid) { const r = t.childResults[cid]; return r && r.round === t.round })
607
+ if (allReported) { t.status = 'debating'; await advanceVerification(t, t.round); continue }
608
+ }
609
+ if (t.status !== 'spawning') continue
599
610
  if (scheduler.activeCount >= params.maxParallelThreshold) break
600
611
  await backfillVerifiers(t)
601
612
  }
@@ -665,6 +676,7 @@ export function apply(ctx) {
665
676
  dir.round = meta.round
666
677
  if (parsed) {
667
678
  if (parsed.routes) dir.routes = (dir.routes || []).concat(parsed.routes)
679
+ if (parsed.lessons) dir.lessons = (dir.lessons || []).concat(parsed.lessons)
668
680
  if (parsed.dead_end_reason) dir.dead_end_reason = parsed.dead_end_reason
669
681
  if (typeof parsed.survival_probability === 'number') dir.survival = clamp01(parsed.survival_probability)
670
682
  if (parsed.lemmas && parsed.lemmas.length) { for (let i = 0; i < parsed.lemmas.length; i++) await addLemmaAsProposition(qid, parsed.lemmas[i]) }
@@ -696,14 +708,19 @@ export function apply(ctx) {
696
708
  logActivity('solver', qid + '/' + dirId + ' dead-end: ' + dir.dead_end_reason)
697
709
  } else {
698
710
  const progressText = prog.directions.map(directionSummary).join('\n')
699
- try {
700
- await followupChild(childId, solverPrompt(q, dir, meta.round + 1, progressText))
701
- agentRegistry[childId].round = meta.round + 1
702
- dir.round = meta.round + 1
703
- } catch (e) {
704
- console.error('vibe-math-v2: solver followup failed: ' + String((e && e.message) || e))
705
- dir.status = 'dead-end'; dir.dead_end_reason = dir.dead_end_reason || '求解器续轮失败(followup 异常)'
711
+ if (!scheduler.running) {
712
+ // paused/aborted: stop the follow-up chain; keep the direction active for resume
706
713
  delete agentRegistry[childId]
714
+ } else {
715
+ try {
716
+ await followupChild(childId, solverPrompt(q, dir, meta.round + 1, progressText))
717
+ agentRegistry[childId].round = meta.round + 1
718
+ dir.round = meta.round + 1
719
+ } catch (e) {
720
+ console.error('vibe-math-v2: solver followup failed: ' + String((e && e.message) || e))
721
+ dir.status = 'dead-end'; dir.dead_end_reason = dir.dead_end_reason || '求解器续轮失败(followup 异常)'
722
+ delete agentRegistry[childId]
723
+ }
707
724
  }
708
725
  }
709
726
  await saveProgress(qid, prog)
@@ -764,6 +781,7 @@ export function apply(ctx) {
764
781
  }
765
782
  async function advanceVerification(t, round) {
766
783
  if (round < params.debateMaxRounds && !consensus(t) && t.children.length > 0) {
784
+ if (!scheduler.running) { t.status = 'paused'; return } // resume will re-advance this task
767
785
  t.round = round + 1
768
786
  const transcript = buildTranscript(t)
769
787
  const nextChildren = []
@@ -922,23 +940,29 @@ export function apply(ctx) {
922
940
 
923
941
  // ================= init / control =================
924
942
  async function resolveRootAgent(agent) { if (rootAgent) return rootAgent; if (agent) { rootAgent = agent; return rootAgent } try { const roots = agents.roots ? agents.roots() : []; if (roots && roots.length > 0) { rootAgent = roots[0]; return rootAgent } } catch (e) {} return rootAgent }
925
- async function init(agent) {
943
+ async function init(agent, fresh) {
926
944
  await resolveRootAgent(agent); if (!rootAgent) return { ok: false, message: 'no root agent available' }
927
945
  currentProject = await readCurrentProject(); await ensureDirs()
928
946
  if ((await readJson('qs/qs.json')) === undefined) await writeJson('qs/qs.json', [])
929
947
  params = Object.assign({}, DEFAULT_PARAMS); await loadSettings(); await loadState()
930
- // In-flight children of a previous process are gone after restart: drop stale
931
- // registrations so scheduling is not blocked by phantom entries. Completed work
932
- // already lives in qs.json / Propos / progress; only the interrupted turn is lost.
933
- if (Object.keys(agentRegistry).length > 0 || Object.keys(tasks).length > 0) {
934
- logActivity('resume', 'cleared ' + Object.keys(agentRegistry).length + ' stale agent(s) and ' + Object.keys(tasks).length + ' in-flight task(s) from previous process')
935
- agentRegistry = {}; tasks = {}
948
+ // Distinguish same-process continue from cross-process restart via processEpoch:
949
+ // equal epoch = same process (pause→resume; children may still be alive), different
950
+ // epoch = previous process wrote this state (in-flight children are gone).
951
+ const prevEpoch = await readJson('VibeMath_State/process_epoch.json')
952
+ const stale = typeof prevEpoch === 'string' && prevEpoch !== processEpoch
953
+ if (fresh || stale) {
954
+ if (fresh) { const ids = Object.keys(agentRegistry); for (let i = 0; i < ids.length; i++) await interruptChild(ids[i]) }
955
+ if (Object.keys(agentRegistry).length > 0 || Object.keys(tasks).length > 0) {
956
+ logActivity(fresh ? 'start' : 'resume', 'cleared ' + Object.keys(agentRegistry).length + ' agent(s) and ' + Object.keys(tasks).length + ' task(s) (' + (fresh ? 'restart' : 'stale from previous process') + ')')
957
+ agentRegistry = {}; tasks = {}
958
+ }
936
959
  }
960
+ await writeJson('VibeMath_State/process_epoch.json', processEpoch)
937
961
  scheduler.activeCount = 0; await saveAll()
938
962
  return { ok: true }
939
963
  }
940
- async function startScheduler(agent) { const r = await init(agent); if (!r.ok) return r; scheduler.running = true; scheduler.startedAt = now(); scheduler.gate = null; logActivity('start', 'scheduler started for project ' + currentProject); await saveAll(); await maybeWriteReport(true); scheduleTick(); return { ok: true, message: 'scheduler started', project: currentProject, frameworkRoot: frameworkRoot() } }
941
- async function resumeScheduler(agent) { const r = await init(agent); if (!r.ok) return r; scheduler.running = true; scheduler.gate = null; logActivity('resume', 'scheduler resumed'); await saveAll(); await maybeWriteReport(true); scheduleTick(); return { ok: true, message: 'scheduler resumed', project: currentProject, frameworkRoot: frameworkRoot() } }
964
+ async function startScheduler(agent) { const r = await init(agent, true); if (!r.ok) return r; scheduler.running = true; scheduler.startedAt = now(); scheduler.gate = null; logActivity('start', 'scheduler started for project ' + currentProject); await saveAll(); await maybeWriteReport(true); scheduleTick(); return { ok: true, message: 'scheduler started', project: currentProject, frameworkRoot: frameworkRoot() } }
965
+ async function resumeScheduler(agent) { const r = await init(agent, false); if (!r.ok) return r; scheduler.running = true; scheduler.gate = null; logActivity('resume', 'scheduler resumed'); await saveAll(); await maybeWriteReport(true); scheduleTick(); return { ok: true, message: 'scheduler resumed', project: currentProject, frameworkRoot: frameworkRoot() } }
942
966
  async function pauseScheduler() { scheduler.running = false; logActivity('pause', 'scheduler paused'); await saveAll(); return { ok: true, message: 'scheduler paused' } }
943
967
  async function abortScheduler() { scheduler.running = false; const ids = Object.keys(agentRegistry); for (let i = 0; i < ids.length; i++) await interruptChild(ids[i]); scheduler.activeCount = 0; logActivity('abort', 'scheduler aborted, ' + ids.length + ' child(ren) interrupted'); await saveAll(); return { ok: true, message: 'scheduler aborted', interrupted: ids.length } }
944
968
  async function getStatus() {