dsh-vibe-math 2.3.16 → 2.4.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.
Files changed (39) hide show
  1. package/README.en.md +64 -61
  2. package/README.md +64 -61
  3. package/SECURITY.md +56 -0
  4. package/cordis.patch.yml +1123 -10
  5. package/docs/AUDIT-CHECKLIST.md +499 -384
  6. package/docs/COMPAT-AUDIT-ROUND2.md +3 -0
  7. package/docs/COMPAT-AUDIT-ROUND3.md +139 -0
  8. package/docs/generate_framework_diagram_v5.mjs +27 -27
  9. package/docs/release-notes/RELEASE-NOTES-2.4.0.md +172 -0
  10. package/docs/release-notes/RELEASE-NOTES-2.4.1.md +129 -0
  11. package/docs/test-timing.md +2 -1
  12. package/installer.js +89 -21
  13. package/package.json +36 -10
  14. package/preset-declaration.js +62 -0
  15. package/prompt-corpus-persona/persona-corpus.json +2 -2
  16. package/prompt-corpus-persona/persona-corpus.md +2 -2
  17. package/prompt-corpus-v5/prompt-corpus-v5.json +7 -7
  18. package/prompt-corpus-v5/prompt-corpus-v5.md +8 -129
  19. package/scripts/build-preset-rows.mjs +146 -0
  20. package/tests/audit-installer-compat.test.mjs +9 -3
  21. package/tests/audit-preset-rows.test.mjs +185 -0
  22. package/tests/audit-v5-integrity.mjs +49 -3
  23. package/tests/e2e-v5-round2.test.mjs +137 -67
  24. package/tests/formal-verify-v5.test.mjs +43 -35
  25. package/tests/prompt-v5-integrity.test.mjs +49 -47
  26. package/tests/selfdrive-v5.mjs +42 -55
  27. package/vibe-math-v2/agent.cordis.yml +5 -5
  28. package/vibe-math-v2/preset.yml +1 -1
  29. package/vibe-math-v2/vibe-math-v2.js +111 -64
  30. package/vibe-math-v3/vibe-math-v3.js +213 -150
  31. package/vibe-math-v3//345/256/236/347/216/260/346/226/271/346/241/210.md +2 -2
  32. package/vibe-math-v4/vibe-math-v4.js +257 -34
  33. package/vibe-math-v5/agent.cordis.yml +8 -8
  34. package/vibe-math-v5/preset.yml +1 -1
  35. package/vibe-math-v5/vibe-math-v5.js +292 -129
  36. package/vibe-math-v5//345/256/236/347/216/260/346/226/271/346/241/210.md +56 -44
  37. package/vibe-math-v5//346/236/266/346/236/204/345/233/276.md +24 -22
  38. package//347/244/272/344/276/213/345/233/276//346/241/206/346/236/266/345/233/276-v5-en.svg +14 -14
  39. package//347/244/272/344/276/213/345/233/276//346/241/206/346/236/266/345/233/276-v5.svg +14 -14
@@ -1,5 +1,5 @@
1
- // Vibe Math V2 — host plugin implementing the NEW architecture spec
2
- // ("新架构-vibe-math-实现方案.md"): probability-driven scheduling over a
1
+ // Vibe Math V2 — host plugin implementing the classic-architecture spec
2
+ // ("vibe-math-v2/实现方案.md"): probability-driven scheduling over a
3
3
  // problem list (qs/qs.json) and a proposition knowledge base (Propos/),
4
4
  // multi-agent independent review → debate → consensus verification, with
5
5
  // checkpoint resume, manual intervention, and file/push progress reporting.
@@ -137,16 +137,10 @@ export function apply(ctx) {
137
137
 
138
138
  // ================= helpers =================
139
139
  function textBlock(t) { return { type: 'text', text: String(t) } }
140
- function now() { return Date.now() }
141
- function uuid() { const h = '0123456789abcdef'; let s = ''; for (let i = 0; i < 36; i++) { if (i === 8 || i === 13 || i === 18 || i === 23) s += '-'; else s += h[Math.floor(Math.random() * 16)] } return s }
142
- function shortId() { const h = '0123456789abcdef'; let s = ''; for (let i = 0; i < 8; i++) s += h[Math.floor(Math.random() * 16)]; return s }
143
- function clamp01(v) { const n = Number(v); if (!Number.isFinite(n)) return 0.5; return Math.max(0, Math.min(1, n)) }
144
140
  function workspaceRoot() { try { if (rootAgent && rootAgent.session && rootAgent.session.header && rootAgent.session.header.cwd) return rootAgent.session.header.cwd } catch (e) {} const sp = sandboxPolicyOf(); if (sp && sp.workspaceRoot) return sp.workspaceRoot; return '.' }
145
141
  function vibeRoot() { return (workspaceRoot() + '/VibeMath').replace(/\\/g, '/') }
146
142
  function projectRoot(slug) { return vibeRoot() + '/Projects/' + slug }
147
143
  function frameworkRoot() { return projectRoot(currentProject) }
148
- function slugify(s) { const t = String(s == null ? '' : s).trim().toLowerCase().replace(/[^a-z0-9_\-\u4e00-\u9fa5]+/g, '-').replace(/^-+|-+$/g, ''); return t || 'project' }
149
- function safeId(s) { return String(s == null ? 'anon' : s).replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 80) || 'anon' }
150
144
  let warnedNoPolicy = false
151
145
  function warnNoPolicyOnce() { if (!warnedNoPolicy) { warnedNoPolicy = true; console.error('vibe-math-v2: sandboxPolicy unavailable; writes go out with no explicit policy') } }
152
146
  // Sandbox fence for our own writes. The `resolve({})` fallback is a last resort and is
@@ -155,33 +149,6 @@ export function apply(ctx) {
155
149
  // which is not necessarily this session's workspace — a silently different fence.
156
150
  function getPolicy() { const sp = sandboxPolicyOf(); if (!sp) { warnNoPolicyOnce(); return undefined } try { if (rootAgent && rootAgent.session) return sp.resolve({ session: rootAgent.session }) } catch (e) { warnNoPolicyOnce() } try { const p = sp.resolve({}); if (!warnedNoPolicy) { warnedNoPolicy = true; console.error('vibe-math-v2: falling back to sandboxPolicy.resolve({}) — the fence root is the host-configured workspace, not necessarily this session cwd') } return p } catch (e) { warnNoPolicyOnce() } return undefined }
157
151
  function makeSignal(ms) { return AbortSignal.timeout(ms || 30000) }
158
- function blocksToText(blocks) { if (!blocks) return ''; let out = ''; for (let i = 0; i < blocks.length; i++) { const b = blocks[i]; if (b && b.type === 'text' && typeof b.text === 'string') out += b.text + '\n' } return out.trim() }
159
- function parseJson(text) {
160
- if (typeof text !== 'string') return undefined
161
- const tryObj = function (s) { try { const v = JSON.parse(s); return (v && typeof v === 'object' && !Array.isArray(v)) ? v : undefined } catch (e) { return undefined } }
162
- const fenceRe = /```(?:json)?[ \t]*([\s\S]*?)```/gi
163
- let m
164
- while ((m = fenceRe.exec(text)) !== null) { const obj = tryObj(m[1].trim()); if (obj !== undefined) return obj }
165
- const whole = tryObj(text.trim()); if (whole !== undefined) return whole
166
- let best = undefined; let bestLen = -1
167
- for (let start = 0; start < text.length; start++) {
168
- if (text[start] !== '{') continue
169
- let depth = 0, inStr = false, esc = false, end = -1
170
- for (let i = start; i < text.length; i++) {
171
- const c = text[i]
172
- if (inStr) { if (esc) esc = false; else if (c === '\\') esc = true; else if (c === '"') inStr = false; continue }
173
- if (c === '"') { inStr = true; continue }
174
- if (c === '{') depth++
175
- else if (c === '}') { depth--; if (depth === 0) { end = i; break } }
176
- }
177
- if (end === -1) continue
178
- const obj = tryObj(text.slice(start, end + 1))
179
- if (obj !== undefined && (end - start + 1) > bestLen) { best = obj; bestLen = end - start + 1 }
180
- }
181
- return best
182
- }
183
- function safeJson(v, fb) { if (v == null || v === '') return fb; try { return JSON.parse(v) } catch (e) { return fb } }
184
- function stripJsonComments(text) { let out = ''; let inStr = false; let inLine = false; let inBlock = false; let esc = false; for (let i = 0; i < text.length; i++) { const c = text[i]; const n = text[i + 1]; if (inLine) { if (c === '\n') { inLine = false; out += c } continue } if (inBlock) { if (c === '*' && n === '/') { inBlock = false; i++ } continue } if (inStr) { out += c; if (esc) esc = false; else if (c === '\\') esc = true; else if (c === '"') inStr = false; continue } if (c === '"') { inStr = true; out += c; continue } if (c === '/' && n === '/') { inLine = true; i++; continue } if (c === '/' && n === '*') { inBlock = true; i++; continue } out += c } return out }
185
152
 
186
153
  // ================= parameter schema =================
187
154
  const PARAM_SCHEMA = [
@@ -996,7 +963,6 @@ export function apply(ctx) {
996
963
  L.push('')
997
964
  await writeText('Formal/TODO.md', L.join('\n'))
998
965
  }
999
- function fmtTime(ms) { try { return new Date(Number(ms) || now()).toISOString().replace('T', ' ').slice(0, 19) } catch (e) { return '' } }
1000
966
  async function rebuildLeanLibIndexes() {
1001
967
  // 扫描**不执行**工具链:每次问"有什么可复用"就跑一遍 lean 既慢又出人意料。
1002
968
  // 每个对象的运行结果存在对象记录里,显示在 Formal/Index.md。
@@ -1187,15 +1153,6 @@ export function apply(ctx) {
1187
1153
 
1188
1154
  // progress:结构化 JSON 对象(旧数据可能是 JSON 字符串,两者兼容解析)。
1189
1155
  // 注意:必须保证返回对象含 directions 数组(晋升/判断/子问题等 progress 可能只有来源/说明等字段)。
1190
- function parseProgress(q) {
1191
- const raw = (q && q.progress) || null
1192
- let p = null
1193
- if (raw && typeof raw === 'object' && !Array.isArray(raw)) p = raw
1194
- else p = safeJson(raw, null)
1195
- if (!p || typeof p !== 'object' || Array.isArray(p)) return { directions: [], experience: '' }
1196
- if (!Array.isArray(p.directions)) p.directions = []
1197
- return p
1198
- }
1199
1156
  async function saveProgress(qid, progObj) { const qs = await getQs(); const q = qs.find(function (x) { return x.id === qid }); if (!q) return; q.progress = progObj; await writeQs(qs) }
1200
1157
 
1201
1158
  // ================= data layer: Propos =================
@@ -1322,24 +1279,7 @@ export function apply(ctx) {
1322
1279
  * own rejection message, which lists every registered global tool, so this
1323
1280
  * never guesses. Returns undefined when nothing usable remains.
1324
1281
  */
1325
- function sanitizeToolFilter(filter, known) {
1326
- if (!filter || !(known instanceof Set) || known.size === 0) return filter
1327
- const out = {}
1328
- for (const key of ['allow', 'deny']) {
1329
- const list = filter[key]
1330
- if (!Array.isArray(list)) continue
1331
- const kept = list.filter(function (n) { return known.has(String(n).trim()) })
1332
- if (kept.length > 0) out[key] = kept
1333
- }
1334
- return (out.allow || out.deny) ? out : undefined
1335
- }
1336
1282
  /** The host names the offending tools and then lists the registered ones. */
1337
- function registeredToolsFromError(message) {
1338
- const m = /known global tools:\s*([^]*)$/.exec(String(message || ''))
1339
- if (!m) return undefined
1340
- const names = m[1].split(',').map(function (s) { return s.trim() }).filter(Boolean)
1341
- return names.length > 0 ? new Set(names) : undefined
1342
- }
1343
1283
  function buildToolFilter(role) { const allow = role === 'solver' ? params.solverToolAllow : role === 'verifier' ? params.verifierToolAllow : undefined; const deny = role === 'solver' ? params.solverToolDeny : role === 'verifier' ? params.verifierToolDeny : undefined; const net = role === 'solver' ? params.solverAllowNetwork : role === 'verifier' ? params.verifierAllowNetwork : undefined; const scr = role === 'solver' ? params.solverAllowScripts : role === 'verifier' ? params.verifierAllowScripts : undefined; let a = Array.isArray(allow) ? allow.slice() : []; let d = Array.isArray(deny) ? deny.slice() : []; if (net === false) d = d.concat(NETWORK_TOOLS); else if (net === true && a.length > 0) a = a.concat(NETWORK_TOOLS); if (scr === false) d = d.concat(SCRIPT_TOOLS); else if (scr === true && a.length > 0) a = a.concat(SCRIPT_TOOLS); const f = {}; if (a.length > 0) f.allow = a; if (d.length > 0) f.deny = d; return (f.allow || f.deny) ? f : undefined }
1344
1284
  async function spawnChild(label, promptText, meta) {
1345
1285
  const request = { prompt: [textBlock(promptText)], parent: rootAgent, agentOptions: childAgentOptions() }
@@ -2594,13 +2534,17 @@ export function apply(ctx) {
2594
2534
  input: { hint: '[start|resume|pause|abort|status|report|mode <auto|manual>|setup|save|template [global|project]|add <id> <desc>|add-proposition <id> <概述>|list-propositions|project [list|new <name>|<name>]|decisions|agents]' },
2595
2535
  handler: async function (invocation) {
2596
2536
  const s = getSession(invocation && invocation.agent)
2597
- if (!s) return { kind: 'success', text: JSON.stringify({ ok: false, error: 'no vibe-math session for this agent' }) }
2537
+ if (!s) return { kind: 'error', text: JSON.stringify({ ok: false, error: 'no vibe-math session for this agent' }) }
2598
2538
  const line = String(invocation && invocation.rawInput ? invocation.rawInput : '').trim()
2599
2539
  const parts = line.length > 0 ? line.split(/\s+/) : []
2600
2540
  const cmd = parts[0] || ''
2601
2541
  const rest = parts.slice(1)
2602
2542
  const result = await s.dispatchVibeCommand(cmd, rest)
2603
- return { kind: 'success', text: JSON.stringify(result, null, 2) }
2543
+ // A business failure (the dispatch result's own ok:false) is a FAILED command: the host's
2544
+ // CommandResult union distinguishes success from error, and returning 'success' made a rejected
2545
+ // invocation look identical to a successful one in the UI.
2546
+ const failed = result !== null && typeof result === 'object' && result.ok === false
2547
+ return { kind: failed ? 'error' : 'success', text: JSON.stringify(result, null, 2) }
2604
2548
  },
2605
2549
  }))
2606
2550
 
@@ -2618,3 +2562,106 @@ export function apply(ctx) {
2618
2562
  // tick timer (registered once; ticks every running session at its own pace)
2619
2563
  ctx.effect(() => { const t = setInterval(function () { for (const s of sessions.values()) { if (s.getRunning() && !s.tickInFlight && s.tickDue() && s.scheduler.gate === null) s.scheduleTick() } }, 1000); return () => clearInterval(t) })
2620
2564
  }
2565
+
2566
+ // ---- test seam: pure, stateless helpers --------------------------------
2567
+ // These helpers were declared inside `apply()` and are now declared at module scope, so
2568
+ // `apply()` closes over exactly the same function objects this export hands out. The audit
2569
+ // suites therefore exercise the REAL implementations by importing this module, instead of
2570
+ // extracting source text and compiling function bodies through the Function constructor
2571
+ // (dynamic code execution, rejected by the plugin-catalog security scan as
2572
+ // DANGEROUS_DYNAMIC_EXECUTION).
2573
+ //
2574
+ // Contract: no member may touch `ctx`, session state or mutable module state. Most are pure;
2575
+ // three are deliberately non-deterministic (`uuid`/`shortId` use Math.random, `fmtTime` falls back
2576
+ // to the clock) and `parseProgress` normalises the object it is handed in place (pre-existing).
2577
+ // Nothing here is used by the plugin at runtime except through `apply()`, and behaviour is
2578
+ // byte-identical to the previous in-`apply` declarations.
2579
+ export const __testHelpers = {
2580
+ uuid,
2581
+ shortId,
2582
+ clamp01,
2583
+ slugify,
2584
+ safeId,
2585
+ blocksToText,
2586
+ parseJson,
2587
+ safeJson,
2588
+ stripJsonComments,
2589
+ fmtTime,
2590
+ parseProgress,
2591
+ sanitizeToolFilter,
2592
+ registeredToolsFromError,
2593
+ }
2594
+
2595
+ function now() { return Date.now() }
2596
+
2597
+ function uuid() { const h = '0123456789abcdef'; let s = ''; for (let i = 0; i < 36; i++) { if (i === 8 || i === 13 || i === 18 || i === 23) s += '-'; else s += h[Math.floor(Math.random() * 16)] } return s }
2598
+
2599
+ function shortId() { const h = '0123456789abcdef'; let s = ''; for (let i = 0; i < 8; i++) s += h[Math.floor(Math.random() * 16)]; return s }
2600
+
2601
+ function clamp01(v) { const n = Number(v); if (!Number.isFinite(n)) return 0.5; return Math.max(0, Math.min(1, n)) }
2602
+
2603
+ function slugify(s) { const t = String(s == null ? '' : s).trim().toLowerCase().replace(/[^a-z0-9_\-\u4e00-\u9fa5]+/g, '-').replace(/^-+|-+$/g, ''); return t || 'project' }
2604
+
2605
+ function safeId(s) { return String(s == null ? 'anon' : s).replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 80) || 'anon' }
2606
+
2607
+ function blocksToText(blocks) { if (!blocks) return ''; let out = ''; for (let i = 0; i < blocks.length; i++) { const b = blocks[i]; if (b && b.type === 'text' && typeof b.text === 'string') out += b.text + '\n' } return out.trim() }
2608
+
2609
+ function parseJson(text) {
2610
+ if (typeof text !== 'string') return undefined
2611
+ const tryObj = function (s) { try { const v = JSON.parse(s); return (v && typeof v === 'object' && !Array.isArray(v)) ? v : undefined } catch (e) { return undefined } }
2612
+ const fenceRe = /```(?:json)?[ \t]*([\s\S]*?)```/gi
2613
+ let m
2614
+ while ((m = fenceRe.exec(text)) !== null) { const obj = tryObj(m[1].trim()); if (obj !== undefined) return obj }
2615
+ const whole = tryObj(text.trim()); if (whole !== undefined) return whole
2616
+ let best = undefined; let bestLen = -1
2617
+ for (let start = 0; start < text.length; start++) {
2618
+ if (text[start] !== '{') continue
2619
+ let depth = 0, inStr = false, esc = false, end = -1
2620
+ for (let i = start; i < text.length; i++) {
2621
+ const c = text[i]
2622
+ if (inStr) { if (esc) esc = false; else if (c === '\\') esc = true; else if (c === '"') inStr = false; continue }
2623
+ if (c === '"') { inStr = true; continue }
2624
+ if (c === '{') depth++
2625
+ else if (c === '}') { depth--; if (depth === 0) { end = i; break } }
2626
+ }
2627
+ if (end === -1) continue
2628
+ const obj = tryObj(text.slice(start, end + 1))
2629
+ if (obj !== undefined && (end - start + 1) > bestLen) { best = obj; bestLen = end - start + 1 }
2630
+ }
2631
+ return best
2632
+ }
2633
+
2634
+ function safeJson(v, fb) { if (v == null || v === '') return fb; try { return JSON.parse(v) } catch (e) { return fb } }
2635
+
2636
+ function stripJsonComments(text) { let out = ''; let inStr = false; let inLine = false; let inBlock = false; let esc = false; for (let i = 0; i < text.length; i++) { const c = text[i]; const n = text[i + 1]; if (inLine) { if (c === '\n') { inLine = false; out += c } continue } if (inBlock) { if (c === '*' && n === '/') { inBlock = false; i++ } continue } if (inStr) { out += c; if (esc) esc = false; else if (c === '\\') esc = true; else if (c === '"') inStr = false; continue } if (c === '"') { inStr = true; out += c; continue } if (c === '/' && n === '/') { inLine = true; i++; continue } if (c === '/' && n === '*') { inBlock = true; i++; continue } out += c } return out }
2637
+
2638
+ function fmtTime(ms) { try { return new Date(Number(ms) || now()).toISOString().replace('T', ' ').slice(0, 19) } catch (e) { return '' } }
2639
+
2640
+ function parseProgress(q) {
2641
+ const raw = (q && q.progress) || null
2642
+ let p = null
2643
+ if (raw && typeof raw === 'object' && !Array.isArray(raw)) p = raw
2644
+ else p = safeJson(raw, null)
2645
+ if (!p || typeof p !== 'object' || Array.isArray(p)) return { directions: [], experience: '' }
2646
+ if (!Array.isArray(p.directions)) p.directions = []
2647
+ return p
2648
+ }
2649
+
2650
+ function sanitizeToolFilter(filter, known) {
2651
+ if (!filter || !(known instanceof Set) || known.size === 0) return filter
2652
+ const out = {}
2653
+ for (const key of ['allow', 'deny']) {
2654
+ const list = filter[key]
2655
+ if (!Array.isArray(list)) continue
2656
+ const kept = list.filter(function (n) { return known.has(String(n).trim()) })
2657
+ if (kept.length > 0) out[key] = kept
2658
+ }
2659
+ return (out.allow || out.deny) ? out : undefined
2660
+ }
2661
+
2662
+ function registeredToolsFromError(message) {
2663
+ const m = /known global tools:\s*([^]*)$/.exec(String(message || ''))
2664
+ if (!m) return undefined
2665
+ const names = m[1].split(',').map(function (s) { return s.trim() }).filter(Boolean)
2666
+ return names.length > 0 ? new Set(names) : undefined
2667
+ }