@timmeck/brain 3.6.0 → 3.7.0
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/dist/ipc/router.d.ts +18 -0
- package/dist/ipc/router.js +104 -1
- package/dist/ipc/router.js.map +1 -1
- package/dist/mcp/advanced-research-tools.d.ts +7 -0
- package/dist/mcp/advanced-research-tools.js +949 -0
- package/dist/mcp/advanced-research-tools.js.map +1 -0
- package/dist/mcp/http-server.js +5 -1
- package/dist/mcp/http-server.js.map +1 -1
- package/dist/mcp/research-tools.d.ts +7 -0
- package/dist/mcp/research-tools.js +309 -0
- package/dist/mcp/research-tools.js.map +1 -0
- package/dist/mcp/server.js +8 -2
- package/dist/mcp/server.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,949 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
function textResult(data) {
|
|
3
|
+
const text = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
|
|
4
|
+
return { content: [{ type: 'text', text }] };
|
|
5
|
+
}
|
|
6
|
+
/** Register advanced research tools using IPC client (for stdio MCP transport) */
|
|
7
|
+
export function registerAdvancedResearchTools(server, ipc) {
|
|
8
|
+
registerAdvancedResearchToolsWithCaller(server, (method, params) => ipc.request(method, params));
|
|
9
|
+
}
|
|
10
|
+
/** Register advanced research tools using router directly (for HTTP MCP transport inside daemon) */
|
|
11
|
+
export function registerAdvancedResearchToolsDirect(server, router) {
|
|
12
|
+
registerAdvancedResearchToolsWithCaller(server, (method, params) => router.handle(method, params));
|
|
13
|
+
}
|
|
14
|
+
function registerAdvancedResearchToolsWithCaller(server, call) {
|
|
15
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
16
|
+
// Self-Observer
|
|
17
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
18
|
+
server.tool('brain_self_observe', 'Get self-observation statistics: how Brain monitors its own behavior, performance metrics, and operational patterns.', {}, async () => {
|
|
19
|
+
const stats = await call('observer.stats', {});
|
|
20
|
+
const lines = [
|
|
21
|
+
'Self-Observation Statistics:',
|
|
22
|
+
` Total observations: ${stats.totalObservations ?? 0}`,
|
|
23
|
+
` Observation types: ${stats.observationTypes ?? 0}`,
|
|
24
|
+
` Active monitors: ${stats.activeMonitors ?? 0}`,
|
|
25
|
+
` Insights generated: ${stats.insightsGenerated ?? 0}`,
|
|
26
|
+
` Last observation: ${stats.lastObservation ? new Date(stats.lastObservation).toLocaleString() : 'never'}`,
|
|
27
|
+
];
|
|
28
|
+
if (stats.breakdown) {
|
|
29
|
+
lines.push('', ' Breakdown by type:');
|
|
30
|
+
for (const [type, count] of Object.entries(stats.breakdown)) {
|
|
31
|
+
lines.push(` ${type}: ${count}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (stats.healthScore !== undefined) {
|
|
35
|
+
lines.push(` Health score: ${stats.healthScore}/100`);
|
|
36
|
+
}
|
|
37
|
+
return textResult(lines.join('\n'));
|
|
38
|
+
});
|
|
39
|
+
server.tool('brain_self_insights', 'Get self-observation insights: patterns Brain has detected about its own behavior and performance. Filter by type and limit results.', {
|
|
40
|
+
type: z.string().optional().describe('Filter by insight type (e.g., "performance", "pattern", "anomaly")'),
|
|
41
|
+
limit: z.number().optional().describe('Max results (default: 20)'),
|
|
42
|
+
}, async (params) => {
|
|
43
|
+
const insights = await call('observer.insights', {
|
|
44
|
+
type: params.type,
|
|
45
|
+
limit: params.limit ?? 20,
|
|
46
|
+
});
|
|
47
|
+
if (!insights?.length)
|
|
48
|
+
return textResult('No self-observation insights yet. Brain needs more observation cycles.');
|
|
49
|
+
const lines = [`Self-Observation Insights (${insights.length}):\n`];
|
|
50
|
+
for (const i of insights) {
|
|
51
|
+
lines.push(` [${i.type ?? 'insight'}] ${i.title ?? i.description ?? 'Untitled'}`);
|
|
52
|
+
if (i.description && i.title)
|
|
53
|
+
lines.push(` ${i.description.slice(0, 200)}`);
|
|
54
|
+
if (i.confidence !== undefined)
|
|
55
|
+
lines.push(` Confidence: ${(i.confidence * 100).toFixed(0)}%`);
|
|
56
|
+
if (i.created_at)
|
|
57
|
+
lines.push(` Observed: ${new Date(i.created_at).toLocaleString()}`);
|
|
58
|
+
lines.push('');
|
|
59
|
+
}
|
|
60
|
+
return textResult(lines.join('\n'));
|
|
61
|
+
});
|
|
62
|
+
server.tool('brain_self_improvement_plan', 'Get self-improvement plan: actionable suggestions Brain has generated based on observing its own performance patterns.', {}, async () => {
|
|
63
|
+
const plan = await call('observer.plan', {});
|
|
64
|
+
if (!plan)
|
|
65
|
+
return textResult('No improvement plan available yet. Brain needs more observation data.');
|
|
66
|
+
const lines = ['Self-Improvement Plan:'];
|
|
67
|
+
if (plan.summary)
|
|
68
|
+
lines.push(` Summary: ${plan.summary}`);
|
|
69
|
+
if (plan.score !== undefined)
|
|
70
|
+
lines.push(` Current performance score: ${plan.score}/100`);
|
|
71
|
+
if (plan.suggestions?.length) {
|
|
72
|
+
lines.push('', ' Suggestions:');
|
|
73
|
+
for (let idx = 0; idx < plan.suggestions.length; idx++) {
|
|
74
|
+
const s = plan.suggestions[idx];
|
|
75
|
+
lines.push(` ${idx + 1}. ${s.title ?? s.description ?? s}`);
|
|
76
|
+
if (s.impact)
|
|
77
|
+
lines.push(` Impact: ${s.impact}`);
|
|
78
|
+
if (s.effort)
|
|
79
|
+
lines.push(` Effort: ${s.effort}`);
|
|
80
|
+
if (s.priority !== undefined)
|
|
81
|
+
lines.push(` Priority: ${s.priority}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (plan.strengths?.length) {
|
|
85
|
+
lines.push('', ' Strengths:');
|
|
86
|
+
for (const s of plan.strengths) {
|
|
87
|
+
lines.push(` + ${s}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (plan.weaknesses?.length) {
|
|
91
|
+
lines.push('', ' Weaknesses:');
|
|
92
|
+
for (const w of plan.weaknesses) {
|
|
93
|
+
lines.push(` - ${w}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return textResult(lines.join('\n'));
|
|
97
|
+
});
|
|
98
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
99
|
+
// Adaptive Strategy
|
|
100
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
101
|
+
server.tool('brain_strategy_status', 'Get adaptive strategy status: all strategies with their current parameters, performance metrics, and revert rates.', {}, async () => {
|
|
102
|
+
const status = await call('strategy.status', {});
|
|
103
|
+
if (!status)
|
|
104
|
+
return textResult('No adaptive strategies configured.');
|
|
105
|
+
const lines = ['Adaptive Strategy Status:'];
|
|
106
|
+
if (status.totalStrategies !== undefined)
|
|
107
|
+
lines.push(` Total strategies: ${status.totalStrategies}`);
|
|
108
|
+
if (status.activeAdaptations !== undefined)
|
|
109
|
+
lines.push(` Active adaptations: ${status.activeAdaptations}`);
|
|
110
|
+
if (status.revertRate !== undefined)
|
|
111
|
+
lines.push(` Revert rate: ${(status.revertRate * 100).toFixed(1)}%`);
|
|
112
|
+
if (status.strategies?.length) {
|
|
113
|
+
lines.push('', ' Strategies:');
|
|
114
|
+
for (const s of status.strategies) {
|
|
115
|
+
lines.push(` ${s.name ?? s.id}: ${s.status ?? 'active'}`);
|
|
116
|
+
if (s.parameters) {
|
|
117
|
+
for (const [key, val] of Object.entries(s.parameters)) {
|
|
118
|
+
lines.push(` ${key}: ${typeof val === 'number' ? val.toFixed(4) : val}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (s.performance !== undefined)
|
|
122
|
+
lines.push(` Performance: ${(s.performance * 100).toFixed(1)}%`);
|
|
123
|
+
if (s.adaptations !== undefined)
|
|
124
|
+
lines.push(` Adaptations: ${s.adaptations}`);
|
|
125
|
+
if (s.reverts !== undefined)
|
|
126
|
+
lines.push(` Reverts: ${s.reverts}`);
|
|
127
|
+
lines.push('');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return textResult(lines.join('\n'));
|
|
131
|
+
});
|
|
132
|
+
server.tool('brain_strategy_adaptations', 'View adaptation history: past parameter changes made by the adaptive strategy engine. Filter by strategy name.', {
|
|
133
|
+
strategy: z.string().optional().describe('Filter by strategy name'),
|
|
134
|
+
limit: z.number().optional().describe('Max results (default: 20)'),
|
|
135
|
+
}, async (params) => {
|
|
136
|
+
const adaptations = await call('strategy.adaptations', {
|
|
137
|
+
strategy: params.strategy,
|
|
138
|
+
limit: params.limit ?? 20,
|
|
139
|
+
});
|
|
140
|
+
if (!adaptations?.length)
|
|
141
|
+
return textResult('No adaptations recorded yet.');
|
|
142
|
+
const lines = [`Adaptation History (${adaptations.length}):\n`];
|
|
143
|
+
for (const a of adaptations) {
|
|
144
|
+
lines.push(` #${a.id} [${a.strategy ?? 'unknown'}] ${a.parameter ?? 'param'}: ${a.oldValue ?? '?'} -> ${a.newValue ?? '?'}`);
|
|
145
|
+
if (a.reason)
|
|
146
|
+
lines.push(` Reason: ${a.reason}`);
|
|
147
|
+
if (a.reverted)
|
|
148
|
+
lines.push(` REVERTED`);
|
|
149
|
+
if (a.created_at)
|
|
150
|
+
lines.push(` When: ${new Date(a.created_at).toLocaleString()}`);
|
|
151
|
+
lines.push('');
|
|
152
|
+
}
|
|
153
|
+
return textResult(lines.join('\n'));
|
|
154
|
+
});
|
|
155
|
+
server.tool('brain_strategy_revert', 'Revert a specific strategy adaptation. Rolls back a parameter change and records the reason.', {
|
|
156
|
+
id: z.number().describe('Adaptation ID to revert'),
|
|
157
|
+
reason: z.string().optional().describe('Reason for reverting'),
|
|
158
|
+
}, async (params) => {
|
|
159
|
+
const result = await call('strategy.revert', {
|
|
160
|
+
id: params.id,
|
|
161
|
+
reason: params.reason,
|
|
162
|
+
});
|
|
163
|
+
if (!result)
|
|
164
|
+
return textResult(`Adaptation #${params.id} not found or already reverted.`);
|
|
165
|
+
return textResult(`Adaptation #${params.id} reverted.${params.reason ? ` Reason: ${params.reason}` : ''}`);
|
|
166
|
+
});
|
|
167
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
168
|
+
// Experiment Engine
|
|
169
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
170
|
+
server.tool('brain_experiment_list', 'List all experiments. Filter by status to see running, completed, or aborted experiments.', {
|
|
171
|
+
status: z.enum(['proposed', 'running', 'completed', 'aborted']).optional().describe('Filter by experiment status'),
|
|
172
|
+
}, async (params) => {
|
|
173
|
+
const experiments = await call('experiment.list', {
|
|
174
|
+
status: params.status,
|
|
175
|
+
});
|
|
176
|
+
if (!experiments?.length)
|
|
177
|
+
return textResult('No experiments found. Use brain_experiment_propose to create one.');
|
|
178
|
+
const lines = [`Experiments (${experiments.length}):\n`];
|
|
179
|
+
for (const e of experiments) {
|
|
180
|
+
lines.push(` #${e.id} [${(e.status ?? 'unknown').toUpperCase()}] ${e.name}`);
|
|
181
|
+
if (e.hypothesis)
|
|
182
|
+
lines.push(` Hypothesis: ${e.hypothesis.slice(0, 150)}`);
|
|
183
|
+
if (e.progress !== undefined)
|
|
184
|
+
lines.push(` Progress: ${e.progress}%`);
|
|
185
|
+
if (e.created_at)
|
|
186
|
+
lines.push(` Created: ${new Date(e.created_at).toLocaleString()}`);
|
|
187
|
+
lines.push('');
|
|
188
|
+
}
|
|
189
|
+
return textResult(lines.join('\n'));
|
|
190
|
+
});
|
|
191
|
+
server.tool('brain_experiment_propose', 'Propose a new experiment with a hypothesis, independent/dependent variables, and control/treatment values. Brain will run it and collect statistical evidence.', {
|
|
192
|
+
name: z.string().describe('Experiment name'),
|
|
193
|
+
hypothesis: z.string().describe('The hypothesis to test (e.g., "Increasing learning rate improves convergence speed")'),
|
|
194
|
+
independent_variable: z.string().describe('The variable being manipulated (e.g., "learning_rate")'),
|
|
195
|
+
dependent_variable: z.string().describe('The variable being measured (e.g., "convergence_cycles")'),
|
|
196
|
+
control_value: z.string().describe('Control group value (e.g., "0.01")'),
|
|
197
|
+
treatment_value: z.string().describe('Treatment group value (e.g., "0.05")'),
|
|
198
|
+
duration_cycles: z.number().optional().describe('How many cycles to run the experiment (default: determined by engine)'),
|
|
199
|
+
}, async (params) => {
|
|
200
|
+
const result = await call('experiment.propose', {
|
|
201
|
+
name: params.name,
|
|
202
|
+
hypothesis: params.hypothesis,
|
|
203
|
+
independent_variable: params.independent_variable,
|
|
204
|
+
dependent_variable: params.dependent_variable,
|
|
205
|
+
control_value: params.control_value,
|
|
206
|
+
treatment_value: params.treatment_value,
|
|
207
|
+
duration_cycles: params.duration_cycles,
|
|
208
|
+
});
|
|
209
|
+
const lines = [
|
|
210
|
+
`Experiment #${result.id} proposed: "${params.name}"`,
|
|
211
|
+
` Hypothesis: ${params.hypothesis}`,
|
|
212
|
+
` IV: ${params.independent_variable} (control: ${params.control_value}, treatment: ${params.treatment_value})`,
|
|
213
|
+
` DV: ${params.dependent_variable}`,
|
|
214
|
+
];
|
|
215
|
+
if (result.status)
|
|
216
|
+
lines.push(` Status: ${result.status}`);
|
|
217
|
+
if (params.duration_cycles)
|
|
218
|
+
lines.push(` Duration: ${params.duration_cycles} cycles`);
|
|
219
|
+
return textResult(lines.join('\n'));
|
|
220
|
+
});
|
|
221
|
+
server.tool('brain_experiment_status', 'Get detailed status of a specific experiment: progress, collected data points, preliminary results.', {
|
|
222
|
+
id: z.number().describe('Experiment ID'),
|
|
223
|
+
}, async (params) => {
|
|
224
|
+
const exp = await call('experiment.get', { id: params.id });
|
|
225
|
+
if (!exp)
|
|
226
|
+
return textResult(`Experiment #${params.id} not found.`);
|
|
227
|
+
const lines = [
|
|
228
|
+
`Experiment #${exp.id}: ${exp.name}`,
|
|
229
|
+
` Status: ${(exp.status ?? 'unknown').toUpperCase()}`,
|
|
230
|
+
` Hypothesis: ${exp.hypothesis ?? 'N/A'}`,
|
|
231
|
+
` Independent variable: ${exp.independent_variable ?? 'N/A'}`,
|
|
232
|
+
` Dependent variable: ${exp.dependent_variable ?? 'N/A'}`,
|
|
233
|
+
` Control value: ${exp.control_value ?? 'N/A'}`,
|
|
234
|
+
` Treatment value: ${exp.treatment_value ?? 'N/A'}`,
|
|
235
|
+
];
|
|
236
|
+
if (exp.progress !== undefined)
|
|
237
|
+
lines.push(` Progress: ${exp.progress}%`);
|
|
238
|
+
if (exp.data_points !== undefined)
|
|
239
|
+
lines.push(` Data points: ${exp.data_points}`);
|
|
240
|
+
if (exp.duration_cycles !== undefined)
|
|
241
|
+
lines.push(` Duration: ${exp.duration_cycles} cycles`);
|
|
242
|
+
if (exp.current_cycle !== undefined)
|
|
243
|
+
lines.push(` Current cycle: ${exp.current_cycle}`);
|
|
244
|
+
if (exp.preliminary_result) {
|
|
245
|
+
lines.push('', ' Preliminary Result:');
|
|
246
|
+
if (exp.preliminary_result.p_value !== undefined)
|
|
247
|
+
lines.push(` P-value: ${exp.preliminary_result.p_value.toFixed(4)}`);
|
|
248
|
+
if (exp.preliminary_result.effect_size !== undefined)
|
|
249
|
+
lines.push(` Effect size: ${exp.preliminary_result.effect_size.toFixed(4)}`);
|
|
250
|
+
if (exp.preliminary_result.significant !== undefined)
|
|
251
|
+
lines.push(` Significant: ${exp.preliminary_result.significant ? 'YES' : 'NO'}`);
|
|
252
|
+
}
|
|
253
|
+
if (exp.created_at)
|
|
254
|
+
lines.push(` Created: ${new Date(exp.created_at).toLocaleString()}`);
|
|
255
|
+
if (exp.completed_at)
|
|
256
|
+
lines.push(` Completed: ${new Date(exp.completed_at).toLocaleString()}`);
|
|
257
|
+
return textResult(lines.join('\n'));
|
|
258
|
+
});
|
|
259
|
+
server.tool('brain_experiment_results', 'Get results of completed experiments with full statistical analysis: p-values, effect sizes, confidence intervals.', {
|
|
260
|
+
limit: z.number().optional().describe('Max results (default: 10)'),
|
|
261
|
+
}, async (params) => {
|
|
262
|
+
const results = await call('experiment.results', {
|
|
263
|
+
limit: params.limit ?? 10,
|
|
264
|
+
});
|
|
265
|
+
if (!results?.length)
|
|
266
|
+
return textResult('No completed experiment results yet.');
|
|
267
|
+
const lines = [`Experiment Results (${results.length}):\n`];
|
|
268
|
+
for (const r of results) {
|
|
269
|
+
lines.push(` #${r.id} ${r.name ?? 'Unnamed'} [${(r.status ?? 'completed').toUpperCase()}]`);
|
|
270
|
+
if (r.hypothesis)
|
|
271
|
+
lines.push(` Hypothesis: ${r.hypothesis.slice(0, 150)}`);
|
|
272
|
+
if (r.p_value !== undefined)
|
|
273
|
+
lines.push(` P-value: ${r.p_value.toFixed(4)}`);
|
|
274
|
+
if (r.effect_size !== undefined)
|
|
275
|
+
lines.push(` Effect size: ${r.effect_size.toFixed(4)}`);
|
|
276
|
+
if (r.significant !== undefined)
|
|
277
|
+
lines.push(` Significant: ${r.significant ? 'YES' : 'NO'}`);
|
|
278
|
+
if (r.confidence_interval)
|
|
279
|
+
lines.push(` CI: [${r.confidence_interval[0]?.toFixed(4)}, ${r.confidence_interval[1]?.toFixed(4)}]`);
|
|
280
|
+
if (r.conclusion)
|
|
281
|
+
lines.push(` Conclusion: ${r.conclusion}`);
|
|
282
|
+
lines.push('');
|
|
283
|
+
}
|
|
284
|
+
return textResult(lines.join('\n'));
|
|
285
|
+
});
|
|
286
|
+
server.tool('brain_experiment_abort', 'Abort a running experiment. Preserves any data collected so far.', {
|
|
287
|
+
id: z.number().describe('Experiment ID to abort'),
|
|
288
|
+
}, async (params) => {
|
|
289
|
+
const result = await call('experiment.abort', { id: params.id });
|
|
290
|
+
if (!result)
|
|
291
|
+
return textResult(`Experiment #${params.id} not found or not running.`);
|
|
292
|
+
return textResult(`Experiment #${params.id} aborted. Data collected so far is preserved.`);
|
|
293
|
+
});
|
|
294
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
295
|
+
// Cross-Domain
|
|
296
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
297
|
+
server.tool('brain_cross_domain_correlations', 'Find correlations across different domains (errors, trades, posts, etc.). Reveals hidden connections between seemingly unrelated events.', {
|
|
298
|
+
limit: z.number().optional().describe('Max correlations to return (default: 20)'),
|
|
299
|
+
}, async (params) => {
|
|
300
|
+
const correlations = await call('crossdomain.correlations', {
|
|
301
|
+
limit: params.limit ?? 20,
|
|
302
|
+
});
|
|
303
|
+
if (!correlations?.length)
|
|
304
|
+
return textResult('No cross-domain correlations found yet. Need more multi-domain data.');
|
|
305
|
+
const lines = [`Cross-Domain Correlations (${correlations.length}):\n`];
|
|
306
|
+
for (const c of correlations) {
|
|
307
|
+
lines.push(` ${c.domainA ?? c.domain_a ?? '?'} <-> ${c.domainB ?? c.domain_b ?? '?'}`);
|
|
308
|
+
if (c.description)
|
|
309
|
+
lines.push(` ${c.description.slice(0, 200)}`);
|
|
310
|
+
if (c.strength !== undefined)
|
|
311
|
+
lines.push(` Strength: ${(c.strength * 100).toFixed(1)}%`);
|
|
312
|
+
if (c.confidence !== undefined)
|
|
313
|
+
lines.push(` Confidence: ${(c.confidence * 100).toFixed(0)}%`);
|
|
314
|
+
if (c.sample_size !== undefined)
|
|
315
|
+
lines.push(` Sample size: ${c.sample_size}`);
|
|
316
|
+
lines.push('');
|
|
317
|
+
}
|
|
318
|
+
return textResult(lines.join('\n'));
|
|
319
|
+
});
|
|
320
|
+
server.tool('brain_cross_domain_analyze', 'Run a fresh cross-domain analysis. Scans recent events across all domains to discover new correlations and patterns.', {}, async () => {
|
|
321
|
+
const result = await call('crossdomain.analyze', {});
|
|
322
|
+
if (!result)
|
|
323
|
+
return textResult('Cross-domain analysis complete. No new findings.');
|
|
324
|
+
const lines = ['Cross-Domain Analysis Complete:'];
|
|
325
|
+
if (result.correlationsFound !== undefined)
|
|
326
|
+
lines.push(` New correlations found: ${result.correlationsFound}`);
|
|
327
|
+
if (result.patternsDetected !== undefined)
|
|
328
|
+
lines.push(` Patterns detected: ${result.patternsDetected}`);
|
|
329
|
+
if (result.domainsAnalyzed !== undefined)
|
|
330
|
+
lines.push(` Domains analyzed: ${result.domainsAnalyzed}`);
|
|
331
|
+
if (result.eventsProcessed !== undefined)
|
|
332
|
+
lines.push(` Events processed: ${result.eventsProcessed}`);
|
|
333
|
+
if (result.duration !== undefined)
|
|
334
|
+
lines.push(` Duration: ${result.duration}ms`);
|
|
335
|
+
if (result.highlights?.length) {
|
|
336
|
+
lines.push('', ' Highlights:');
|
|
337
|
+
for (const h of result.highlights) {
|
|
338
|
+
lines.push(` - ${h}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return textResult(lines.join('\n'));
|
|
342
|
+
});
|
|
343
|
+
server.tool('brain_cross_domain_narrative', 'Generate a human-readable narrative explaining cross-domain relationships. Tells the story of how different domains interact.', {}, async () => {
|
|
344
|
+
const result = await call('crossdomain.narrative', {});
|
|
345
|
+
if (!result)
|
|
346
|
+
return textResult('No cross-domain narrative available yet. Run an analysis first.');
|
|
347
|
+
if (typeof result === 'string')
|
|
348
|
+
return textResult(result);
|
|
349
|
+
const lines = ['Cross-Domain Narrative:'];
|
|
350
|
+
if (result.narrative)
|
|
351
|
+
lines.push('', result.narrative);
|
|
352
|
+
if (result.summary)
|
|
353
|
+
lines.push('', `Summary: ${result.summary}`);
|
|
354
|
+
if (result.keyFindings?.length) {
|
|
355
|
+
lines.push('', 'Key Findings:');
|
|
356
|
+
for (const f of result.keyFindings) {
|
|
357
|
+
lines.push(` - ${f}`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (result.recommendations?.length) {
|
|
361
|
+
lines.push('', 'Recommendations:');
|
|
362
|
+
for (const r of result.recommendations) {
|
|
363
|
+
lines.push(` - ${r}`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return textResult(lines.join('\n'));
|
|
367
|
+
});
|
|
368
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
369
|
+
// Counterfactual
|
|
370
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
371
|
+
server.tool('brain_what_if', 'Ask a "what if" question: what would have happened if a variable had a different value? Uses counterfactual reasoning based on historical data.', {
|
|
372
|
+
intervention: z.object({
|
|
373
|
+
variable: z.string().describe('The variable to change (e.g., "learning_rate", "retry_count")'),
|
|
374
|
+
actual_value: z.string().describe('The actual value that was used'),
|
|
375
|
+
counterfactual_value: z.string().describe('The hypothetical alternative value'),
|
|
376
|
+
}).describe('The counterfactual intervention'),
|
|
377
|
+
outcome_variable: z.string().describe('The outcome variable to predict (e.g., "error_rate", "success_count")'),
|
|
378
|
+
}, async (params) => {
|
|
379
|
+
const result = await call('counterfactual.whatif', {
|
|
380
|
+
intervention: params.intervention,
|
|
381
|
+
outcome_variable: params.outcome_variable,
|
|
382
|
+
});
|
|
383
|
+
if (!result)
|
|
384
|
+
return textResult('Could not compute counterfactual. Need more historical data.');
|
|
385
|
+
const lines = [
|
|
386
|
+
'What-If Analysis:',
|
|
387
|
+
` Variable: ${params.intervention.variable}`,
|
|
388
|
+
` Actual value: ${params.intervention.actual_value}`,
|
|
389
|
+
` Counterfactual value: ${params.intervention.counterfactual_value}`,
|
|
390
|
+
` Outcome variable: ${params.outcome_variable}`,
|
|
391
|
+
];
|
|
392
|
+
if (result.actual_outcome !== undefined)
|
|
393
|
+
lines.push(` Actual outcome: ${result.actual_outcome}`);
|
|
394
|
+
if (result.predicted_outcome !== undefined)
|
|
395
|
+
lines.push(` Predicted outcome: ${result.predicted_outcome}`);
|
|
396
|
+
if (result.difference !== undefined)
|
|
397
|
+
lines.push(` Difference: ${result.difference}`);
|
|
398
|
+
if (result.confidence !== undefined)
|
|
399
|
+
lines.push(` Confidence: ${(result.confidence * 100).toFixed(0)}%`);
|
|
400
|
+
if (result.explanation)
|
|
401
|
+
lines.push(` Explanation: ${result.explanation}`);
|
|
402
|
+
if (result.caveats?.length) {
|
|
403
|
+
lines.push('', ' Caveats:');
|
|
404
|
+
for (const c of result.caveats) {
|
|
405
|
+
lines.push(` - ${c}`);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return textResult(lines.join('\n'));
|
|
409
|
+
});
|
|
410
|
+
server.tool('brain_counterfactual_history', 'View past counterfactual analyses and their predictions vs actual outcomes.', {
|
|
411
|
+
limit: z.number().optional().describe('Max results (default: 20)'),
|
|
412
|
+
}, async (params) => {
|
|
413
|
+
const history = await call('counterfactual.history', {
|
|
414
|
+
limit: params.limit ?? 20,
|
|
415
|
+
});
|
|
416
|
+
if (!history?.length)
|
|
417
|
+
return textResult('No counterfactual analyses recorded yet.');
|
|
418
|
+
const lines = [`Counterfactual History (${history.length}):\n`];
|
|
419
|
+
for (const h of history) {
|
|
420
|
+
lines.push(` #${h.id} ${h.variable ?? '?'}: ${h.actual_value ?? '?'} -> ${h.counterfactual_value ?? '?'}`);
|
|
421
|
+
if (h.outcome_variable)
|
|
422
|
+
lines.push(` Outcome: ${h.outcome_variable}`);
|
|
423
|
+
if (h.predicted_outcome !== undefined)
|
|
424
|
+
lines.push(` Predicted: ${h.predicted_outcome}`);
|
|
425
|
+
if (h.confidence !== undefined)
|
|
426
|
+
lines.push(` Confidence: ${(h.confidence * 100).toFixed(0)}%`);
|
|
427
|
+
if (h.created_at)
|
|
428
|
+
lines.push(` When: ${new Date(h.created_at).toLocaleString()}`);
|
|
429
|
+
lines.push('');
|
|
430
|
+
}
|
|
431
|
+
return textResult(lines.join('\n'));
|
|
432
|
+
});
|
|
433
|
+
server.tool('brain_intervention_impact', 'Predict the impact of changing a variable to a new value. Estimates the effect on outcomes based on historical counterfactual data.', {
|
|
434
|
+
variable: z.string().describe('The variable to change'),
|
|
435
|
+
proposed_value: z.string().describe('The proposed new value'),
|
|
436
|
+
current_value: z.string().describe('The current value'),
|
|
437
|
+
}, async (params) => {
|
|
438
|
+
const result = await call('counterfactual.impact', {
|
|
439
|
+
variable: params.variable,
|
|
440
|
+
proposed_value: params.proposed_value,
|
|
441
|
+
current_value: params.current_value,
|
|
442
|
+
});
|
|
443
|
+
if (!result)
|
|
444
|
+
return textResult('Could not estimate intervention impact. Need more data.');
|
|
445
|
+
const lines = [
|
|
446
|
+
'Intervention Impact Analysis:',
|
|
447
|
+
` Variable: ${params.variable}`,
|
|
448
|
+
` Current: ${params.current_value} -> Proposed: ${params.proposed_value}`,
|
|
449
|
+
];
|
|
450
|
+
if (result.estimated_impact !== undefined)
|
|
451
|
+
lines.push(` Estimated impact: ${result.estimated_impact}`);
|
|
452
|
+
if (result.risk_level)
|
|
453
|
+
lines.push(` Risk level: ${result.risk_level}`);
|
|
454
|
+
if (result.confidence !== undefined)
|
|
455
|
+
lines.push(` Confidence: ${(result.confidence * 100).toFixed(0)}%`);
|
|
456
|
+
if (result.affected_outcomes?.length) {
|
|
457
|
+
lines.push('', ' Affected outcomes:');
|
|
458
|
+
for (const o of result.affected_outcomes) {
|
|
459
|
+
lines.push(` ${o.variable}: ${o.current ?? '?'} -> ${o.predicted ?? '?'} (${o.direction ?? '?'})`);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
if (result.recommendation)
|
|
463
|
+
lines.push(`\n Recommendation: ${result.recommendation}`);
|
|
464
|
+
return textResult(lines.join('\n'));
|
|
465
|
+
});
|
|
466
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
467
|
+
// Knowledge Distillation
|
|
468
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
469
|
+
server.tool('brain_knowledge_summary', 'Get a summary of all distilled knowledge: principles, anti-patterns, and domain expertise that Brain has extracted from experience.', {}, async () => {
|
|
470
|
+
const summary = await call('knowledge.summary', {});
|
|
471
|
+
if (!summary)
|
|
472
|
+
return textResult('No distilled knowledge yet. Brain needs more experience.');
|
|
473
|
+
const lines = ['Knowledge Summary:'];
|
|
474
|
+
if (summary.totalPrinciples !== undefined)
|
|
475
|
+
lines.push(` Principles: ${summary.totalPrinciples}`);
|
|
476
|
+
if (summary.totalAntiPatterns !== undefined)
|
|
477
|
+
lines.push(` Anti-patterns: ${summary.totalAntiPatterns}`);
|
|
478
|
+
if (summary.domains?.length)
|
|
479
|
+
lines.push(` Domains: ${summary.domains.join(', ')}`);
|
|
480
|
+
if (summary.totalExperience !== undefined)
|
|
481
|
+
lines.push(` Total experience entries: ${summary.totalExperience}`);
|
|
482
|
+
if (summary.lastUpdated)
|
|
483
|
+
lines.push(` Last updated: ${new Date(summary.lastUpdated).toLocaleString()}`);
|
|
484
|
+
if (summary.topPrinciples?.length) {
|
|
485
|
+
lines.push('', ' Top Principles:');
|
|
486
|
+
for (const p of summary.topPrinciples) {
|
|
487
|
+
lines.push(` - ${p.title ?? p.description ?? p}`);
|
|
488
|
+
if (p.confidence !== undefined)
|
|
489
|
+
lines.push(` Confidence: ${(p.confidence * 100).toFixed(0)}%`);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (summary.topAntiPatterns?.length) {
|
|
493
|
+
lines.push('', ' Top Anti-Patterns:');
|
|
494
|
+
for (const a of summary.topAntiPatterns) {
|
|
495
|
+
lines.push(` - ${a.title ?? a.description ?? a}`);
|
|
496
|
+
if (a.severity)
|
|
497
|
+
lines.push(` Severity: ${a.severity}`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return textResult(lines.join('\n'));
|
|
501
|
+
});
|
|
502
|
+
server.tool('brain_knowledge_principles', 'Get distilled principles: proven best practices and strategies that Brain has learned from data. Filter by domain.', {
|
|
503
|
+
domain: z.string().optional().describe('Filter by domain (e.g., "error-handling", "trading", "deployment")'),
|
|
504
|
+
limit: z.number().optional().describe('Max results (default: 20)'),
|
|
505
|
+
}, async (params) => {
|
|
506
|
+
const principles = await call('knowledge.principles', {
|
|
507
|
+
domain: params.domain,
|
|
508
|
+
limit: params.limit ?? 20,
|
|
509
|
+
});
|
|
510
|
+
if (!principles?.length)
|
|
511
|
+
return textResult('No principles distilled yet.');
|
|
512
|
+
const lines = [`Distilled Principles (${principles.length}):\n`];
|
|
513
|
+
for (const p of principles) {
|
|
514
|
+
lines.push(` #${p.id ?? '?'} ${p.title ?? p.name ?? 'Untitled'}`);
|
|
515
|
+
if (p.description)
|
|
516
|
+
lines.push(` ${p.description.slice(0, 200)}`);
|
|
517
|
+
if (p.domain)
|
|
518
|
+
lines.push(` Domain: ${p.domain}`);
|
|
519
|
+
if (p.confidence !== undefined)
|
|
520
|
+
lines.push(` Confidence: ${(p.confidence * 100).toFixed(0)}%`);
|
|
521
|
+
if (p.evidence_count !== undefined)
|
|
522
|
+
lines.push(` Evidence: ${p.evidence_count} observations`);
|
|
523
|
+
lines.push('');
|
|
524
|
+
}
|
|
525
|
+
return textResult(lines.join('\n'));
|
|
526
|
+
});
|
|
527
|
+
server.tool('brain_knowledge_anti_patterns', 'Get distilled anti-patterns: known pitfalls, bad practices, and patterns to avoid. Learned from failures and negative outcomes.', {
|
|
528
|
+
domain: z.string().optional().describe('Filter by domain'),
|
|
529
|
+
limit: z.number().optional().describe('Max results (default: 20)'),
|
|
530
|
+
}, async (params) => {
|
|
531
|
+
const antiPatterns = await call('knowledge.antipatterns', {
|
|
532
|
+
domain: params.domain,
|
|
533
|
+
limit: params.limit ?? 20,
|
|
534
|
+
});
|
|
535
|
+
if (!antiPatterns?.length)
|
|
536
|
+
return textResult('No anti-patterns identified yet.');
|
|
537
|
+
const lines = [`Anti-Patterns (${antiPatterns.length}):\n`];
|
|
538
|
+
for (const a of antiPatterns) {
|
|
539
|
+
lines.push(` #${a.id ?? '?'} ${a.title ?? a.name ?? 'Untitled'}`);
|
|
540
|
+
if (a.description)
|
|
541
|
+
lines.push(` ${a.description.slice(0, 200)}`);
|
|
542
|
+
if (a.domain)
|
|
543
|
+
lines.push(` Domain: ${a.domain}`);
|
|
544
|
+
if (a.severity)
|
|
545
|
+
lines.push(` Severity: ${a.severity}`);
|
|
546
|
+
if (a.occurrences !== undefined)
|
|
547
|
+
lines.push(` Occurrences: ${a.occurrences}`);
|
|
548
|
+
if (a.mitigation)
|
|
549
|
+
lines.push(` Mitigation: ${a.mitigation.slice(0, 150)}`);
|
|
550
|
+
lines.push('');
|
|
551
|
+
}
|
|
552
|
+
return textResult(lines.join('\n'));
|
|
553
|
+
});
|
|
554
|
+
server.tool('brain_knowledge_package', 'Package distilled knowledge for a domain into a transferable format. Useful for sharing learnings between projects or brains.', {
|
|
555
|
+
domain: z.string().describe('Domain to package knowledge for'),
|
|
556
|
+
}, async (params) => {
|
|
557
|
+
const pkg = await call('knowledge.package', {
|
|
558
|
+
domain: params.domain,
|
|
559
|
+
});
|
|
560
|
+
if (!pkg)
|
|
561
|
+
return textResult(`No knowledge available for domain "${params.domain}".`);
|
|
562
|
+
const lines = [`Knowledge Package: ${params.domain}`];
|
|
563
|
+
if (pkg.principles?.length) {
|
|
564
|
+
lines.push(`\n Principles (${pkg.principles.length}):`);
|
|
565
|
+
for (const p of pkg.principles) {
|
|
566
|
+
lines.push(` - ${p.title ?? p.description ?? p}`);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
if (pkg.antiPatterns?.length) {
|
|
570
|
+
lines.push(`\n Anti-Patterns (${pkg.antiPatterns.length}):`);
|
|
571
|
+
for (const a of pkg.antiPatterns) {
|
|
572
|
+
lines.push(` - ${a.title ?? a.description ?? a}`);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
if (pkg.rules?.length) {
|
|
576
|
+
lines.push(`\n Rules (${pkg.rules.length}):`);
|
|
577
|
+
for (const r of pkg.rules) {
|
|
578
|
+
lines.push(` - ${r.pattern ?? r.description ?? r} -> ${r.action ?? ''}`);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
if (pkg.metadata) {
|
|
582
|
+
lines.push('', ' Metadata:');
|
|
583
|
+
if (pkg.metadata.version)
|
|
584
|
+
lines.push(` Version: ${pkg.metadata.version}`);
|
|
585
|
+
if (pkg.metadata.created)
|
|
586
|
+
lines.push(` Created: ${new Date(pkg.metadata.created).toLocaleString()}`);
|
|
587
|
+
if (pkg.metadata.experience_count)
|
|
588
|
+
lines.push(` Experience entries: ${pkg.metadata.experience_count}`);
|
|
589
|
+
}
|
|
590
|
+
return textResult(lines.join('\n'));
|
|
591
|
+
});
|
|
592
|
+
server.tool('brain_knowledge_evolution', 'View how knowledge has evolved over time. Shows how principles and anti-patterns have changed across periods.', {
|
|
593
|
+
domain: z.string().optional().describe('Filter by domain'),
|
|
594
|
+
periods: z.number().optional().describe('Number of time periods to show (default: 5)'),
|
|
595
|
+
}, async (params) => {
|
|
596
|
+
const evolution = await call('knowledge.evolution', {
|
|
597
|
+
domain: params.domain,
|
|
598
|
+
periods: params.periods ?? 5,
|
|
599
|
+
});
|
|
600
|
+
if (!evolution)
|
|
601
|
+
return textResult('No knowledge evolution data available yet.');
|
|
602
|
+
const lines = ['Knowledge Evolution:'];
|
|
603
|
+
if (params.domain)
|
|
604
|
+
lines.push(` Domain: ${params.domain}`);
|
|
605
|
+
if (evolution.periods?.length) {
|
|
606
|
+
for (const p of evolution.periods) {
|
|
607
|
+
lines.push(`\n Period: ${p.label ?? p.start ?? '?'}`);
|
|
608
|
+
if (p.principlesAdded !== undefined)
|
|
609
|
+
lines.push(` Principles added: ${p.principlesAdded}`);
|
|
610
|
+
if (p.principlesRevised !== undefined)
|
|
611
|
+
lines.push(` Principles revised: ${p.principlesRevised}`);
|
|
612
|
+
if (p.antiPatternsAdded !== undefined)
|
|
613
|
+
lines.push(` Anti-patterns added: ${p.antiPatternsAdded}`);
|
|
614
|
+
if (p.knowledgeScore !== undefined)
|
|
615
|
+
lines.push(` Knowledge score: ${p.knowledgeScore}`);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
if (evolution.trend)
|
|
619
|
+
lines.push(`\n Overall trend: ${evolution.trend}`);
|
|
620
|
+
if (evolution.summary)
|
|
621
|
+
lines.push(` Summary: ${evolution.summary}`);
|
|
622
|
+
return textResult(lines.join('\n'));
|
|
623
|
+
});
|
|
624
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
625
|
+
// Research Agenda
|
|
626
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
627
|
+
server.tool('brain_research_agenda', 'View the current research agenda: prioritized list of open research questions and investigations Brain wants to pursue.', {
|
|
628
|
+
limit: z.number().optional().describe('Max items (default: 20)'),
|
|
629
|
+
}, async (params) => {
|
|
630
|
+
const items = await call('agenda.list', {
|
|
631
|
+
limit: params.limit ?? 20,
|
|
632
|
+
});
|
|
633
|
+
if (!items?.length)
|
|
634
|
+
return textResult('Research agenda is empty. Use brain_research_ask to add questions.');
|
|
635
|
+
const lines = [`Research Agenda (${items.length}):\n`];
|
|
636
|
+
for (const item of items) {
|
|
637
|
+
const priority = item.priority !== undefined ? ` (priority: ${(item.priority * 100).toFixed(0)}%)` : '';
|
|
638
|
+
lines.push(` #${item.id} ${item.question ?? item.title ?? 'Untitled'}${priority}`);
|
|
639
|
+
if (item.type)
|
|
640
|
+
lines.push(` Type: ${item.type}`);
|
|
641
|
+
if (item.status)
|
|
642
|
+
lines.push(` Status: ${item.status}`);
|
|
643
|
+
if (item.rationale)
|
|
644
|
+
lines.push(` Rationale: ${item.rationale.slice(0, 150)}`);
|
|
645
|
+
lines.push('');
|
|
646
|
+
}
|
|
647
|
+
return textResult(lines.join('\n'));
|
|
648
|
+
});
|
|
649
|
+
server.tool('brain_research_next', 'Get the next highest-priority research question to investigate. Use this to decide what Brain should focus on next.', {}, async () => {
|
|
650
|
+
const next = await call('agenda.next', {});
|
|
651
|
+
if (!next)
|
|
652
|
+
return textResult('No pending research questions. The agenda is clear.');
|
|
653
|
+
const lines = [
|
|
654
|
+
'Next Research Priority:',
|
|
655
|
+
` #${next.id}: ${next.question ?? next.title ?? 'Untitled'}`,
|
|
656
|
+
];
|
|
657
|
+
if (next.type)
|
|
658
|
+
lines.push(` Type: ${next.type}`);
|
|
659
|
+
if (next.priority !== undefined)
|
|
660
|
+
lines.push(` Priority: ${(next.priority * 100).toFixed(0)}%`);
|
|
661
|
+
if (next.rationale)
|
|
662
|
+
lines.push(` Rationale: ${next.rationale}`);
|
|
663
|
+
if (next.suggested_approach)
|
|
664
|
+
lines.push(` Suggested approach: ${next.suggested_approach}`);
|
|
665
|
+
return textResult(lines.join('\n'));
|
|
666
|
+
});
|
|
667
|
+
server.tool('brain_research_prioritize', 'Change the priority of a research question. Higher priority means it will be investigated sooner.', {
|
|
668
|
+
id: z.number().describe('Research question ID'),
|
|
669
|
+
priority: z.number().min(0).max(1).describe('New priority (0.0 = lowest, 1.0 = highest)'),
|
|
670
|
+
}, async (params) => {
|
|
671
|
+
const result = await call('agenda.prioritize', {
|
|
672
|
+
id: params.id,
|
|
673
|
+
priority: params.priority,
|
|
674
|
+
});
|
|
675
|
+
if (!result)
|
|
676
|
+
return textResult(`Research question #${params.id} not found.`);
|
|
677
|
+
return textResult(`Research question #${params.id} priority set to ${(params.priority * 100).toFixed(0)}%.`);
|
|
678
|
+
});
|
|
679
|
+
server.tool('brain_research_ask', 'Add a new research question to the agenda. Brain will investigate it during research cycles.', {
|
|
680
|
+
question: z.string().describe('The research question to investigate'),
|
|
681
|
+
type: z.string().optional().describe('Question type (e.g., "causal", "optimization", "exploration", "validation")'),
|
|
682
|
+
}, async (params) => {
|
|
683
|
+
const result = await call('agenda.ask', {
|
|
684
|
+
question: params.question,
|
|
685
|
+
type: params.type,
|
|
686
|
+
});
|
|
687
|
+
const lines = [
|
|
688
|
+
`Research question #${result.id} added to agenda.`,
|
|
689
|
+
` Question: ${params.question}`,
|
|
690
|
+
];
|
|
691
|
+
if (params.type)
|
|
692
|
+
lines.push(` Type: ${params.type}`);
|
|
693
|
+
if (result.priority !== undefined)
|
|
694
|
+
lines.push(` Initial priority: ${(result.priority * 100).toFixed(0)}%`);
|
|
695
|
+
return textResult(lines.join('\n'));
|
|
696
|
+
});
|
|
697
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
698
|
+
// Anomaly Detective
|
|
699
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
700
|
+
server.tool('brain_anomalies', 'List detected anomalies: unusual patterns, outliers, and unexpected behaviors that Brain has found. Filter by type.', {
|
|
701
|
+
type: z.string().optional().describe('Filter by anomaly type (e.g., "spike", "drift", "outlier", "pattern_break")'),
|
|
702
|
+
limit: z.number().optional().describe('Max results (default: 20)'),
|
|
703
|
+
}, async (params) => {
|
|
704
|
+
const anomalies = await call('anomaly.list', {
|
|
705
|
+
type: params.type,
|
|
706
|
+
limit: params.limit ?? 20,
|
|
707
|
+
});
|
|
708
|
+
if (!anomalies?.length)
|
|
709
|
+
return textResult('No anomalies detected. Brain is monitoring for unusual patterns.');
|
|
710
|
+
const lines = [`Detected Anomalies (${anomalies.length}):\n`];
|
|
711
|
+
for (const a of anomalies) {
|
|
712
|
+
lines.push(` #${a.id} [${(a.type ?? 'unknown').toUpperCase()}] ${a.title ?? a.description ?? 'Untitled'}`);
|
|
713
|
+
if (a.description && a.title)
|
|
714
|
+
lines.push(` ${a.description.slice(0, 200)}`);
|
|
715
|
+
if (a.severity)
|
|
716
|
+
lines.push(` Severity: ${a.severity}`);
|
|
717
|
+
if (a.confidence !== undefined)
|
|
718
|
+
lines.push(` Confidence: ${(a.confidence * 100).toFixed(0)}%`);
|
|
719
|
+
if (a.metric)
|
|
720
|
+
lines.push(` Metric: ${a.metric}`);
|
|
721
|
+
if (a.expected_value !== undefined && a.actual_value !== undefined) {
|
|
722
|
+
lines.push(` Expected: ${a.expected_value} | Actual: ${a.actual_value}`);
|
|
723
|
+
}
|
|
724
|
+
if (a.detected_at)
|
|
725
|
+
lines.push(` Detected: ${new Date(a.detected_at).toLocaleString()}`);
|
|
726
|
+
lines.push('');
|
|
727
|
+
}
|
|
728
|
+
return textResult(lines.join('\n'));
|
|
729
|
+
});
|
|
730
|
+
server.tool('brain_anomaly_investigate', 'Deep-dive into a specific anomaly: root cause analysis, related events, potential explanations, and recommended actions.', {
|
|
731
|
+
id: z.number().describe('Anomaly ID to investigate'),
|
|
732
|
+
}, async (params) => {
|
|
733
|
+
const result = await call('anomaly.investigate', { id: params.id });
|
|
734
|
+
if (!result)
|
|
735
|
+
return textResult(`Anomaly #${params.id} not found.`);
|
|
736
|
+
const lines = [
|
|
737
|
+
`Anomaly Investigation #${result.id ?? params.id}:`,
|
|
738
|
+
` Type: ${result.type ?? 'unknown'}`,
|
|
739
|
+
` Title: ${result.title ?? 'Untitled'}`,
|
|
740
|
+
];
|
|
741
|
+
if (result.description)
|
|
742
|
+
lines.push(` Description: ${result.description}`);
|
|
743
|
+
if (result.root_cause)
|
|
744
|
+
lines.push(` Root cause: ${result.root_cause}`);
|
|
745
|
+
if (result.severity)
|
|
746
|
+
lines.push(` Severity: ${result.severity}`);
|
|
747
|
+
if (result.confidence !== undefined)
|
|
748
|
+
lines.push(` Confidence: ${(result.confidence * 100).toFixed(0)}%`);
|
|
749
|
+
if (result.related_events?.length) {
|
|
750
|
+
lines.push('', ' Related events:');
|
|
751
|
+
for (const e of result.related_events) {
|
|
752
|
+
lines.push(` - ${e.type ?? e.event ?? e}: ${e.description ?? ''}`);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
if (result.explanations?.length) {
|
|
756
|
+
lines.push('', ' Possible explanations:');
|
|
757
|
+
for (const e of result.explanations) {
|
|
758
|
+
lines.push(` - ${e}`);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
if (result.recommendations?.length) {
|
|
762
|
+
lines.push('', ' Recommended actions:');
|
|
763
|
+
for (const r of result.recommendations) {
|
|
764
|
+
lines.push(` - ${r}`);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return textResult(lines.join('\n'));
|
|
768
|
+
});
|
|
769
|
+
server.tool('brain_anomaly_history', 'View anomaly detection history: past anomalies with their resolution status and outcomes.', {
|
|
770
|
+
limit: z.number().optional().describe('Max results (default: 20)'),
|
|
771
|
+
}, async (params) => {
|
|
772
|
+
const history = await call('anomaly.history', {
|
|
773
|
+
limit: params.limit ?? 20,
|
|
774
|
+
});
|
|
775
|
+
if (!history?.length)
|
|
776
|
+
return textResult('No anomaly history yet.');
|
|
777
|
+
const lines = [`Anomaly History (${history.length}):\n`];
|
|
778
|
+
for (const h of history) {
|
|
779
|
+
const status = h.resolved ? 'RESOLVED' : 'OPEN';
|
|
780
|
+
lines.push(` #${h.id} [${status}] [${h.type ?? '?'}] ${h.title ?? h.description ?? 'Untitled'}`);
|
|
781
|
+
if (h.severity)
|
|
782
|
+
lines.push(` Severity: ${h.severity}`);
|
|
783
|
+
if (h.resolution)
|
|
784
|
+
lines.push(` Resolution: ${h.resolution.slice(0, 150)}`);
|
|
785
|
+
if (h.detected_at)
|
|
786
|
+
lines.push(` Detected: ${new Date(h.detected_at).toLocaleString()}`);
|
|
787
|
+
if (h.resolved_at)
|
|
788
|
+
lines.push(` Resolved: ${new Date(h.resolved_at).toLocaleString()}`);
|
|
789
|
+
lines.push('');
|
|
790
|
+
}
|
|
791
|
+
return textResult(lines.join('\n'));
|
|
792
|
+
});
|
|
793
|
+
server.tool('brain_drift_report', 'Get a drift report: detect gradual changes in metrics, behavior patterns, or data distributions over time.', {}, async () => {
|
|
794
|
+
const report = await call('anomaly.drift', {});
|
|
795
|
+
if (!report)
|
|
796
|
+
return textResult('No drift detected. All metrics are stable.');
|
|
797
|
+
const lines = ['Drift Report:'];
|
|
798
|
+
if (report.status)
|
|
799
|
+
lines.push(` Status: ${report.status}`);
|
|
800
|
+
if (report.totalMetrics !== undefined)
|
|
801
|
+
lines.push(` Metrics monitored: ${report.totalMetrics}`);
|
|
802
|
+
if (report.driftingMetrics !== undefined)
|
|
803
|
+
lines.push(` Drifting metrics: ${report.driftingMetrics}`);
|
|
804
|
+
if (report.stableMetrics !== undefined)
|
|
805
|
+
lines.push(` Stable metrics: ${report.stableMetrics}`);
|
|
806
|
+
if (report.drifts?.length) {
|
|
807
|
+
lines.push('', ' Detected Drifts:');
|
|
808
|
+
for (const d of report.drifts) {
|
|
809
|
+
lines.push(` ${d.metric ?? d.name ?? '?'}: ${d.direction ?? '?'}`);
|
|
810
|
+
if (d.magnitude !== undefined)
|
|
811
|
+
lines.push(` Magnitude: ${d.magnitude}`);
|
|
812
|
+
if (d.baseline !== undefined && d.current !== undefined)
|
|
813
|
+
lines.push(` Baseline: ${d.baseline} -> Current: ${d.current}`);
|
|
814
|
+
if (d.significance !== undefined)
|
|
815
|
+
lines.push(` Significance: ${(d.significance * 100).toFixed(1)}%`);
|
|
816
|
+
if (d.since)
|
|
817
|
+
lines.push(` Since: ${new Date(d.since).toLocaleString()}`);
|
|
818
|
+
lines.push('');
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
if (report.recommendation)
|
|
822
|
+
lines.push(` Recommendation: ${report.recommendation}`);
|
|
823
|
+
return textResult(lines.join('\n'));
|
|
824
|
+
});
|
|
825
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
826
|
+
// Research Journal
|
|
827
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
828
|
+
server.tool('brain_journal', 'Read research journal entries: observations, discoveries, hypotheses, experiments, and reflections. Filter by type.', {
|
|
829
|
+
type: z.string().optional().describe('Filter by entry type (e.g., "observation", "discovery", "hypothesis", "experiment", "reflection")'),
|
|
830
|
+
limit: z.number().optional().describe('Max entries (default: 20)'),
|
|
831
|
+
}, async (params) => {
|
|
832
|
+
const entries = await call('journal.entries', {
|
|
833
|
+
type: params.type,
|
|
834
|
+
limit: params.limit ?? 20,
|
|
835
|
+
});
|
|
836
|
+
if (!entries?.length)
|
|
837
|
+
return textResult('No journal entries yet. Use brain_journal_write to add one.');
|
|
838
|
+
const lines = [`Research Journal (${entries.length} entries):\n`];
|
|
839
|
+
for (const e of entries) {
|
|
840
|
+
const date = e.created_at ? new Date(e.created_at).toLocaleDateString() : '?';
|
|
841
|
+
const sig = e.significance !== undefined ? ` [significance: ${e.significance}]` : '';
|
|
842
|
+
lines.push(` #${e.id} [${(e.type ?? 'entry').toUpperCase()}] ${date} — ${e.title ?? 'Untitled'}${sig}`);
|
|
843
|
+
if (e.content)
|
|
844
|
+
lines.push(` ${e.content.slice(0, 200)}`);
|
|
845
|
+
if (e.tags?.length)
|
|
846
|
+
lines.push(` Tags: ${Array.isArray(e.tags) ? e.tags.join(', ') : e.tags}`);
|
|
847
|
+
lines.push('');
|
|
848
|
+
}
|
|
849
|
+
return textResult(lines.join('\n'));
|
|
850
|
+
});
|
|
851
|
+
server.tool('brain_journal_summary', 'Get a summary of recent research journal activity: key themes, important discoveries, and active investigations.', {
|
|
852
|
+
limit: z.number().optional().describe('Number of recent entries to summarize (default: 50)'),
|
|
853
|
+
}, async (params) => {
|
|
854
|
+
const summary = await call('journal.summary', {
|
|
855
|
+
limit: params.limit ?? 50,
|
|
856
|
+
});
|
|
857
|
+
if (!summary)
|
|
858
|
+
return textResult('No journal data to summarize.');
|
|
859
|
+
if (typeof summary === 'string')
|
|
860
|
+
return textResult(summary);
|
|
861
|
+
const lines = ['Research Journal Summary:'];
|
|
862
|
+
if (summary.totalEntries !== undefined)
|
|
863
|
+
lines.push(` Total entries: ${summary.totalEntries}`);
|
|
864
|
+
if (summary.recentEntries !== undefined)
|
|
865
|
+
lines.push(` Recent entries: ${summary.recentEntries}`);
|
|
866
|
+
if (summary.byType) {
|
|
867
|
+
lines.push('', ' Entries by type:');
|
|
868
|
+
for (const [type, count] of Object.entries(summary.byType)) {
|
|
869
|
+
lines.push(` ${type}: ${count}`);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
if (summary.keyThemes?.length) {
|
|
873
|
+
lines.push('', ' Key themes:');
|
|
874
|
+
for (const t of summary.keyThemes) {
|
|
875
|
+
lines.push(` - ${t}`);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
if (summary.highlights?.length) {
|
|
879
|
+
lines.push('', ' Highlights:');
|
|
880
|
+
for (const h of summary.highlights) {
|
|
881
|
+
lines.push(` - ${h.title ?? h}`);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
if (summary.narrative)
|
|
885
|
+
lines.push(`\n ${summary.narrative}`);
|
|
886
|
+
return textResult(lines.join('\n'));
|
|
887
|
+
});
|
|
888
|
+
server.tool('brain_journal_milestones', 'View research milestones: significant achievements, breakthroughs, and key findings from the journal.', {
|
|
889
|
+
limit: z.number().optional().describe('Max milestones (default: 10)'),
|
|
890
|
+
}, async (params) => {
|
|
891
|
+
const milestones = await call('journal.milestones', {
|
|
892
|
+
limit: params.limit ?? 10,
|
|
893
|
+
});
|
|
894
|
+
if (!milestones?.length)
|
|
895
|
+
return textResult('No milestones recorded yet.');
|
|
896
|
+
const lines = [`Research Milestones (${milestones.length}):\n`];
|
|
897
|
+
for (const m of milestones) {
|
|
898
|
+
const date = m.created_at ? new Date(m.created_at).toLocaleDateString() : '?';
|
|
899
|
+
lines.push(` #${m.id} [${date}] ${m.title ?? 'Untitled'}`);
|
|
900
|
+
if (m.content ?? m.description)
|
|
901
|
+
lines.push(` ${(m.content ?? m.description).slice(0, 200)}`);
|
|
902
|
+
if (m.significance !== undefined)
|
|
903
|
+
lines.push(` Significance: ${m.significance}`);
|
|
904
|
+
if (m.impact)
|
|
905
|
+
lines.push(` Impact: ${m.impact}`);
|
|
906
|
+
lines.push('');
|
|
907
|
+
}
|
|
908
|
+
return textResult(lines.join('\n'));
|
|
909
|
+
});
|
|
910
|
+
server.tool('brain_journal_write', 'Write a new research journal entry. Document observations, discoveries, reflections, or experiment notes.', {
|
|
911
|
+
type: z.enum(['observation', 'discovery', 'hypothesis', 'experiment', 'reflection', 'milestone', 'note']).describe('Entry type'),
|
|
912
|
+
title: z.string().describe('Entry title'),
|
|
913
|
+
content: z.string().describe('Entry content — the full observation, discovery, or reflection'),
|
|
914
|
+
tags: z.array(z.string()).describe('Tags for categorization'),
|
|
915
|
+
significance: z.number().min(1).max(10).describe('Significance level (1 = minor note, 10 = major breakthrough)'),
|
|
916
|
+
}, async (params) => {
|
|
917
|
+
const result = await call('journal.write', {
|
|
918
|
+
type: params.type,
|
|
919
|
+
title: params.title,
|
|
920
|
+
content: params.content,
|
|
921
|
+
tags: params.tags,
|
|
922
|
+
significance: params.significance,
|
|
923
|
+
});
|
|
924
|
+
return textResult(`Journal entry #${result.id} written: [${params.type.toUpperCase()}] "${params.title}" (significance: ${params.significance}/10)`);
|
|
925
|
+
});
|
|
926
|
+
server.tool('brain_journal_search', 'Search the research journal by keyword. Finds entries matching the query across titles, content, and tags.', {
|
|
927
|
+
query: z.string().describe('Search query'),
|
|
928
|
+
limit: z.number().optional().describe('Max results (default: 20)'),
|
|
929
|
+
}, async (params) => {
|
|
930
|
+
const results = await call('journal.search', {
|
|
931
|
+
query: params.query,
|
|
932
|
+
limit: params.limit ?? 20,
|
|
933
|
+
});
|
|
934
|
+
if (!results?.length)
|
|
935
|
+
return textResult(`No journal entries matching "${params.query}".`);
|
|
936
|
+
const lines = [`Journal Search: "${params.query}" (${results.length} results):\n`];
|
|
937
|
+
for (const r of results) {
|
|
938
|
+
const date = r.created_at ? new Date(r.created_at).toLocaleDateString() : '?';
|
|
939
|
+
lines.push(` #${r.id} [${(r.type ?? 'entry').toUpperCase()}] ${date} — ${r.title ?? 'Untitled'}`);
|
|
940
|
+
if (r.content)
|
|
941
|
+
lines.push(` ${r.content.slice(0, 150)}`);
|
|
942
|
+
if (r.tags?.length)
|
|
943
|
+
lines.push(` Tags: ${Array.isArray(r.tags) ? r.tags.join(', ') : r.tags}`);
|
|
944
|
+
lines.push('');
|
|
945
|
+
}
|
|
946
|
+
return textResult(lines.join('\n'));
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
//# sourceMappingURL=advanced-research-tools.js.map
|