@mindrian_os/install 1.13.0-beta.14 → 1.13.0-beta.17

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 (52) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +15 -0
  3. package/commands/file-meeting.md +2 -0
  4. package/commands/grade.md +2 -0
  5. package/commands/mva-brief.md +56 -0
  6. package/commands/mva-option.md +89 -0
  7. package/commands/new-project.md +2 -0
  8. package/commands/onboard.md +2 -0
  9. package/hooks/hooks.json +9 -0
  10. package/lib/agents/mva/brain-classic-traps.cjs +77 -0
  11. package/lib/agents/mva/brain-cross-domain.cjs +79 -0
  12. package/lib/agents/mva/brain-similar-ventures.cjs +93 -0
  13. package/lib/agents/mva/dashboard-graph-neighborhood.cjs +72 -0
  14. package/lib/agents/mva/index.cjs +42 -0
  15. package/lib/agents/mva/six-hats-red-black.cjs +137 -0
  16. package/lib/agents/mva/tavily-funding-scan.cjs +147 -0
  17. package/lib/agents/mva/test-all-six-agents.cjs +467 -0
  18. package/lib/conversation/operator.cjs +64 -0
  19. package/lib/conversation/operator.test.cjs +160 -0
  20. package/lib/core/cache-prune.cjs +114 -8
  21. package/lib/core/install-state.cjs +242 -0
  22. package/lib/core/mva-agent-contract.cjs +170 -0
  23. package/lib/core/mva-agent-contract.test.cjs +169 -0
  24. package/lib/core/mva-budget.cjs +75 -0
  25. package/lib/core/mva-budget.test.cjs +68 -0
  26. package/lib/core/mva-classifier.cjs +370 -0
  27. package/lib/core/mva-classifier.test.cjs +248 -0
  28. package/lib/core/mva-deck-builder.cjs +452 -0
  29. package/lib/core/mva-deck-builder.test.cjs +287 -0
  30. package/lib/core/mva-detect.smoke.test.cjs +197 -0
  31. package/lib/core/mva-dispatcher.cjs +110 -0
  32. package/lib/core/mva-dispatcher.test.cjs +216 -0
  33. package/lib/core/mva-option-router.cjs +292 -0
  34. package/lib/core/mva-option-router.test.cjs +483 -0
  35. package/lib/core/mva-orchestrator.cjs +324 -0
  36. package/lib/core/mva-orchestrator.test.cjs +908 -0
  37. package/lib/core/mva-progressive-renderer.cjs +194 -0
  38. package/lib/core/mva-progressive-renderer.test.cjs +157 -0
  39. package/lib/core/mva-rule-linter.cjs +213 -0
  40. package/lib/core/mva-rule-linter.test.cjs +336 -0
  41. package/lib/core/mva-state.cjs +159 -0
  42. package/lib/core/mva-telemetry.cjs +170 -0
  43. package/lib/core/mva-telemetry.test.cjs +196 -0
  44. package/lib/core/mva-vercel-deploy.cjs +168 -0
  45. package/lib/core/mva-vercel-deploy.test.cjs +239 -0
  46. package/lib/core/navigation/dashboard-helpers.cjs +145 -0
  47. package/lib/core/navigation.cjs +11 -0
  48. package/lib/core/resolve-vercel-key.cjs +107 -0
  49. package/lib/core/resolve-vercel-key.test.cjs +137 -0
  50. package/lib/memory/run-feynman-tests.cjs +27 -0
  51. package/package.json +1 -1
  52. package/skills/mva-pipeline/SKILL.md +129 -0
@@ -0,0 +1,170 @@
1
+ /*
2
+ * Copyright (c) 2026 Mindrian. BSL 1.1.
3
+ *
4
+ * Phase 118-01 Plan 01 Task 1 -- mva-agent-contract.
5
+ *
6
+ * The AgentResult shape + the runAgent wrapper used by mva-dispatcher.cjs
7
+ * (Task 3 of this plan). Plan 118-02 implements 6 specific agents against
8
+ * the Agent contract documented below.
9
+ *
10
+ * AgentContext interface
11
+ * ----------------------
12
+ * {
13
+ * sentence_sha256: string, // dispatcher-provided -- ONLY identifier passed to agents
14
+ * remaining_budget_ms: number, // dispatcher-provided
15
+ * }
16
+ *
17
+ * raw_sentence is NEVER exposed to agents (Canon Part 8 hard invariant).
18
+ * Agents that need linguistic features must derive them from neighborhood-graph
19
+ * queries against room.db (sentence-sha8 only), NOT from the raw sentence string.
20
+ *
21
+ * process.env.MVA_SENTENCE is NEVER set. There is no escape hatch.
22
+ *
23
+ * Agent function signature
24
+ * ------------------------
25
+ * async function agent(context, signal) -> { status: 'ok'|'empty', payload } | null
26
+ *
27
+ * If the agent throws, runAgent wraps with status='error'.
28
+ * If the AbortSignal fires before resolve, runAgent wraps with status='timeout'.
29
+ * If the agent returns null, runAgent wraps with status='empty'.
30
+ *
31
+ * Pure CJS, node built-ins only, zero new runtime dependencies.
32
+ * Canon Part 8 invariants:
33
+ * - NEVER serialize stack traces (stack traces can contain user content).
34
+ * - Error messages capped at 200 chars (sliced to prevent user-content blow-through).
35
+ * - The AgentContext object passed to the agent contains ONLY documented keys.
36
+ */
37
+ 'use strict';
38
+
39
+ const ALLOWED_STATUSES = new Set(['ok', 'empty', 'error', 'timeout']);
40
+
41
+ /**
42
+ * AGENT_RESULT_SHAPE -- a frozen object documenting the AgentResult shape.
43
+ *
44
+ * AgentResult = {
45
+ * agent_id: string, // 'brain_similar' | ... | 'dashboard_graph'
46
+ * status: 'ok' | 'empty' | 'error' | 'timeout',
47
+ * duration_ms: number, // wall-clock from runAgent invocation
48
+ * payload?: any, // agent-specific; opaque to dispatcher
49
+ * error?: string, // sanitized message (<= 200 chars)
50
+ * };
51
+ */
52
+ const AGENT_RESULT_SHAPE = Object.freeze({
53
+ agent_id: 'string',
54
+ status: "'ok' | 'empty' | 'error' | 'timeout'",
55
+ duration_ms: 'number >= 0',
56
+ payload: 'optional any',
57
+ error: 'optional string'
58
+ });
59
+
60
+ /**
61
+ * validateAgentResult -- returns true if the result conforms to AgentResult shape.
62
+ *
63
+ * @param {unknown} result
64
+ * @returns {boolean}
65
+ */
66
+ function validateAgentResult(result) {
67
+ if (!result || typeof result !== 'object') return false;
68
+ if (typeof result.agent_id !== 'string' || result.agent_id.length === 0) return false;
69
+ if (typeof result.status !== 'string' || !ALLOWED_STATUSES.has(result.status)) return false;
70
+ if (typeof result.duration_ms !== 'number' || result.duration_ms < 0) return false;
71
+ return true;
72
+ }
73
+
74
+ /**
75
+ * Sanitize an error into a short message. Drops stack traces, drops object
76
+ * properties, slices to 200 chars to prevent any user-content blow-through.
77
+ *
78
+ * @param {unknown} err
79
+ * @returns {string}
80
+ */
81
+ function sanitizeError(err) {
82
+ let msg;
83
+ if (err && typeof err === 'object' && typeof err.message === 'string') {
84
+ msg = err.message;
85
+ } else {
86
+ msg = String(err);
87
+ }
88
+ return msg.slice(0, 200);
89
+ }
90
+
91
+ /**
92
+ * runAgent -- invoke an agent function under an AbortController timeout. Catches
93
+ * any throw and converts to a structured AgentResult.
94
+ *
95
+ * @param {{ id: string, fn: Function }} agentDef
96
+ * @param {{ sentence_sha256: string }} context
97
+ * @param {{ timeoutMs?: number }} opts
98
+ * @returns {Promise<{ agent_id: string, status: string, duration_ms: number, payload?: any, error?: string }>}
99
+ */
100
+ async function runAgent(agentDef, context, opts) {
101
+ const timeoutMs = (opts && typeof opts.timeoutMs === 'number') ? opts.timeoutMs : 35000;
102
+ const t0 = Date.now();
103
+ const controller = new AbortController();
104
+
105
+ // Build an AgentContext containing ONLY documented keys. We do not spread
106
+ // arbitrary caller props (defense-in-depth against accidental raw_sentence
107
+ // leakage). Canon Part 8 invariant.
108
+ const agentContext = {
109
+ sentence_sha256: context && context.sentence_sha256,
110
+ remaining_budget_ms: timeoutMs
111
+ };
112
+
113
+ let timer;
114
+ const timeoutPromise = new Promise((_resolve, reject) => {
115
+ timer = setTimeout(() => {
116
+ controller.abort();
117
+ reject(new Error('__mva_dispatcher_timeout__'));
118
+ }, timeoutMs);
119
+ });
120
+
121
+ try {
122
+ const result = await Promise.race([
123
+ Promise.resolve().then(() => agentDef.fn(agentContext, controller.signal)),
124
+ timeoutPromise
125
+ ]);
126
+
127
+ if (timer) clearTimeout(timer);
128
+
129
+ const duration_ms = Date.now() - t0;
130
+
131
+ if (result === null || result === undefined) {
132
+ return { agent_id: agentDef.id, status: 'empty', duration_ms };
133
+ }
134
+
135
+ // Result is { status: 'ok' | 'empty', payload }.
136
+ if (result && typeof result === 'object' && typeof result.status === 'string') {
137
+ if (result.status === 'ok' || result.status === 'empty') {
138
+ return {
139
+ agent_id: agentDef.id,
140
+ status: result.status,
141
+ duration_ms,
142
+ payload: result.payload
143
+ };
144
+ }
145
+ }
146
+
147
+ // Unknown shape -- treat as empty (defense-in-depth; agent contract violation).
148
+ return { agent_id: agentDef.id, status: 'empty', duration_ms };
149
+ } catch (err) {
150
+ if (timer) clearTimeout(timer);
151
+ const duration_ms = Date.now() - t0;
152
+
153
+ if (controller.signal.aborted) {
154
+ return { agent_id: agentDef.id, status: 'timeout', duration_ms };
155
+ }
156
+
157
+ return {
158
+ agent_id: agentDef.id,
159
+ status: 'error',
160
+ duration_ms,
161
+ error: sanitizeError(err)
162
+ };
163
+ }
164
+ }
165
+
166
+ module.exports = {
167
+ runAgent,
168
+ validateAgentResult,
169
+ AGENT_RESULT_SHAPE
170
+ };
@@ -0,0 +1,169 @@
1
+ /*
2
+ * Copyright (c) 2026 Mindrian. BSL 1.1.
3
+ *
4
+ * Phase 118-01 Plan 01 Task 1 -- mva-agent-contract tests.
5
+ *
6
+ * Tests the AgentResult shape, validateAgentResult, and runAgent wrapper.
7
+ * Per Canon Part 8 (Graph Boundary): AgentContext NEVER carries raw sentence;
8
+ * the only sentence-derived field is sentence_sha256. There is no escape hatch
9
+ * via process.env.MVA_SENTENCE.
10
+ *
11
+ * Pure CJS, node built-ins only. Run via `node --test`.
12
+ */
13
+ 'use strict';
14
+
15
+ const test = require('node:test');
16
+ const assert = require('node:assert');
17
+
18
+ const {
19
+ runAgent,
20
+ validateAgentResult,
21
+ AGENT_RESULT_SHAPE
22
+ } = require('./mva-agent-contract.cjs');
23
+
24
+ test('mva-agent-contract: Test 1 -- agent returns ok wraps result', async () => {
25
+ const agentDef = {
26
+ id: 'test',
27
+ fn: async (_ctx, _signal) => ({ status: 'ok', payload: { x: 1 } })
28
+ };
29
+ const result = await runAgent(
30
+ agentDef,
31
+ { sentence_sha256: 'abc123' },
32
+ { timeoutMs: 1000 }
33
+ );
34
+ assert.strictEqual(result.agent_id, 'test');
35
+ assert.strictEqual(result.status, 'ok');
36
+ assert.deepStrictEqual(result.payload, { x: 1 });
37
+ assert.strictEqual(typeof result.duration_ms, 'number');
38
+ assert.ok(result.duration_ms >= 0);
39
+ });
40
+
41
+ test('mva-agent-contract: Test 2 -- agent throws becomes error result', async () => {
42
+ const agentDef = {
43
+ id: 'test',
44
+ fn: async (_ctx, _signal) => { throw new Error('boom'); }
45
+ };
46
+ const result = await runAgent(
47
+ agentDef,
48
+ { sentence_sha256: 'abc123' },
49
+ { timeoutMs: 1000 }
50
+ );
51
+ assert.strictEqual(result.agent_id, 'test');
52
+ assert.strictEqual(result.status, 'error');
53
+ assert.strictEqual(result.error, 'boom');
54
+ assert.ok(!('stack' in result), 'result must never carry stack');
55
+ assert.strictEqual(typeof result.duration_ms, 'number');
56
+ });
57
+
58
+ test('mva-agent-contract: Test 3 -- agent returns null becomes empty', async () => {
59
+ const agentDef = {
60
+ id: 'test',
61
+ fn: async (_ctx, _signal) => null
62
+ };
63
+ const result = await runAgent(
64
+ agentDef,
65
+ { sentence_sha256: 'abc123' },
66
+ { timeoutMs: 1000 }
67
+ );
68
+ assert.strictEqual(result.agent_id, 'test');
69
+ assert.strictEqual(result.status, 'empty');
70
+ assert.strictEqual(typeof result.duration_ms, 'number');
71
+ });
72
+
73
+ test('mva-agent-contract: Test 4 -- agent exceeds timeout returns timeout', async () => {
74
+ const agentDef = {
75
+ id: 'test',
76
+ fn: async (_ctx, signal) => {
77
+ await new Promise((resolve, reject) => {
78
+ const t = setTimeout(resolve, 500);
79
+ signal.addEventListener('abort', () => {
80
+ clearTimeout(t);
81
+ reject(new Error('aborted'));
82
+ });
83
+ });
84
+ return { status: 'ok', payload: {} };
85
+ }
86
+ };
87
+ const t0 = Date.now();
88
+ const result = await runAgent(
89
+ agentDef,
90
+ { sentence_sha256: 'abc123' },
91
+ { timeoutMs: 100 }
92
+ );
93
+ const elapsed = Date.now() - t0;
94
+ assert.strictEqual(result.agent_id, 'test');
95
+ assert.strictEqual(result.status, 'timeout');
96
+ assert.ok(elapsed < 300, `elapsed ${elapsed} must be under 300ms`);
97
+ assert.ok(result.duration_ms >= 90, `duration_ms ${result.duration_ms} should be ~100`);
98
+ });
99
+
100
+ test('mva-agent-contract: Test 5 -- agent receives both args, signal observed', async () => {
101
+ let observedSignalAbortedAtStart = null;
102
+ let observedSignalAbortedAtEnd = null;
103
+ let observedSha = null;
104
+ let observedRemaining = null;
105
+ const agentDef = {
106
+ id: 'test',
107
+ fn: async (ctx, signal) => {
108
+ observedSignalAbortedAtStart = signal.aborted;
109
+ observedSha = ctx.sentence_sha256;
110
+ observedRemaining = ctx.remaining_budget_ms;
111
+ // Wait long enough to be cancelled
112
+ await new Promise((resolve, reject) => {
113
+ const t = setTimeout(resolve, 500);
114
+ signal.addEventListener('abort', () => {
115
+ clearTimeout(t);
116
+ observedSignalAbortedAtEnd = signal.aborted;
117
+ reject(new Error('aborted'));
118
+ });
119
+ });
120
+ return { status: 'ok', payload: {} };
121
+ }
122
+ };
123
+ const result = await runAgent(
124
+ agentDef,
125
+ { sentence_sha256: 'def456' },
126
+ { timeoutMs: 100 }
127
+ );
128
+ assert.strictEqual(observedSignalAbortedAtStart, false, 'signal must start non-aborted');
129
+ assert.strictEqual(observedSignalAbortedAtEnd, true, 'signal must be aborted on timeout');
130
+ assert.strictEqual(observedSha, 'def456');
131
+ assert.strictEqual(observedRemaining, 100);
132
+ assert.strictEqual(result.status, 'timeout');
133
+ });
134
+
135
+ test('mva-agent-contract: Test 6 -- validateAgentResult', () => {
136
+ assert.strictEqual(
137
+ validateAgentResult({ agent_id: 'x', status: 'ok', duration_ms: 10 }),
138
+ true
139
+ );
140
+ assert.strictEqual(
141
+ validateAgentResult({ agent_id: 'x', status: 'empty', duration_ms: 10 }),
142
+ true
143
+ );
144
+ assert.strictEqual(
145
+ validateAgentResult({ agent_id: 'x', status: 'error', duration_ms: 10, error: 'e' }),
146
+ true
147
+ );
148
+ assert.strictEqual(
149
+ validateAgentResult({ agent_id: 'x', status: 'timeout', duration_ms: 10 }),
150
+ true
151
+ );
152
+ assert.strictEqual(validateAgentResult(null), false);
153
+ assert.strictEqual(validateAgentResult({}), false);
154
+ assert.strictEqual(validateAgentResult({ status: 'ok', duration_ms: 10 }), false);
155
+ assert.strictEqual(validateAgentResult({ agent_id: 'x', duration_ms: 10 }), false);
156
+ assert.strictEqual(
157
+ validateAgentResult({ agent_id: 'x', status: 'invalid', duration_ms: 10 }),
158
+ false
159
+ );
160
+ assert.strictEqual(
161
+ validateAgentResult({ agent_id: 'x', status: 'ok', duration_ms: -1 }),
162
+ false
163
+ );
164
+ assert.strictEqual(
165
+ validateAgentResult({ agent_id: 'x', status: 'ok' }),
166
+ false
167
+ );
168
+ assert.ok(AGENT_RESULT_SHAPE, 'shape doc must be exported');
169
+ });
@@ -0,0 +1,75 @@
1
+ /*
2
+ * Copyright (c) 2026 Mindrian. BSL 1.1.
3
+ *
4
+ * Phase 118-01 Plan 01 Task 2 -- mva-budget.
5
+ *
6
+ * Global wall-clock budget tracker used by mva-dispatcher.cjs. Per binding
7
+ * decision B2 (HARD):
8
+ * GLOBAL_BUDGET_MS = 45000 (the hard 30-Second-MVA cap; named for the
9
+ * sharp-question fallback window per source spec
10
+ * line 113 -- "Hard budget: 45 seconds maximum")
11
+ * PER_AGENT_CAP_MS = 35000 (each agent gets at most 35s OR remaining
12
+ * global, whichever is less -- source spec
13
+ * line 123 -- "Agents return structured JSON
14
+ * within 35s budget each")
15
+ *
16
+ * The dispatcher creates ONE budget per dispatch call. Each agent's per-agent
17
+ * abort signal feeds from this budget via perAgentMs(): min(35000, remainingMs()).
18
+ *
19
+ * Pure CJS, leaf module -- no dependencies on any other lib/core file.
20
+ * Canon Part 8: this module touches NO user content; it is wall-clock math only.
21
+ */
22
+ 'use strict';
23
+
24
+ const GLOBAL_BUDGET_MS = 45000;
25
+ const PER_AGENT_CAP_MS = 35000;
26
+
27
+ /**
28
+ * Create a wall-clock budget tracker.
29
+ *
30
+ * @param {number} [globalBudgetMs=45000]
31
+ * @returns {{
32
+ * startedAt: number,
33
+ * globalBudgetMs: number,
34
+ * remainingMs: () => number,
35
+ * perAgentMs: (cap?: number) => number,
36
+ * isExpired: () => boolean,
37
+ * elapsedMs: () => number,
38
+ * }}
39
+ */
40
+ function createBudget(globalBudgetMs) {
41
+ const cap = (typeof globalBudgetMs === 'number') ? globalBudgetMs : GLOBAL_BUDGET_MS;
42
+ const startedAt = Date.now();
43
+
44
+ function remainingMs() {
45
+ return Math.max(0, cap - (Date.now() - startedAt));
46
+ }
47
+
48
+ function perAgentMs(perAgentCapMs) {
49
+ const requested = (typeof perAgentCapMs === 'number') ? perAgentCapMs : PER_AGENT_CAP_MS;
50
+ return Math.max(0, Math.min(requested, remainingMs()));
51
+ }
52
+
53
+ function isExpired() {
54
+ return remainingMs() === 0;
55
+ }
56
+
57
+ function elapsedMs() {
58
+ return Date.now() - startedAt;
59
+ }
60
+
61
+ return {
62
+ startedAt,
63
+ globalBudgetMs: cap,
64
+ remainingMs,
65
+ perAgentMs,
66
+ isExpired,
67
+ elapsedMs
68
+ };
69
+ }
70
+
71
+ module.exports = {
72
+ createBudget,
73
+ GLOBAL_BUDGET_MS,
74
+ PER_AGENT_CAP_MS
75
+ };
@@ -0,0 +1,68 @@
1
+ /*
2
+ * Copyright (c) 2026 Mindrian. BSL 1.1.
3
+ *
4
+ * Phase 118-01 Plan 01 Task 2 -- mva-budget tests.
5
+ *
6
+ * Per binding decision B2 (HARD):
7
+ * 45-second global wall-clock budget + 35-second per-agent budget.
8
+ * Per-agent abort signal feeds from the global deadline: each agent gets
9
+ * min(35s, remaining_global_budget).
10
+ *
11
+ * Pure CJS, node built-ins only. Run via `node --test`.
12
+ */
13
+ 'use strict';
14
+
15
+ const test = require('node:test');
16
+ const assert = require('node:assert');
17
+
18
+ const {
19
+ createBudget,
20
+ GLOBAL_BUDGET_MS,
21
+ PER_AGENT_CAP_MS
22
+ } = require('./mva-budget.cjs');
23
+
24
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
25
+
26
+ test('mva-budget: Test 1 -- remaining decreases over time', async () => {
27
+ const budget = createBudget(45000);
28
+ const r0 = budget.remainingMs();
29
+ assert.ok(r0 >= 44990 && r0 <= 45000, `expected ~45000, got ${r0}`);
30
+ await sleep(100);
31
+ const r1 = budget.remainingMs();
32
+ // Allow generous slop for slow CI / GC pauses.
33
+ assert.ok(r1 >= 44800 && r1 <= 44910, `expected ~44900, got ${r1}`);
34
+ });
35
+
36
+ test('mva-budget: Test 2 -- default globalBudgetMs is 45000', () => {
37
+ const budget = createBudget();
38
+ const r = budget.remainingMs();
39
+ assert.ok(r >= 44990 && r <= 45000, `expected ~45000, got ${r}`);
40
+ assert.strictEqual(budget.globalBudgetMs, 45000);
41
+ });
42
+
43
+ test('mva-budget: Test 3 -- perAgentMs returns 35000 when remaining is 45000', () => {
44
+ const budget = createBudget(45000);
45
+ assert.strictEqual(budget.perAgentMs(35000), 35000);
46
+ });
47
+
48
+ test('mva-budget: Test 4 -- perAgentMs caps at remaining', async () => {
49
+ const budget = createBudget(200);
50
+ await sleep(150);
51
+ const cap = budget.perAgentMs(180);
52
+ // remaining is ~50; perAgentCap 180 -> min(180, 50) = ~50
53
+ assert.ok(cap >= 0 && cap <= 80, `expected ~50, got ${cap}`);
54
+ assert.ok(cap < 180, 'must NOT return 180 when remaining is lower');
55
+ });
56
+
57
+ test('mva-budget: Test 5 -- isExpired flips true after globalBudgetMs', async () => {
58
+ const budget = createBudget(80);
59
+ assert.strictEqual(budget.isExpired(), false);
60
+ await sleep(120);
61
+ assert.strictEqual(budget.isExpired(), true);
62
+ assert.strictEqual(budget.remainingMs(), 0);
63
+ });
64
+
65
+ test('mva-budget: Test 6 -- exported constants', () => {
66
+ assert.strictEqual(GLOBAL_BUDGET_MS, 45000);
67
+ assert.strictEqual(PER_AGENT_CAP_MS, 35000);
68
+ });