@behavior-os/mcp-server 0.0.0-stage → 2.2.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/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/rate-limit.d.ts +64 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +127 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/server.d.ts +28 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +3441 -0
- package/dist/server.js.map +1 -0
- package/package.json +30 -4
- package/README.md +0 -3
package/dist/server.js
ADDED
|
@@ -0,0 +1,3441 @@
|
|
|
1
|
+
import { dirname } from 'node:path';
|
|
2
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
3
|
+
import { BehaviorOS, PROTOCOL_STEPS, resolveAuditRoot } from '@behavior-os/core';
|
|
4
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { rateLimitConfigFromEnv, ToolRateLimiter, withRateLimit } from './rate-limit.js';
|
|
8
|
+
/** Module directory: a stable anchor for relative config paths (R19). */
|
|
9
|
+
const SERVER_DIR = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
// Global behaviorOS instance
|
|
11
|
+
let behaviorOS = null;
|
|
12
|
+
function getBehaviorOS() {
|
|
13
|
+
if (!behaviorOS) {
|
|
14
|
+
behaviorOS = new BehaviorOS({
|
|
15
|
+
dnaPath: process.env.BEHAVIOROS_DNA_PATH,
|
|
16
|
+
// Optional SQLite persistence: set BEHAVIOROS_DB_PATH to a file to keep
|
|
17
|
+
// missions/learnings/agents/audit_log and role/policy state across restarts.
|
|
18
|
+
persistence: process.env.BEHAVIOROS_DB_PATH
|
|
19
|
+
? { path: process.env.BEHAVIOROS_DB_PATH }
|
|
20
|
+
: undefined,
|
|
21
|
+
// Governed code editing is opt-in and dry-run by default; set
|
|
22
|
+
// BEHAVIOROS_CODE_EDIT=true (plus an explicit apply=true per call) to let
|
|
23
|
+
// bos_code_edit rewrite files inside BEHAVIOROS_CODE_EDIT_ROOT.
|
|
24
|
+
codeEdit: {
|
|
25
|
+
enabled: process.env.BEHAVIOROS_CODE_EDIT === 'true',
|
|
26
|
+
root: process.env.BEHAVIOROS_CODE_EDIT_ROOT,
|
|
27
|
+
},
|
|
28
|
+
// DNA evolution is opt-in; with BEHAVIOROS_EVOLUTION=true, bos_evolution
|
|
29
|
+
// / bos_apply_code_fixes with apply:true can write tool-scoped warn
|
|
30
|
+
// rules derived from recorded failure events.
|
|
31
|
+
evolution: process.env.BEHAVIOROS_EVOLUTION === 'true' ? { enabled: true } : undefined,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return behaviorOS;
|
|
35
|
+
}
|
|
36
|
+
// Create MCP server
|
|
37
|
+
export const server = new McpServer({
|
|
38
|
+
name: 'behavioros',
|
|
39
|
+
version: '2.0.0',
|
|
40
|
+
});
|
|
41
|
+
/**
|
|
42
|
+
* Throttle every tool at the registration boundary. Wrapping here (instead of
|
|
43
|
+
* in each handler) means all 30 tools and any future tool get the same limit;
|
|
44
|
+
* the override is local to this server instance and delegates to the original
|
|
45
|
+
* method with a bound receiver.
|
|
46
|
+
*/
|
|
47
|
+
const parsedRateLimit = rateLimitConfigFromEnv();
|
|
48
|
+
for (const warning of parsedRateLimit.warnings) {
|
|
49
|
+
console.error(`[Behavior-OS] ${warning}`);
|
|
50
|
+
}
|
|
51
|
+
export const toolRateLimiter = new ToolRateLimiter(parsedRateLimit.config);
|
|
52
|
+
const registerTool = server.registerTool.bind(server);
|
|
53
|
+
server.registerTool = ((name, config, callback) => registerTool(name, config, withRateLimit(name, callback, toolRateLimiter)));
|
|
54
|
+
/**
|
|
55
|
+
* Exported for tests that need a fresh server instance (e.g. InMemoryTransport
|
|
56
|
+
* contracts). Each call creates a new McpServer so the singleton does not get
|
|
57
|
+
* re-connected across tests.
|
|
58
|
+
*/
|
|
59
|
+
export function createServer() {
|
|
60
|
+
return new McpServer({ name: 'behavioros', version: '2.0.0' });
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Canonical structural shape of a vote, shared by every tool that accepts one
|
|
64
|
+
* (`bos_decide`, `bos_evolution`, `bos_apply_code_fixes`, `bos_code_edit`).
|
|
65
|
+
* Kept in one place so a malformed vote is rejected at the tool boundary
|
|
66
|
+
* instead of reaching the decision engine.
|
|
67
|
+
*/
|
|
68
|
+
export const voteSchema = z.object({
|
|
69
|
+
voterId: z.string(),
|
|
70
|
+
choice: z.enum(['approve', 'reject', 'abstain']),
|
|
71
|
+
weight: z.number().optional().describe('Voting weight (default 1)'),
|
|
72
|
+
reason: z.string().optional(),
|
|
73
|
+
});
|
|
74
|
+
// ============================================================
|
|
75
|
+
// GOVERNANCE TOOLS
|
|
76
|
+
// ============================================================
|
|
77
|
+
server.registerTool('bos_evaluate_governance', {
|
|
78
|
+
description: 'Evaluate governance rules for an action',
|
|
79
|
+
inputSchema: z.object({
|
|
80
|
+
action: z.string().describe('The action to evaluate'),
|
|
81
|
+
agent: z.string().describe('Agent ID performing the action'),
|
|
82
|
+
scope: z.string().optional().describe('Scope of the action'),
|
|
83
|
+
context: z.record(z.string(), z.any()).optional().describe('Additional context'),
|
|
84
|
+
}),
|
|
85
|
+
annotations: {
|
|
86
|
+
destructiveHint: true,
|
|
87
|
+
readOnlyHint: false,
|
|
88
|
+
title: 'Evaluate Governance',
|
|
89
|
+
},
|
|
90
|
+
}, async ({ action, agent, scope, context }) => {
|
|
91
|
+
try {
|
|
92
|
+
const bos = getBehaviorOS();
|
|
93
|
+
// `context` is the condition bag the engine matches rule conditions
|
|
94
|
+
// against, so it must be nested under `context.context` (and always carry
|
|
95
|
+
// the action). Spreading it at the top level silently matched no rules.
|
|
96
|
+
const decision = bos.evaluateGovernance(action, {
|
|
97
|
+
agent,
|
|
98
|
+
scope,
|
|
99
|
+
context: { action, ...(context ?? {}) },
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
content: [{ type: 'text', text: JSON.stringify(decision, null, 2) }],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
return {
|
|
107
|
+
content: [
|
|
108
|
+
{
|
|
109
|
+
type: 'text',
|
|
110
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
isError: true,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
server.registerTool('bos_select_dna', {
|
|
118
|
+
description: 'Select a DNA pattern for the current session',
|
|
119
|
+
inputSchema: z.object({
|
|
120
|
+
pattern: z.string().describe('DNA pattern name (e.g., surgical-team, enterprise-governance)'),
|
|
121
|
+
}),
|
|
122
|
+
annotations: {
|
|
123
|
+
readOnlyHint: true,
|
|
124
|
+
title: 'Select DNA Pattern',
|
|
125
|
+
},
|
|
126
|
+
}, async ({ pattern }) => {
|
|
127
|
+
try {
|
|
128
|
+
const bos = getBehaviorOS();
|
|
129
|
+
// Load predefined DNA patterns
|
|
130
|
+
const dnas = await loadDNAPatterns();
|
|
131
|
+
const dna = dnas[pattern.toLowerCase()];
|
|
132
|
+
if (!dna) {
|
|
133
|
+
return {
|
|
134
|
+
content: [
|
|
135
|
+
{
|
|
136
|
+
type: 'text',
|
|
137
|
+
text: `DNA pattern "${pattern}" not found. Available: ${Object.keys(dnas).join(', ')}`,
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
isError: true,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
await bos.loadDNA(dna);
|
|
144
|
+
return {
|
|
145
|
+
content: [{ type: 'text', text: `DNA pattern "${pattern}" selected successfully.` }],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
return {
|
|
150
|
+
content: [
|
|
151
|
+
{
|
|
152
|
+
type: 'text',
|
|
153
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
isError: true,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
server.registerTool('bos_list_dna', {
|
|
161
|
+
description: 'List all available DNA patterns',
|
|
162
|
+
inputSchema: z.object({}),
|
|
163
|
+
annotations: {
|
|
164
|
+
readOnlyHint: true,
|
|
165
|
+
title: 'List DNA Patterns',
|
|
166
|
+
},
|
|
167
|
+
}, async () => {
|
|
168
|
+
try {
|
|
169
|
+
const bos = getBehaviorOS();
|
|
170
|
+
const dnas = await loadDNAPatterns();
|
|
171
|
+
const patterns = Object.entries(dnas).map(([key, dna]) => ({
|
|
172
|
+
key,
|
|
173
|
+
name: dna.name,
|
|
174
|
+
version: dna.version,
|
|
175
|
+
description: dna.description,
|
|
176
|
+
governanceRules: dna.governance?.length ?? 0,
|
|
177
|
+
}));
|
|
178
|
+
// Mirror the plugin host: listing patterns is step 2 of the 7-step protocol.
|
|
179
|
+
// Without this, MCP callers stall at step 1 after select_dna.
|
|
180
|
+
bos.advanceProtocolStep('bos_list_dna');
|
|
181
|
+
return {
|
|
182
|
+
content: [
|
|
183
|
+
{ type: 'text', text: JSON.stringify({ total: patterns.length, patterns }, null, 2) },
|
|
184
|
+
],
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
return {
|
|
189
|
+
content: [
|
|
190
|
+
{
|
|
191
|
+
type: 'text',
|
|
192
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
isError: true,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
// ============================================================
|
|
200
|
+
// MISSION TOOLS
|
|
201
|
+
// ============================================================
|
|
202
|
+
server.registerTool('bos_create_mission', {
|
|
203
|
+
description: 'Create a new mission',
|
|
204
|
+
inputSchema: z.object({
|
|
205
|
+
title: z.string().describe('Mission title'),
|
|
206
|
+
type: z
|
|
207
|
+
.enum(['feature', 'bugfix', 'deployment', 'research', 'refactor', 'audit', 'incident'])
|
|
208
|
+
.describe('Mission type'),
|
|
209
|
+
priority: z.enum(['critical', 'high', 'medium', 'low']).describe('Priority level'),
|
|
210
|
+
requiredChecks: z
|
|
211
|
+
.array(z.string().trim().min(1))
|
|
212
|
+
.optional()
|
|
213
|
+
.describe('Gates that must each have passing evidence before completion'),
|
|
214
|
+
}),
|
|
215
|
+
annotations: {
|
|
216
|
+
title: 'Create Mission',
|
|
217
|
+
},
|
|
218
|
+
}, async ({ title, type, priority, requiredChecks }) => {
|
|
219
|
+
try {
|
|
220
|
+
const bos = getBehaviorOS();
|
|
221
|
+
const mission = bos.createMission({
|
|
222
|
+
title,
|
|
223
|
+
type: type,
|
|
224
|
+
priority: priority,
|
|
225
|
+
requiredChecks,
|
|
226
|
+
});
|
|
227
|
+
return {
|
|
228
|
+
content: [{ type: 'text', text: JSON.stringify(mission, null, 2) }],
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
return {
|
|
233
|
+
content: [
|
|
234
|
+
{
|
|
235
|
+
type: 'text',
|
|
236
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
isError: true,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
server.registerTool('bos_start_mission', {
|
|
244
|
+
description: 'Start a mission by ID',
|
|
245
|
+
inputSchema: z.object({
|
|
246
|
+
missionId: z.string().describe('Mission ID'),
|
|
247
|
+
expectedVersion: z
|
|
248
|
+
.number()
|
|
249
|
+
.int()
|
|
250
|
+
.nonnegative()
|
|
251
|
+
.optional()
|
|
252
|
+
.describe('Reject the transition if the mission version differs'),
|
|
253
|
+
actor: z.string().optional().describe('Actor recorded in the transition history'),
|
|
254
|
+
}),
|
|
255
|
+
annotations: {
|
|
256
|
+
title: 'Start Mission',
|
|
257
|
+
},
|
|
258
|
+
}, async ({ missionId, expectedVersion, actor }) => {
|
|
259
|
+
try {
|
|
260
|
+
const bos = getBehaviorOS();
|
|
261
|
+
const result = bos.transitionMission(missionId, 'active', { expectedVersion, actor });
|
|
262
|
+
if (!result.ok) {
|
|
263
|
+
return {
|
|
264
|
+
content: [
|
|
265
|
+
{
|
|
266
|
+
type: 'text',
|
|
267
|
+
text: JSON.stringify({
|
|
268
|
+
ok: false,
|
|
269
|
+
code: result.code,
|
|
270
|
+
message: result.message,
|
|
271
|
+
missionId,
|
|
272
|
+
status: result.mission?.status ?? null,
|
|
273
|
+
}, null, 2),
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
isError: true,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
if (result.changed) {
|
|
280
|
+
bos.advanceProtocolStep('bos_start_mission');
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
content: [
|
|
284
|
+
{
|
|
285
|
+
type: 'text',
|
|
286
|
+
text: `Mission "${missionId}" started. Status: ${result.mission.status}`,
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
return {
|
|
293
|
+
content: [
|
|
294
|
+
{
|
|
295
|
+
type: 'text',
|
|
296
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
isError: true,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
server.registerTool('bos_complete_mission', {
|
|
304
|
+
description: 'Complete a mission only when every required check has passing, non-simulated evidence. Refuses invalid transitions, stale versions and missing/failed evidence.',
|
|
305
|
+
inputSchema: z.object({
|
|
306
|
+
missionId: z.string().describe('Mission ID'),
|
|
307
|
+
evidence: z
|
|
308
|
+
.array(z.object({
|
|
309
|
+
gate: z.string().describe('Gate id; must match a requiredChecks entry'),
|
|
310
|
+
status: z.enum(['passed', 'failed', 'skipped']),
|
|
311
|
+
source: z.string().describe('Command, tool or report that produced the evidence'),
|
|
312
|
+
detail: z.string().optional(),
|
|
313
|
+
simulated: z
|
|
314
|
+
.boolean()
|
|
315
|
+
.optional()
|
|
316
|
+
.describe('true marks a simulated check; it never satisfies a required gate'),
|
|
317
|
+
provenance: z
|
|
318
|
+
.enum(['executed', 'reported', 'simulated'])
|
|
319
|
+
.optional()
|
|
320
|
+
.describe('How the evidence was produced; executed requires executionId'),
|
|
321
|
+
executionId: z
|
|
322
|
+
.string()
|
|
323
|
+
.optional()
|
|
324
|
+
.describe('Unique id of the execution that produced executed evidence'),
|
|
325
|
+
}))
|
|
326
|
+
.optional()
|
|
327
|
+
.describe('Evidence to record with the transition'),
|
|
328
|
+
expectedVersion: z.number().int().nonnegative().optional(),
|
|
329
|
+
actor: z.string().optional().describe('Who authorized the transition'),
|
|
330
|
+
}),
|
|
331
|
+
annotations: {
|
|
332
|
+
title: 'Complete Mission',
|
|
333
|
+
},
|
|
334
|
+
}, async ({ missionId, evidence, expectedVersion, actor }) => {
|
|
335
|
+
try {
|
|
336
|
+
const bos = getBehaviorOS();
|
|
337
|
+
const result = bos.transitionMission(missionId, 'completed', {
|
|
338
|
+
evidence,
|
|
339
|
+
expectedVersion,
|
|
340
|
+
actor,
|
|
341
|
+
});
|
|
342
|
+
if (!result.ok) {
|
|
343
|
+
return {
|
|
344
|
+
content: [
|
|
345
|
+
{
|
|
346
|
+
type: 'text',
|
|
347
|
+
text: JSON.stringify({
|
|
348
|
+
ok: false,
|
|
349
|
+
code: result.code,
|
|
350
|
+
message: result.message,
|
|
351
|
+
missionId,
|
|
352
|
+
status: result.mission?.status ?? null,
|
|
353
|
+
}, null, 2),
|
|
354
|
+
},
|
|
355
|
+
],
|
|
356
|
+
isError: true,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
content: [
|
|
361
|
+
{
|
|
362
|
+
type: 'text',
|
|
363
|
+
text: JSON.stringify({
|
|
364
|
+
ok: true,
|
|
365
|
+
changed: result.changed,
|
|
366
|
+
missionId,
|
|
367
|
+
status: result.mission.status,
|
|
368
|
+
version: result.mission.version ?? 0,
|
|
369
|
+
completionBasis: result.mission.completionBasis ?? null,
|
|
370
|
+
evidenceVerified: result.mission.completionBasis === 'evidence',
|
|
371
|
+
}, null, 2),
|
|
372
|
+
},
|
|
373
|
+
],
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
catch (error) {
|
|
377
|
+
return {
|
|
378
|
+
content: [
|
|
379
|
+
{
|
|
380
|
+
type: 'text',
|
|
381
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
382
|
+
},
|
|
383
|
+
],
|
|
384
|
+
isError: true,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
server.registerTool('bos_fail_mission', {
|
|
389
|
+
description: 'Mark a mission as failed with an explicit reason and audit trail.',
|
|
390
|
+
inputSchema: z.object({
|
|
391
|
+
missionId: z.string().describe('Mission ID'),
|
|
392
|
+
reason: z.string().optional().describe('Why the mission failed'),
|
|
393
|
+
expectedVersion: z.number().int().nonnegative().optional(),
|
|
394
|
+
actor: z.string().optional(),
|
|
395
|
+
}),
|
|
396
|
+
annotations: {
|
|
397
|
+
title: 'Fail Mission',
|
|
398
|
+
},
|
|
399
|
+
}, async ({ missionId, reason, expectedVersion, actor }) => {
|
|
400
|
+
try {
|
|
401
|
+
const bos = getBehaviorOS();
|
|
402
|
+
const result = bos.transitionMission(missionId, 'failed', {
|
|
403
|
+
reason,
|
|
404
|
+
expectedVersion,
|
|
405
|
+
actor,
|
|
406
|
+
});
|
|
407
|
+
if (!result.ok) {
|
|
408
|
+
return {
|
|
409
|
+
content: [
|
|
410
|
+
{
|
|
411
|
+
type: 'text',
|
|
412
|
+
text: JSON.stringify({
|
|
413
|
+
ok: false,
|
|
414
|
+
code: result.code,
|
|
415
|
+
message: result.message,
|
|
416
|
+
missionId,
|
|
417
|
+
status: result.mission?.status ?? null,
|
|
418
|
+
}, null, 2),
|
|
419
|
+
},
|
|
420
|
+
],
|
|
421
|
+
isError: true,
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
return {
|
|
425
|
+
content: [
|
|
426
|
+
{
|
|
427
|
+
type: 'text',
|
|
428
|
+
text: JSON.stringify({
|
|
429
|
+
ok: true,
|
|
430
|
+
changed: result.changed,
|
|
431
|
+
missionId,
|
|
432
|
+
status: result.mission.status,
|
|
433
|
+
version: result.mission.version ?? 0,
|
|
434
|
+
reason: result.mission.lastTransitionReason ?? null,
|
|
435
|
+
}, null, 2),
|
|
436
|
+
},
|
|
437
|
+
],
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
return {
|
|
442
|
+
content: [
|
|
443
|
+
{
|
|
444
|
+
type: 'text',
|
|
445
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
446
|
+
},
|
|
447
|
+
],
|
|
448
|
+
isError: true,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
server.registerTool('bos_cancel_mission', {
|
|
453
|
+
description: 'Cancel a mission that will not be completed, recording the reason.',
|
|
454
|
+
inputSchema: z.object({
|
|
455
|
+
missionId: z.string().describe('Mission ID'),
|
|
456
|
+
reason: z.string().optional().describe('Why the mission was cancelled'),
|
|
457
|
+
expectedVersion: z.number().int().nonnegative().optional(),
|
|
458
|
+
actor: z.string().optional(),
|
|
459
|
+
}),
|
|
460
|
+
annotations: {
|
|
461
|
+
title: 'Cancel Mission',
|
|
462
|
+
},
|
|
463
|
+
}, async ({ missionId, reason, expectedVersion, actor }) => {
|
|
464
|
+
try {
|
|
465
|
+
const bos = getBehaviorOS();
|
|
466
|
+
const result = bos.transitionMission(missionId, 'cancelled', {
|
|
467
|
+
reason,
|
|
468
|
+
expectedVersion,
|
|
469
|
+
actor,
|
|
470
|
+
});
|
|
471
|
+
if (!result.ok) {
|
|
472
|
+
return {
|
|
473
|
+
content: [
|
|
474
|
+
{
|
|
475
|
+
type: 'text',
|
|
476
|
+
text: JSON.stringify({
|
|
477
|
+
ok: false,
|
|
478
|
+
code: result.code,
|
|
479
|
+
message: result.message,
|
|
480
|
+
missionId,
|
|
481
|
+
status: result.mission?.status ?? null,
|
|
482
|
+
}, null, 2),
|
|
483
|
+
},
|
|
484
|
+
],
|
|
485
|
+
isError: true,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
return {
|
|
489
|
+
content: [
|
|
490
|
+
{
|
|
491
|
+
type: 'text',
|
|
492
|
+
text: JSON.stringify({
|
|
493
|
+
ok: true,
|
|
494
|
+
changed: result.changed,
|
|
495
|
+
missionId,
|
|
496
|
+
status: result.mission.status,
|
|
497
|
+
version: result.mission.version ?? 0,
|
|
498
|
+
reason: result.mission.lastTransitionReason ?? null,
|
|
499
|
+
}, null, 2),
|
|
500
|
+
},
|
|
501
|
+
],
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
catch (error) {
|
|
505
|
+
return {
|
|
506
|
+
content: [
|
|
507
|
+
{
|
|
508
|
+
type: 'text',
|
|
509
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
510
|
+
},
|
|
511
|
+
],
|
|
512
|
+
isError: true,
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
server.registerTool('bos_list_missions', {
|
|
517
|
+
description: 'List all missions with optional filters',
|
|
518
|
+
inputSchema: z.object({
|
|
519
|
+
status: z
|
|
520
|
+
.enum(['pending', 'active', 'paused', 'completed', 'failed', 'cancelled'])
|
|
521
|
+
.optional()
|
|
522
|
+
.describe('Filter by status'),
|
|
523
|
+
type: z.string().optional().describe('Filter by type'),
|
|
524
|
+
priority: z.string().optional().describe('Filter by priority'),
|
|
525
|
+
limit: z.number().optional().describe('Maximum number of missions to return'),
|
|
526
|
+
}),
|
|
527
|
+
annotations: {
|
|
528
|
+
readOnlyHint: true,
|
|
529
|
+
title: 'List Missions',
|
|
530
|
+
},
|
|
531
|
+
}, async ({ status, type, priority, limit = 50, }) => {
|
|
532
|
+
try {
|
|
533
|
+
const bos = getBehaviorOS();
|
|
534
|
+
// The facade owns the filter semantics (applied BEFORE the limit) so the
|
|
535
|
+
// MCP tool and the plugin tool cannot drift apart.
|
|
536
|
+
const missions = bos.listMissions(limit, {
|
|
537
|
+
status: status,
|
|
538
|
+
type: type,
|
|
539
|
+
priority: priority,
|
|
540
|
+
});
|
|
541
|
+
return {
|
|
542
|
+
content: [{ type: 'text', text: JSON.stringify(missions, null, 2) }],
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
catch (error) {
|
|
546
|
+
return {
|
|
547
|
+
content: [
|
|
548
|
+
{
|
|
549
|
+
type: 'text',
|
|
550
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
551
|
+
},
|
|
552
|
+
],
|
|
553
|
+
isError: true,
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
server.registerTool('bos_set_agent', {
|
|
558
|
+
description: 'Set the governed agent identity for this session (from a registered agent id, or a persona defined by the active DNA). Used to attribute audits/governance to a role instead of a model name.',
|
|
559
|
+
inputSchema: z.object({
|
|
560
|
+
id: z.string().optional().describe('Registered agent ID'),
|
|
561
|
+
name: z.string().optional().describe('Agent name (registers the agent when the id is new)'),
|
|
562
|
+
type: z
|
|
563
|
+
.enum(['orchestrator', 'developer', 'architect', 'reviewer', 'operator', 'analyst'])
|
|
564
|
+
.optional()
|
|
565
|
+
.describe('Agent type (defaults to operator)'),
|
|
566
|
+
capabilities: z.array(z.string()).optional().describe('Agent capabilities'),
|
|
567
|
+
persona: z
|
|
568
|
+
.string()
|
|
569
|
+
.optional()
|
|
570
|
+
.describe('Persona role/name from the active DNA (e.g. "architect")'),
|
|
571
|
+
}),
|
|
572
|
+
annotations: {
|
|
573
|
+
title: 'Set Agent',
|
|
574
|
+
},
|
|
575
|
+
}, async (input) => {
|
|
576
|
+
try {
|
|
577
|
+
const bos = getBehaviorOS();
|
|
578
|
+
const identity = bos.setActiveAgent(input);
|
|
579
|
+
if (!identity) {
|
|
580
|
+
const personas = bos.getDNA()?.personas.map((p) => p.role) ?? [];
|
|
581
|
+
return {
|
|
582
|
+
content: [
|
|
583
|
+
{
|
|
584
|
+
type: 'text',
|
|
585
|
+
text: `Error: no agent or persona matched. Available personas: ${personas.length ? personas.join(', ') : '(none — select a DNA first)'}`,
|
|
586
|
+
},
|
|
587
|
+
],
|
|
588
|
+
isError: true,
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
return {
|
|
592
|
+
content: [{ type: 'text', text: JSON.stringify(identity, null, 2) }],
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
catch (error) {
|
|
596
|
+
return {
|
|
597
|
+
content: [
|
|
598
|
+
{
|
|
599
|
+
type: 'text',
|
|
600
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
601
|
+
},
|
|
602
|
+
],
|
|
603
|
+
isError: true,
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
server.registerTool('bos_whoami', {
|
|
608
|
+
description: 'Resolve the governed agent identity acting in this session (agent id, DNA pattern, persona, source) plus the personas/agents that can be selected',
|
|
609
|
+
inputSchema: z.object({}),
|
|
610
|
+
annotations: {
|
|
611
|
+
readOnlyHint: true,
|
|
612
|
+
title: 'Who Am I',
|
|
613
|
+
},
|
|
614
|
+
}, async () => {
|
|
615
|
+
try {
|
|
616
|
+
const bos = getBehaviorOS();
|
|
617
|
+
const identity = bos.resolveAgentIdentity();
|
|
618
|
+
const payload = {
|
|
619
|
+
...identity,
|
|
620
|
+
personas: bos.getDNA()?.personas.map((p) => p.role) ?? [],
|
|
621
|
+
registeredAgents: bos.listAgents().map((a) => ({ id: a.id, name: a.name, type: a.type })),
|
|
622
|
+
};
|
|
623
|
+
return {
|
|
624
|
+
content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }],
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
catch (error) {
|
|
628
|
+
return {
|
|
629
|
+
content: [
|
|
630
|
+
{
|
|
631
|
+
type: 'text',
|
|
632
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
633
|
+
},
|
|
634
|
+
],
|
|
635
|
+
isError: true,
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
// ============================================================
|
|
640
|
+
// QUALITY TOOLS
|
|
641
|
+
// ============================================================
|
|
642
|
+
server.registerTool('bos_run_audit', {
|
|
643
|
+
description: 'Run a full audit pipeline for a mission. agentId is optional and defaults to the session identity resolved from the active DNA (persona or registered agent) — never the model name.',
|
|
644
|
+
inputSchema: z.object({
|
|
645
|
+
missionId: z.string().describe('Mission ID'),
|
|
646
|
+
agentId: z
|
|
647
|
+
.string()
|
|
648
|
+
.optional()
|
|
649
|
+
.describe('Explicit agent ID (defaults to the DNA-derived identity)'),
|
|
650
|
+
stages: z.array(z.string()).optional().describe('Stages to audit'),
|
|
651
|
+
simulate: z
|
|
652
|
+
.boolean()
|
|
653
|
+
.optional()
|
|
654
|
+
.describe('true (default) runs a simulated audit and never reports a pass; false executes the real checks'),
|
|
655
|
+
}),
|
|
656
|
+
annotations: {
|
|
657
|
+
title: 'Run Audit',
|
|
658
|
+
},
|
|
659
|
+
}, async ({ missionId, agentId, stages, simulate, }) => {
|
|
660
|
+
try {
|
|
661
|
+
const bos = getBehaviorOS();
|
|
662
|
+
const report = await bos.runAudit({
|
|
663
|
+
missionId,
|
|
664
|
+
agentId,
|
|
665
|
+
stages,
|
|
666
|
+
simulate,
|
|
667
|
+
// A stdio MCP client can launch this server from any cwd; resolve the
|
|
668
|
+
// workspace from the environment the way the plugin does, so real
|
|
669
|
+
// stages never run outside the project (false red audits).
|
|
670
|
+
root: resolveAuditRoot(process.env, SERVER_DIR),
|
|
671
|
+
});
|
|
672
|
+
return {
|
|
673
|
+
content: [{ type: 'text', text: JSON.stringify(report, null, 2) }],
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
catch (error) {
|
|
677
|
+
return {
|
|
678
|
+
content: [
|
|
679
|
+
{
|
|
680
|
+
type: 'text',
|
|
681
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
682
|
+
},
|
|
683
|
+
],
|
|
684
|
+
isError: true,
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
server.registerTool('bos_check_quality', {
|
|
689
|
+
description: 'Check quality metrics against governance gates',
|
|
690
|
+
inputSchema: z.object({
|
|
691
|
+
metrics: z
|
|
692
|
+
.record(z.string(), z.number())
|
|
693
|
+
.describe('Quality metrics (coverage, lint_errors, etc.)'),
|
|
694
|
+
}),
|
|
695
|
+
annotations: {
|
|
696
|
+
readOnlyHint: true,
|
|
697
|
+
title: 'Check Quality',
|
|
698
|
+
},
|
|
699
|
+
}, async ({ metrics }) => {
|
|
700
|
+
try {
|
|
701
|
+
const bos = getBehaviorOS();
|
|
702
|
+
const result = bos.checkQuality(metrics);
|
|
703
|
+
return {
|
|
704
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
catch (error) {
|
|
708
|
+
return {
|
|
709
|
+
content: [
|
|
710
|
+
{
|
|
711
|
+
type: 'text',
|
|
712
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
713
|
+
},
|
|
714
|
+
],
|
|
715
|
+
isError: true,
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
// ============================================================
|
|
720
|
+
// PRAGMATIC VALUE ASSESSMENT TOOLS (Feature 10: Validação Pragmática)
|
|
721
|
+
// ============================================================
|
|
722
|
+
server.registerTool('bos_assess_pragmatic_value', {
|
|
723
|
+
description: 'Assess the pragmatic value of code changes: net lines removed, complexity change, test coverage change',
|
|
724
|
+
inputSchema: z.object({
|
|
725
|
+
changes: z
|
|
726
|
+
.array(z.object({
|
|
727
|
+
addedLines: z.number().min(0).describe('Lines added'),
|
|
728
|
+
removedLines: z.number().min(0).describe('Lines removed'),
|
|
729
|
+
}))
|
|
730
|
+
.describe('Code changes to assess'),
|
|
731
|
+
metrics: z
|
|
732
|
+
.object({
|
|
733
|
+
before: z
|
|
734
|
+
.object({
|
|
735
|
+
complexity: z.number().describe('Cyclomatic complexity before changes'),
|
|
736
|
+
testCoverage: z.number().describe('Test coverage percentage before'),
|
|
737
|
+
rulesCount: z.number().describe('Business rules count before'),
|
|
738
|
+
})
|
|
739
|
+
.passthrough(),
|
|
740
|
+
after: z
|
|
741
|
+
.object({
|
|
742
|
+
complexity: z.number().describe('Cyclomatic complexity after changes'),
|
|
743
|
+
testCoverage: z.number().describe('Test coverage percentage after'),
|
|
744
|
+
rulesCount: z.number().describe('Business rules count after'),
|
|
745
|
+
})
|
|
746
|
+
.passthrough(),
|
|
747
|
+
})
|
|
748
|
+
.describe('Metrics before and after the changes'),
|
|
749
|
+
}),
|
|
750
|
+
annotations: { readOnlyHint: true, title: 'Assess Pragmatic Value' },
|
|
751
|
+
}, async ({ changes, metrics }) => {
|
|
752
|
+
try {
|
|
753
|
+
const bos = getBehaviorOS();
|
|
754
|
+
const result = bos.assessPragmaticValue(changes, metrics);
|
|
755
|
+
return {
|
|
756
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
catch (error) {
|
|
760
|
+
return {
|
|
761
|
+
content: [
|
|
762
|
+
{
|
|
763
|
+
type: 'text',
|
|
764
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
765
|
+
},
|
|
766
|
+
],
|
|
767
|
+
isError: true,
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
// ============================================================
|
|
772
|
+
// PIPELINE TOOLS
|
|
773
|
+
// ============================================================
|
|
774
|
+
server.registerTool('bos_preflight', {
|
|
775
|
+
description: 'Synchronously pre-flight an action against the gate pipeline; blocks before execution',
|
|
776
|
+
inputSchema: z.object({
|
|
777
|
+
action: z.string().describe('Action to pre-flight, e.g. "deploy"'),
|
|
778
|
+
agentId: z.string().describe('Agent ID'),
|
|
779
|
+
context: z
|
|
780
|
+
.record(z.string(), z.unknown())
|
|
781
|
+
.optional()
|
|
782
|
+
.describe('Extra condition flags (e.g. { production: true })'),
|
|
783
|
+
}),
|
|
784
|
+
annotations: {
|
|
785
|
+
readOnlyHint: true,
|
|
786
|
+
title: 'Pre-flight Action',
|
|
787
|
+
},
|
|
788
|
+
}, async ({ action, agentId, context, }) => {
|
|
789
|
+
try {
|
|
790
|
+
const bos = getBehaviorOS();
|
|
791
|
+
const result = bos.preflightAction(action, { agent: agentId, context });
|
|
792
|
+
return {
|
|
793
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
catch (error) {
|
|
797
|
+
return {
|
|
798
|
+
content: [
|
|
799
|
+
{
|
|
800
|
+
type: 'text',
|
|
801
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
802
|
+
},
|
|
803
|
+
],
|
|
804
|
+
isError: true,
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
// ============================================================
|
|
809
|
+
// DECISION TOOLS
|
|
810
|
+
// ============================================================
|
|
811
|
+
server.registerTool('bos_decide', {
|
|
812
|
+
description: 'Resolve a subject by weighted vote (majority/supermajority/unanimous + quorum)',
|
|
813
|
+
inputSchema: z.object({
|
|
814
|
+
subject: z.string().describe('What is being decided'),
|
|
815
|
+
votes: z.array(voteSchema).describe('The votes to tally'),
|
|
816
|
+
policy: z
|
|
817
|
+
.enum(['majority', 'supermajority', 'unanimous'])
|
|
818
|
+
.optional()
|
|
819
|
+
.describe('Decision policy (default majority)'),
|
|
820
|
+
quorum: z.number().optional().describe('Minimum participating weight (default 1)'),
|
|
821
|
+
missionId: z.string().optional().describe('Related mission ID'),
|
|
822
|
+
}),
|
|
823
|
+
annotations: {
|
|
824
|
+
title: 'Decide',
|
|
825
|
+
},
|
|
826
|
+
}, async ({ subject, votes, policy, quorum, missionId, }) => {
|
|
827
|
+
try {
|
|
828
|
+
const bos = getBehaviorOS();
|
|
829
|
+
const decision = bos.decide({ subject, votes, policy, quorum, missionId });
|
|
830
|
+
return {
|
|
831
|
+
content: [{ type: 'text', text: JSON.stringify(decision, null, 2) }],
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
catch (error) {
|
|
835
|
+
return {
|
|
836
|
+
content: [
|
|
837
|
+
{
|
|
838
|
+
type: 'text',
|
|
839
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
840
|
+
},
|
|
841
|
+
],
|
|
842
|
+
isError: true,
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
// ============================================================
|
|
847
|
+
// EVOLUTION TOOLS
|
|
848
|
+
// ============================================================
|
|
849
|
+
server.registerTool('bos_evolution', {
|
|
850
|
+
description: 'Propose (and optionally apply) learning-driven DNA changes, scored by confidence',
|
|
851
|
+
inputSchema: z.object({
|
|
852
|
+
minConfidence: z
|
|
853
|
+
.number()
|
|
854
|
+
.optional()
|
|
855
|
+
.describe('Minimum confidence in [0,1] for a proposal (default 0.8)'),
|
|
856
|
+
apply: z
|
|
857
|
+
.boolean()
|
|
858
|
+
.optional()
|
|
859
|
+
.describe('Write proposals into the active DNA (requires evolution.enabled)'),
|
|
860
|
+
votes: z
|
|
861
|
+
.array(voteSchema)
|
|
862
|
+
.optional()
|
|
863
|
+
.describe('If supplied, the change must be approved by this vote'),
|
|
864
|
+
policy: z.enum(['majority', 'supermajority', 'unanimous']).optional(),
|
|
865
|
+
}),
|
|
866
|
+
annotations: {
|
|
867
|
+
title: 'DNA evolution',
|
|
868
|
+
},
|
|
869
|
+
}, async ({ minConfidence, apply, votes, policy, }) => {
|
|
870
|
+
try {
|
|
871
|
+
const bos = getBehaviorOS();
|
|
872
|
+
const result = bos.applyEvolution({ minConfidence, apply, votes, policy });
|
|
873
|
+
return {
|
|
874
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
catch (error) {
|
|
878
|
+
return {
|
|
879
|
+
content: [
|
|
880
|
+
{
|
|
881
|
+
type: 'text',
|
|
882
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
883
|
+
},
|
|
884
|
+
],
|
|
885
|
+
isError: true,
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
// ============================================================
|
|
890
|
+
// CODE FIX TOOLS
|
|
891
|
+
// ============================================================
|
|
892
|
+
server.registerTool('bos_code_fixes', {
|
|
893
|
+
description: 'Propose code fixes derived from repeated fix events, scored by confidence',
|
|
894
|
+
inputSchema: z.object({
|
|
895
|
+
minConfidence: z
|
|
896
|
+
.number()
|
|
897
|
+
.optional()
|
|
898
|
+
.describe('Minimum confidence in [0,1] for a proposal (default 0.8)'),
|
|
899
|
+
}),
|
|
900
|
+
annotations: {
|
|
901
|
+
title: 'Code fix proposals',
|
|
902
|
+
},
|
|
903
|
+
}, async ({ minConfidence }) => {
|
|
904
|
+
try {
|
|
905
|
+
const bos = getBehaviorOS();
|
|
906
|
+
const proposals = bos.proposeCodeFixes(minConfidence);
|
|
907
|
+
return {
|
|
908
|
+
content: [{ type: 'text', text: JSON.stringify(proposals, null, 2) }],
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
catch (error) {
|
|
912
|
+
return {
|
|
913
|
+
content: [
|
|
914
|
+
{
|
|
915
|
+
type: 'text',
|
|
916
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
917
|
+
},
|
|
918
|
+
],
|
|
919
|
+
isError: true,
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
});
|
|
923
|
+
server.registerTool('bos_apply_code_fixes', {
|
|
924
|
+
description: 'Persist code-fix proposals (requires evolution.enabled + apply:true; optional votes gate via decision engine)',
|
|
925
|
+
inputSchema: z.object({
|
|
926
|
+
minConfidence: z.number().optional().describe('Minimum confidence in [0,1] (default 0.8)'),
|
|
927
|
+
apply: z
|
|
928
|
+
.boolean()
|
|
929
|
+
.optional()
|
|
930
|
+
.describe('Write proposals to the store (requires evolution.enabled)'),
|
|
931
|
+
votes: z
|
|
932
|
+
.array(voteSchema)
|
|
933
|
+
.optional()
|
|
934
|
+
.describe('If supplied, the change must be approved by this vote'),
|
|
935
|
+
policy: z.enum(['majority', 'supermajority', 'unanimous']).optional(),
|
|
936
|
+
}),
|
|
937
|
+
annotations: {
|
|
938
|
+
title: 'Apply code fixes',
|
|
939
|
+
},
|
|
940
|
+
}, async ({ minConfidence, apply, votes, policy, }) => {
|
|
941
|
+
try {
|
|
942
|
+
const bos = getBehaviorOS();
|
|
943
|
+
const result = bos.applyCodeFixes({ minConfidence, apply, votes, policy });
|
|
944
|
+
return {
|
|
945
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
catch (error) {
|
|
949
|
+
return {
|
|
950
|
+
content: [
|
|
951
|
+
{
|
|
952
|
+
type: 'text',
|
|
953
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
954
|
+
},
|
|
955
|
+
],
|
|
956
|
+
isError: true,
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
// ============================================================
|
|
961
|
+
// LIST CODE FIX RESULTS
|
|
962
|
+
// ============================================================
|
|
963
|
+
server.registerTool('bos_list_code_fixes', {
|
|
964
|
+
description: 'List persisted code-fix application results (CodeFixResult records)',
|
|
965
|
+
inputSchema: z.object({
|
|
966
|
+
limit: z.number().optional().describe('Maximum results to return (default 50)'),
|
|
967
|
+
}),
|
|
968
|
+
annotations: {
|
|
969
|
+
title: 'List code fix results',
|
|
970
|
+
},
|
|
971
|
+
}, async ({ limit }) => {
|
|
972
|
+
try {
|
|
973
|
+
const bos = getBehaviorOS();
|
|
974
|
+
const results = bos.listCodeFixResults(limit);
|
|
975
|
+
return {
|
|
976
|
+
content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
catch (error) {
|
|
980
|
+
return {
|
|
981
|
+
content: [
|
|
982
|
+
{
|
|
983
|
+
type: 'text',
|
|
984
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
985
|
+
},
|
|
986
|
+
],
|
|
987
|
+
isError: true,
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
// ============================================================
|
|
992
|
+
// GOVERNED CODE EDITING
|
|
993
|
+
// ============================================================
|
|
994
|
+
server.registerTool('bos_code_edit', {
|
|
995
|
+
description: 'Preview (dry-run) or apply a single-text replacement inside a workspace file. Writes only when code editing is enabled (BEHAVIOROS_CODE_EDIT=true) *and* apply=true; optional votes gate the change. Every evaluation is persisted with before/after SHA-256 hashes.',
|
|
996
|
+
inputSchema: z.object({
|
|
997
|
+
file: z.string().describe('File path, absolute or relative to the workspace root'),
|
|
998
|
+
find: z.string().describe('Exact text to replace; must occur exactly once'),
|
|
999
|
+
replace: z.string().describe('Replacement text'),
|
|
1000
|
+
apply: z
|
|
1001
|
+
.boolean()
|
|
1002
|
+
.optional()
|
|
1003
|
+
.describe('Write the change (requires code editing to be enabled). Default false = dry run'),
|
|
1004
|
+
expectedBeforeHash: z
|
|
1005
|
+
.string()
|
|
1006
|
+
.optional()
|
|
1007
|
+
.describe('Hash from a previous preview; refuses a file that changed since'),
|
|
1008
|
+
missionId: z.string().optional().describe('Mission this edit belongs to'),
|
|
1009
|
+
votes: z.array(voteSchema).optional().describe('Votes gating the change'),
|
|
1010
|
+
applyVotes: z
|
|
1011
|
+
.array(voteSchema)
|
|
1012
|
+
.optional()
|
|
1013
|
+
.describe('Deprecated alias of `votes`; prefer `votes`'),
|
|
1014
|
+
policy: z
|
|
1015
|
+
.enum(['majority', 'supermajority', 'unanimous'])
|
|
1016
|
+
.optional()
|
|
1017
|
+
.describe('Vote policy when votes are supplied'),
|
|
1018
|
+
}),
|
|
1019
|
+
annotations: {
|
|
1020
|
+
destructiveHint: true,
|
|
1021
|
+
readOnlyHint: false,
|
|
1022
|
+
title: 'Governed code edit',
|
|
1023
|
+
},
|
|
1024
|
+
}, async ({ file, find, replace, apply, expectedBeforeHash, missionId, votes, applyVotes, policy, }) => {
|
|
1025
|
+
try {
|
|
1026
|
+
if (votes && applyVotes) {
|
|
1027
|
+
throw new Error('bos_code_edit received both `votes` and the deprecated `applyVotes`; send only one.');
|
|
1028
|
+
}
|
|
1029
|
+
const bos = getBehaviorOS();
|
|
1030
|
+
const result = bos.applyCodeEdit({
|
|
1031
|
+
file,
|
|
1032
|
+
find,
|
|
1033
|
+
replace,
|
|
1034
|
+
apply,
|
|
1035
|
+
expectedBeforeHash,
|
|
1036
|
+
missionId,
|
|
1037
|
+
votes: votes ?? applyVotes,
|
|
1038
|
+
policy,
|
|
1039
|
+
});
|
|
1040
|
+
return {
|
|
1041
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
1042
|
+
...(result.error ? { isError: true } : {}),
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
catch (error) {
|
|
1046
|
+
return {
|
|
1047
|
+
content: [
|
|
1048
|
+
{
|
|
1049
|
+
type: 'text',
|
|
1050
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1051
|
+
},
|
|
1052
|
+
],
|
|
1053
|
+
isError: true,
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
});
|
|
1057
|
+
server.registerTool('bos_list_code_edits', {
|
|
1058
|
+
description: 'List persisted code-edit records — dry runs, refusals and applied edits alike (CodeEditResult records)',
|
|
1059
|
+
inputSchema: z.object({
|
|
1060
|
+
limit: z.number().optional().describe('Maximum results to return (default 50)'),
|
|
1061
|
+
}),
|
|
1062
|
+
annotations: {
|
|
1063
|
+
readOnlyHint: true,
|
|
1064
|
+
title: 'List code edits',
|
|
1065
|
+
},
|
|
1066
|
+
}, async ({ limit }) => {
|
|
1067
|
+
try {
|
|
1068
|
+
const bos = getBehaviorOS();
|
|
1069
|
+
const records = bos.listCodeEdits(limit);
|
|
1070
|
+
return {
|
|
1071
|
+
content: [{ type: 'text', text: JSON.stringify(records, null, 2) }],
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
catch (error) {
|
|
1075
|
+
return {
|
|
1076
|
+
content: [
|
|
1077
|
+
{
|
|
1078
|
+
type: 'text',
|
|
1079
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1080
|
+
},
|
|
1081
|
+
],
|
|
1082
|
+
isError: true,
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
1086
|
+
// ============================================================
|
|
1087
|
+
// LEARNING TOOLS
|
|
1088
|
+
// ============================================================
|
|
1089
|
+
server.registerTool('bos_record_learning', {
|
|
1090
|
+
description: 'Record a learning event, or promote an existing learning record one lifecycle stage (observation → lesson → candidate → validated → policy → monitored). Promotion to validated requires evidence.',
|
|
1091
|
+
inputSchema: z.object({
|
|
1092
|
+
type: z
|
|
1093
|
+
.enum(['insight', 'pattern', 'fix', 'failure', 'optimization'])
|
|
1094
|
+
.describe('Learning type'),
|
|
1095
|
+
source: z.string().describe('Source of the learning'),
|
|
1096
|
+
content: z.string().describe('Learning content'),
|
|
1097
|
+
impact: z.enum(['low', 'medium', 'high', 'critical']).describe('Impact level'),
|
|
1098
|
+
relatedMission: z.string().optional().describe('Related mission ID'),
|
|
1099
|
+
learningId: z
|
|
1100
|
+
.string()
|
|
1101
|
+
.optional()
|
|
1102
|
+
.describe('Existing learning record to promote (instead of recording a new event)'),
|
|
1103
|
+
stage: z
|
|
1104
|
+
.enum(['observation', 'lesson', 'candidate', 'validated', 'policy', 'monitored'])
|
|
1105
|
+
.optional()
|
|
1106
|
+
.describe('Target stage; must be exactly one step after the current stage'),
|
|
1107
|
+
evidence: z
|
|
1108
|
+
.array(z.string())
|
|
1109
|
+
.optional()
|
|
1110
|
+
.describe('Evidence for the promotion; required to reach validated'),
|
|
1111
|
+
reason: z.string().optional(),
|
|
1112
|
+
}),
|
|
1113
|
+
annotations: {
|
|
1114
|
+
title: 'Record Learning',
|
|
1115
|
+
},
|
|
1116
|
+
}, async ({ type, source, content, impact, relatedMission, learningId, stage, evidence, reason, }) => {
|
|
1117
|
+
try {
|
|
1118
|
+
const bos = getBehaviorOS();
|
|
1119
|
+
// Promotion path: move an existing record exactly one stage forward.
|
|
1120
|
+
// Same contract as the plugin's tool handler — hosts must agree on the
|
|
1121
|
+
// lifecycle or cross-host records would drift.
|
|
1122
|
+
if (learningId) {
|
|
1123
|
+
if (!stage) {
|
|
1124
|
+
return {
|
|
1125
|
+
content: [
|
|
1126
|
+
{
|
|
1127
|
+
type: 'text',
|
|
1128
|
+
text: 'Learning promotion refused [INVALID_INPUT]: provide the target `stage` for `learningId`.',
|
|
1129
|
+
},
|
|
1130
|
+
],
|
|
1131
|
+
isError: true,
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
const result = bos.advanceLearningRecord(learningId, stage, {
|
|
1135
|
+
actor: 'mcp',
|
|
1136
|
+
...(evidence ? { evidence } : {}),
|
|
1137
|
+
...(reason ? { reason } : {}),
|
|
1138
|
+
});
|
|
1139
|
+
return {
|
|
1140
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
const event = bos.recordLearning({
|
|
1144
|
+
type: type,
|
|
1145
|
+
source,
|
|
1146
|
+
content,
|
|
1147
|
+
impact: impact,
|
|
1148
|
+
relatedMission,
|
|
1149
|
+
});
|
|
1150
|
+
return {
|
|
1151
|
+
content: [{ type: 'text', text: JSON.stringify(event, null, 2) }],
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
catch (error) {
|
|
1155
|
+
return {
|
|
1156
|
+
content: [
|
|
1157
|
+
{
|
|
1158
|
+
type: 'text',
|
|
1159
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1160
|
+
},
|
|
1161
|
+
],
|
|
1162
|
+
isError: true,
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
});
|
|
1166
|
+
server.registerTool('bos_get_insights', {
|
|
1167
|
+
description: 'Get high-impact insights from learning engine',
|
|
1168
|
+
inputSchema: z.object({
|
|
1169
|
+
limit: z.number().optional().describe('Maximum insights to return'),
|
|
1170
|
+
}),
|
|
1171
|
+
annotations: {
|
|
1172
|
+
readOnlyHint: true,
|
|
1173
|
+
title: 'Get Insights',
|
|
1174
|
+
},
|
|
1175
|
+
}, async ({ limit = 10 }) => {
|
|
1176
|
+
try {
|
|
1177
|
+
const bos = getBehaviorOS();
|
|
1178
|
+
const insights = bos.getInsights(limit);
|
|
1179
|
+
return {
|
|
1180
|
+
content: [{ type: 'text', text: JSON.stringify(insights, null, 2) }],
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
catch (error) {
|
|
1184
|
+
return {
|
|
1185
|
+
content: [
|
|
1186
|
+
{
|
|
1187
|
+
type: 'text',
|
|
1188
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1189
|
+
},
|
|
1190
|
+
],
|
|
1191
|
+
isError: true,
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
});
|
|
1195
|
+
// ============================================================
|
|
1196
|
+
// STATUS TOOLS
|
|
1197
|
+
// ============================================================
|
|
1198
|
+
server.registerTool('bos_get_status', {
|
|
1199
|
+
description: 'Get current system status and health',
|
|
1200
|
+
inputSchema: z.object({}),
|
|
1201
|
+
annotations: {
|
|
1202
|
+
readOnlyHint: true,
|
|
1203
|
+
title: 'Get Status',
|
|
1204
|
+
},
|
|
1205
|
+
}, async () => {
|
|
1206
|
+
try {
|
|
1207
|
+
const bos = getBehaviorOS();
|
|
1208
|
+
const status = bos.getStatus();
|
|
1209
|
+
return {
|
|
1210
|
+
content: [{ type: 'text', text: JSON.stringify(status, null, 2) }],
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
catch (error) {
|
|
1214
|
+
return {
|
|
1215
|
+
content: [
|
|
1216
|
+
{
|
|
1217
|
+
type: 'text',
|
|
1218
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1219
|
+
},
|
|
1220
|
+
],
|
|
1221
|
+
isError: true,
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
1224
|
+
});
|
|
1225
|
+
server.registerTool('bos_get_state', {
|
|
1226
|
+
description: 'Get current agent protocol state',
|
|
1227
|
+
inputSchema: z.object({}),
|
|
1228
|
+
annotations: {
|
|
1229
|
+
readOnlyHint: true,
|
|
1230
|
+
title: 'Get State',
|
|
1231
|
+
},
|
|
1232
|
+
}, async () => {
|
|
1233
|
+
try {
|
|
1234
|
+
const bos = getBehaviorOS();
|
|
1235
|
+
const state = bos.getState();
|
|
1236
|
+
// Resolve State is step 3 of the 7-step protocol; the plugin host already
|
|
1237
|
+
// advances it here so MCP callers must mirror that to stay aligned.
|
|
1238
|
+
bos.advanceProtocolStep('bos_get_state');
|
|
1239
|
+
return {
|
|
1240
|
+
content: [{ type: 'text', text: JSON.stringify(state, null, 2) }],
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
catch (error) {
|
|
1244
|
+
return {
|
|
1245
|
+
content: [
|
|
1246
|
+
{
|
|
1247
|
+
type: 'text',
|
|
1248
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1249
|
+
},
|
|
1250
|
+
],
|
|
1251
|
+
isError: true,
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
});
|
|
1255
|
+
// ============================================================
|
|
1256
|
+
// LIST TOOLS
|
|
1257
|
+
// ============================================================
|
|
1258
|
+
server.registerTool('bos_list_decisions', {
|
|
1259
|
+
description: 'List persisted voting/quorum decisions (requires persistence)',
|
|
1260
|
+
inputSchema: z.object({
|
|
1261
|
+
limit: z.number().optional().describe('Maximum results (default 50)'),
|
|
1262
|
+
}),
|
|
1263
|
+
annotations: { readOnlyHint: true, title: 'List Decisions' },
|
|
1264
|
+
}, async ({ limit }) => {
|
|
1265
|
+
try {
|
|
1266
|
+
const bos = getBehaviorOS();
|
|
1267
|
+
const decisions = bos.listDecisions(limit);
|
|
1268
|
+
return { content: [{ type: 'text', text: JSON.stringify(decisions, null, 2) }] };
|
|
1269
|
+
}
|
|
1270
|
+
catch (error) {
|
|
1271
|
+
return {
|
|
1272
|
+
content: [
|
|
1273
|
+
{
|
|
1274
|
+
type: 'text',
|
|
1275
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1276
|
+
},
|
|
1277
|
+
],
|
|
1278
|
+
isError: true,
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
server.registerTool('bos_list_pipeline_runs', {
|
|
1283
|
+
description: 'List persisted gate-pipeline runs / blocked pre-flights (requires persistence)',
|
|
1284
|
+
inputSchema: z.object({
|
|
1285
|
+
limit: z.number().optional().describe('Maximum results (default 50)'),
|
|
1286
|
+
}),
|
|
1287
|
+
annotations: { readOnlyHint: true, title: 'List Pipeline Runs' },
|
|
1288
|
+
}, async ({ limit }) => {
|
|
1289
|
+
try {
|
|
1290
|
+
const bos = getBehaviorOS();
|
|
1291
|
+
const runs = bos.listPipelineRuns(limit);
|
|
1292
|
+
return { content: [{ type: 'text', text: JSON.stringify(runs, null, 2) }] };
|
|
1293
|
+
}
|
|
1294
|
+
catch (error) {
|
|
1295
|
+
return {
|
|
1296
|
+
content: [
|
|
1297
|
+
{
|
|
1298
|
+
type: 'text',
|
|
1299
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1300
|
+
},
|
|
1301
|
+
],
|
|
1302
|
+
isError: true,
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
});
|
|
1306
|
+
server.registerTool('bos_list_audit_reports', {
|
|
1307
|
+
description: 'List persisted audit reports (requires persistence)',
|
|
1308
|
+
inputSchema: z.object({
|
|
1309
|
+
limit: z.number().optional().describe('Maximum results (default 50)'),
|
|
1310
|
+
}),
|
|
1311
|
+
annotations: { readOnlyHint: true, title: 'List Audit Reports' },
|
|
1312
|
+
}, async ({ limit }) => {
|
|
1313
|
+
try {
|
|
1314
|
+
const bos = getBehaviorOS();
|
|
1315
|
+
const reports = bos.listAuditReports(limit);
|
|
1316
|
+
return { content: [{ type: 'text', text: JSON.stringify(reports, null, 2) }] };
|
|
1317
|
+
}
|
|
1318
|
+
catch (error) {
|
|
1319
|
+
return {
|
|
1320
|
+
content: [
|
|
1321
|
+
{
|
|
1322
|
+
type: 'text',
|
|
1323
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1324
|
+
},
|
|
1325
|
+
],
|
|
1326
|
+
isError: true,
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
});
|
|
1330
|
+
server.registerTool('bos_get_log', {
|
|
1331
|
+
description: 'Read the recent Behavior-OS governance log (how DNA rules were applied to actions in this session)',
|
|
1332
|
+
inputSchema: z.object({
|
|
1333
|
+
limit: z.number().optional().describe('Maximum entries to return (default 50)'),
|
|
1334
|
+
}),
|
|
1335
|
+
annotations: { readOnlyHint: true, title: 'Governance Log' },
|
|
1336
|
+
}, async ({ limit }) => {
|
|
1337
|
+
try {
|
|
1338
|
+
const bos = getBehaviorOS();
|
|
1339
|
+
const entries = bos.getGovernanceLog(limit ?? 50);
|
|
1340
|
+
return {
|
|
1341
|
+
content: [
|
|
1342
|
+
{ type: 'text', text: JSON.stringify({ total: entries.length, entries }, null, 2) },
|
|
1343
|
+
],
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
catch (error) {
|
|
1347
|
+
return {
|
|
1348
|
+
content: [
|
|
1349
|
+
{
|
|
1350
|
+
type: 'text',
|
|
1351
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1352
|
+
},
|
|
1353
|
+
],
|
|
1354
|
+
isError: true,
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
});
|
|
1358
|
+
server.registerTool('bos_verify_audit_trail', {
|
|
1359
|
+
description: 'Verify the SHA-256 hash chain of the governance log. Reports whether the in-memory log is intact (tamper-evident audit).',
|
|
1360
|
+
inputSchema: z.object({}),
|
|
1361
|
+
annotations: { readOnlyHint: true, title: 'Verify Audit Trail' },
|
|
1362
|
+
}, async () => {
|
|
1363
|
+
try {
|
|
1364
|
+
const bos = getBehaviorOS();
|
|
1365
|
+
const verification = bos.verifyAuditTrail();
|
|
1366
|
+
const chain = bos.getGovernanceChainHead();
|
|
1367
|
+
const sink = bos.getGovernanceLogSink();
|
|
1368
|
+
return {
|
|
1369
|
+
content: [
|
|
1370
|
+
{
|
|
1371
|
+
type: 'text',
|
|
1372
|
+
text: JSON.stringify({
|
|
1373
|
+
...verification,
|
|
1374
|
+
...chain,
|
|
1375
|
+
sink: {
|
|
1376
|
+
enabled: sink.path !== null,
|
|
1377
|
+
path: sink.path,
|
|
1378
|
+
writeErrors: sink.writeErrors,
|
|
1379
|
+
},
|
|
1380
|
+
}, null, 2),
|
|
1381
|
+
},
|
|
1382
|
+
],
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
catch (error) {
|
|
1386
|
+
return {
|
|
1387
|
+
content: [
|
|
1388
|
+
{
|
|
1389
|
+
type: 'text',
|
|
1390
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1391
|
+
},
|
|
1392
|
+
],
|
|
1393
|
+
isError: true,
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
});
|
|
1397
|
+
// ============================================================
|
|
1398
|
+
// ANALYTICS TOOLS
|
|
1399
|
+
// ============================================================
|
|
1400
|
+
server.registerTool('bos_recommendations', {
|
|
1401
|
+
description: 'Scored recommendations derived from recent learning events',
|
|
1402
|
+
inputSchema: z.object({
|
|
1403
|
+
limit: z.number().optional().describe('Max recommendations (default 5)'),
|
|
1404
|
+
ageLimitHours: z.number().optional().describe('Ignore older events (default 168h)'),
|
|
1405
|
+
}),
|
|
1406
|
+
annotations: { readOnlyHint: true, title: 'Recommendations' },
|
|
1407
|
+
}, async ({ limit, ageLimitHours }) => {
|
|
1408
|
+
try {
|
|
1409
|
+
const bos = getBehaviorOS();
|
|
1410
|
+
const recs = bos.recommend(limit, ageLimitHours);
|
|
1411
|
+
return { content: [{ type: 'text', text: JSON.stringify(recs, null, 2) }] };
|
|
1412
|
+
}
|
|
1413
|
+
catch (error) {
|
|
1414
|
+
return {
|
|
1415
|
+
content: [
|
|
1416
|
+
{
|
|
1417
|
+
type: 'text',
|
|
1418
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1419
|
+
},
|
|
1420
|
+
],
|
|
1421
|
+
isError: true,
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
});
|
|
1425
|
+
server.registerTool('bos_drift', {
|
|
1426
|
+
description: 'Compare two DNA patterns (by name) and report behavioral drift',
|
|
1427
|
+
inputSchema: z.object({
|
|
1428
|
+
dnaA: z.string().describe('Older DNA pattern name, e.g. "lean-factory"'),
|
|
1429
|
+
dnaB: z.string().describe('Newer DNA pattern name, e.g. "devops-pipeline"'),
|
|
1430
|
+
}),
|
|
1431
|
+
annotations: { readOnlyHint: true, title: 'DNA Drift' },
|
|
1432
|
+
}, async ({ dnaA, dnaB }) => {
|
|
1433
|
+
try {
|
|
1434
|
+
const dnas = await loadDNAPatterns();
|
|
1435
|
+
const a = dnas[dnaA.toLowerCase()];
|
|
1436
|
+
const b = dnas[dnaB.toLowerCase()];
|
|
1437
|
+
if (!a || !b) {
|
|
1438
|
+
const missing = [!a ? dnaA : null, !b ? dnaB : null].filter(Boolean).join('", "');
|
|
1439
|
+
return {
|
|
1440
|
+
content: [
|
|
1441
|
+
{
|
|
1442
|
+
type: 'text',
|
|
1443
|
+
text: `DNA pattern(s) not found: "${missing}". Available: ${Object.keys(dnas)
|
|
1444
|
+
.sort()
|
|
1445
|
+
.join(', ')}`,
|
|
1446
|
+
},
|
|
1447
|
+
],
|
|
1448
|
+
isError: true,
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
const drift = getBehaviorOS().compareDNADrift(a, b);
|
|
1452
|
+
return { content: [{ type: 'text', text: JSON.stringify(drift, null, 2) }] };
|
|
1453
|
+
}
|
|
1454
|
+
catch (error) {
|
|
1455
|
+
return {
|
|
1456
|
+
content: [
|
|
1457
|
+
{
|
|
1458
|
+
type: 'text',
|
|
1459
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1460
|
+
},
|
|
1461
|
+
],
|
|
1462
|
+
isError: true,
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
});
|
|
1466
|
+
// ============================================================
|
|
1467
|
+
// PREDICTION TOOLS
|
|
1468
|
+
// ============================================================
|
|
1469
|
+
server.registerTool('bos_predict_governance', {
|
|
1470
|
+
description: 'Predict likely future governance events from learning history. Returns confidence-scored predictions about governance-block, mission-failure, quality-fall, drift-significant, or pattern-repeated events.',
|
|
1471
|
+
inputSchema: z.object({
|
|
1472
|
+
limit: z.number().optional().describe('Max predictions to return (default 5)'),
|
|
1473
|
+
}),
|
|
1474
|
+
annotations: { readOnlyHint: true, title: 'Predict Governance Events' },
|
|
1475
|
+
}, async ({ limit }) => {
|
|
1476
|
+
try {
|
|
1477
|
+
const bos = getBehaviorOS();
|
|
1478
|
+
const predictions = bos.predict(limit);
|
|
1479
|
+
return { content: [{ type: 'text', text: JSON.stringify(predictions, null, 2) }] };
|
|
1480
|
+
}
|
|
1481
|
+
catch (error) {
|
|
1482
|
+
return {
|
|
1483
|
+
content: [
|
|
1484
|
+
{
|
|
1485
|
+
type: 'text',
|
|
1486
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1487
|
+
},
|
|
1488
|
+
],
|
|
1489
|
+
isError: true,
|
|
1490
|
+
};
|
|
1491
|
+
}
|
|
1492
|
+
});
|
|
1493
|
+
// ============================================================
|
|
1494
|
+
// CONSENSUS & CROSS-DNA LEARNING TOOLS (Feature #8)
|
|
1495
|
+
// ============================================================
|
|
1496
|
+
server.registerTool('bos_resolve_consensus', {
|
|
1497
|
+
description: 'Resolve a multi-agent consensus round. Votes are weighted; ties and all-abstain resolve to rejected (fail-safe).',
|
|
1498
|
+
inputSchema: z.object({
|
|
1499
|
+
subject: z.string().describe('What is being decided'),
|
|
1500
|
+
votes: z.array(z.object({
|
|
1501
|
+
agentId: z.string().describe('Stable agent identifier'),
|
|
1502
|
+
choice: z.enum(['approve', 'reject', 'abstain']),
|
|
1503
|
+
weight: z.number().optional().describe('Weight in the tally (default 1)'),
|
|
1504
|
+
reason: z.string().optional().describe('Human-readable justification'),
|
|
1505
|
+
})),
|
|
1506
|
+
policy: z.enum(['majority', 'supermajority', 'unanimous']).optional(),
|
|
1507
|
+
quorum: z.number().optional().describe('Minimum participating weight'),
|
|
1508
|
+
}),
|
|
1509
|
+
annotations: {
|
|
1510
|
+
title: 'Resolve Consensus',
|
|
1511
|
+
},
|
|
1512
|
+
}, async ({ subject, votes, policy, quorum, }) => {
|
|
1513
|
+
try {
|
|
1514
|
+
const bos = getBehaviorOS();
|
|
1515
|
+
const result = bos.resolveConsensus({ subject, votes, policy, quorum });
|
|
1516
|
+
return {
|
|
1517
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
catch (error) {
|
|
1521
|
+
return {
|
|
1522
|
+
content: [
|
|
1523
|
+
{
|
|
1524
|
+
type: 'text',
|
|
1525
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1526
|
+
},
|
|
1527
|
+
],
|
|
1528
|
+
isError: true,
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
});
|
|
1532
|
+
server.registerTool('bos_suggest_cross_dna_transfer', {
|
|
1533
|
+
description: 'Suggest learning transfers (patterns, rules, fixes) from one DNA to another based on shared behavioral patterns.',
|
|
1534
|
+
inputSchema: z.object({
|
|
1535
|
+
sourceDna: z.string().describe('Source DNA pattern name'),
|
|
1536
|
+
targetDna: z.string().describe('Target DNA pattern name'),
|
|
1537
|
+
minConfidence: z
|
|
1538
|
+
.number()
|
|
1539
|
+
.min(0)
|
|
1540
|
+
.max(1)
|
|
1541
|
+
.optional()
|
|
1542
|
+
.describe('Minimum confidence threshold (default 0.5)'),
|
|
1543
|
+
}),
|
|
1544
|
+
annotations: { readOnlyHint: true, title: 'Suggest Cross-DNA Transfer' },
|
|
1545
|
+
}, async ({ sourceDna, targetDna, minConfidence, }) => {
|
|
1546
|
+
try {
|
|
1547
|
+
const bos = getBehaviorOS();
|
|
1548
|
+
const dnas = await loadDNAPatterns();
|
|
1549
|
+
const source = dnas[sourceDna.toLowerCase()];
|
|
1550
|
+
const target = dnas[targetDna.toLowerCase()];
|
|
1551
|
+
if (!source || !target) {
|
|
1552
|
+
const missing = [!source ? sourceDna : null, !target ? targetDna : null]
|
|
1553
|
+
.filter(Boolean)
|
|
1554
|
+
.join('", "');
|
|
1555
|
+
return {
|
|
1556
|
+
content: [
|
|
1557
|
+
{
|
|
1558
|
+
type: 'text',
|
|
1559
|
+
text: `DNA pattern(s) not found: "${missing}". Available: ${Object.keys(dnas).join(', ')}`,
|
|
1560
|
+
},
|
|
1561
|
+
],
|
|
1562
|
+
isError: true,
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
const result = bos.suggestCrossDnaTransfer(source, target, minConfidence ?? 0.5);
|
|
1566
|
+
return {
|
|
1567
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
catch (error) {
|
|
1571
|
+
return {
|
|
1572
|
+
content: [
|
|
1573
|
+
{
|
|
1574
|
+
type: 'text',
|
|
1575
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1576
|
+
},
|
|
1577
|
+
],
|
|
1578
|
+
isError: true,
|
|
1579
|
+
};
|
|
1580
|
+
}
|
|
1581
|
+
});
|
|
1582
|
+
// ============================================================
|
|
1583
|
+
// RESOURCES
|
|
1584
|
+
// ============================================================
|
|
1585
|
+
server.registerResource('behavioros://protocol/steps', 'behavioros://protocol/steps', { mimeType: 'text/plain', description: 'Protocol Steps' }, async () => ({
|
|
1586
|
+
contents: [
|
|
1587
|
+
{
|
|
1588
|
+
uri: 'behavioros://protocol/steps',
|
|
1589
|
+
// Rendered from PROTOCOL_STEPS in @behavior-os/core, so it cannot drift.
|
|
1590
|
+
text: [
|
|
1591
|
+
'# Behavior-OS 7-Step Protocol',
|
|
1592
|
+
'',
|
|
1593
|
+
...PROTOCOL_STEPS.map((step) => `${step.number}. ${step.name} (${step.tool})`),
|
|
1594
|
+
].join('\n'),
|
|
1595
|
+
},
|
|
1596
|
+
],
|
|
1597
|
+
}));
|
|
1598
|
+
server.registerResource('behavioros://dna/catalog', 'behavioros://dna/catalog', { mimeType: 'application/json', description: 'DNA Catalog' }, async () => {
|
|
1599
|
+
const dnas = await loadDNAPatterns();
|
|
1600
|
+
const available = Object.keys(dnas).sort();
|
|
1601
|
+
return {
|
|
1602
|
+
contents: [
|
|
1603
|
+
{
|
|
1604
|
+
uri: 'behavioros://dna/catalog',
|
|
1605
|
+
text: JSON.stringify({
|
|
1606
|
+
available,
|
|
1607
|
+
default: available.includes('enterprise-governance')
|
|
1608
|
+
? 'enterprise-governance'
|
|
1609
|
+
: available[0],
|
|
1610
|
+
}, null, 2),
|
|
1611
|
+
},
|
|
1612
|
+
],
|
|
1613
|
+
};
|
|
1614
|
+
});
|
|
1615
|
+
// ============================================================
|
|
1616
|
+
// DNA PATTERNS - Carregados do sistema de arquivos
|
|
1617
|
+
// ============================================================
|
|
1618
|
+
async function loadDNAPatterns() {
|
|
1619
|
+
const fs = await import('node:fs');
|
|
1620
|
+
const path = await import('node:path');
|
|
1621
|
+
const { fileURLToPath } = await import('node:url');
|
|
1622
|
+
const yaml = await import('js-yaml');
|
|
1623
|
+
// Default to the repository's dnas/ directory resolved relative to this module
|
|
1624
|
+
// (packages/mcp-server/{src,dist}/server.ts -> <repo>/dnas) so the server works on
|
|
1625
|
+
// any machine instead of depending on a hardcoded checkout path.
|
|
1626
|
+
const defaultDnasPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../dnas');
|
|
1627
|
+
const dnaPath = process.env.BEHAVIOROS_DNA_PATH || defaultDnasPath;
|
|
1628
|
+
const dnas = {};
|
|
1629
|
+
try {
|
|
1630
|
+
const resolved = path.resolve(dnaPath);
|
|
1631
|
+
const files = fs.statSync(resolved).isDirectory()
|
|
1632
|
+
? fs
|
|
1633
|
+
.readdirSync(resolved)
|
|
1634
|
+
.filter((file) => file.endsWith('.yaml') || file.endsWith('.yml'))
|
|
1635
|
+
.map((file) => path.join(resolved, file))
|
|
1636
|
+
: [resolved];
|
|
1637
|
+
for (const filePath of files) {
|
|
1638
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
1639
|
+
const dna = yaml.load(content);
|
|
1640
|
+
const name = path.basename(filePath).replace(/\.(yaml|yml)$/, '');
|
|
1641
|
+
dnas[name] = dna;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
catch {
|
|
1645
|
+
// Fallback to hardcoded patterns if filesystem loading fails
|
|
1646
|
+
console.error('Warning: Could not load DNA patterns from filesystem, using defaults');
|
|
1647
|
+
}
|
|
1648
|
+
// Ensure we have at least the basic patterns
|
|
1649
|
+
if (Object.keys(dnas).length === 0) {
|
|
1650
|
+
dnas['surgical-team'] = {
|
|
1651
|
+
name: 'Surgical Team',
|
|
1652
|
+
version: '2.0.0',
|
|
1653
|
+
description: 'Zero-defect governance with sterile field protocols',
|
|
1654
|
+
personas: [],
|
|
1655
|
+
governance: [],
|
|
1656
|
+
quality: [],
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
return dnas;
|
|
1660
|
+
}
|
|
1661
|
+
// ============================================================
|
|
1662
|
+
// METRICS & OBSERVABILITY TOOLS
|
|
1663
|
+
// ============================================================
|
|
1664
|
+
server.registerTool('bos_get_metrics', {
|
|
1665
|
+
description: 'Get current system-wide governance metrics and observability data',
|
|
1666
|
+
inputSchema: z.object({}).passthrough().optional(),
|
|
1667
|
+
annotations: { readOnlyHint: true, title: 'Get Metrics' },
|
|
1668
|
+
}, async () => {
|
|
1669
|
+
try {
|
|
1670
|
+
const bos = getBehaviorOS();
|
|
1671
|
+
const metrics = bos.getMetrics();
|
|
1672
|
+
return { content: [{ type: 'text', text: JSON.stringify(metrics, null, 2) }] };
|
|
1673
|
+
}
|
|
1674
|
+
catch (error) {
|
|
1675
|
+
return {
|
|
1676
|
+
content: [
|
|
1677
|
+
{
|
|
1678
|
+
type: 'text',
|
|
1679
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1680
|
+
},
|
|
1681
|
+
],
|
|
1682
|
+
isError: true,
|
|
1683
|
+
};
|
|
1684
|
+
}
|
|
1685
|
+
});
|
|
1686
|
+
server.registerTool('bos_reset_metrics', {
|
|
1687
|
+
description: 'Reset all governance metrics (useful for fresh measurements)',
|
|
1688
|
+
inputSchema: z.object({}).passthrough().optional(),
|
|
1689
|
+
annotations: { destructiveHint: true, title: 'Reset Metrics' },
|
|
1690
|
+
}, async () => {
|
|
1691
|
+
try {
|
|
1692
|
+
const bos = getBehaviorOS();
|
|
1693
|
+
bos.resetMetrics();
|
|
1694
|
+
return { content: [{ type: 'text', text: 'Metrics reset successfully' }] };
|
|
1695
|
+
}
|
|
1696
|
+
catch (error) {
|
|
1697
|
+
return {
|
|
1698
|
+
content: [
|
|
1699
|
+
{
|
|
1700
|
+
type: 'text',
|
|
1701
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1702
|
+
},
|
|
1703
|
+
],
|
|
1704
|
+
isError: true,
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
});
|
|
1708
|
+
// ============================================================
|
|
1709
|
+
// BRAINSTORMING & DYNAMIC DNA TOOLS
|
|
1710
|
+
// ============================================================
|
|
1711
|
+
server.registerTool('bos_create_brainstorm', {
|
|
1712
|
+
description: 'Create a new brainstorming session for multi-agent collaboration',
|
|
1713
|
+
inputSchema: z.object({
|
|
1714
|
+
title: z.string().describe('Title of the brainstorming topic'),
|
|
1715
|
+
participants: z.array(z.string()).describe('Agent IDs participating'),
|
|
1716
|
+
dnaPattern: z.string().describe('DNA pattern governing the session'),
|
|
1717
|
+
}),
|
|
1718
|
+
annotations: { readOnlyHint: true, title: 'Create Brainstorm' },
|
|
1719
|
+
}, async ({ title, participants, dnaPattern }) => {
|
|
1720
|
+
try {
|
|
1721
|
+
const bos = getBehaviorOS();
|
|
1722
|
+
const session = bos.createBrainstormSession(title, participants, dnaPattern);
|
|
1723
|
+
return { content: [{ type: 'text', text: JSON.stringify(session, null, 2) }] };
|
|
1724
|
+
}
|
|
1725
|
+
catch (error) {
|
|
1726
|
+
return {
|
|
1727
|
+
content: [
|
|
1728
|
+
{
|
|
1729
|
+
type: 'text',
|
|
1730
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1731
|
+
},
|
|
1732
|
+
],
|
|
1733
|
+
isError: true,
|
|
1734
|
+
};
|
|
1735
|
+
}
|
|
1736
|
+
});
|
|
1737
|
+
server.registerTool('bos_list_brainstorms', {
|
|
1738
|
+
description: 'List all brainstorming sessions',
|
|
1739
|
+
inputSchema: z.object({}).passthrough().optional(),
|
|
1740
|
+
annotations: { readOnlyHint: true, title: 'List Brainstorms' },
|
|
1741
|
+
}, async () => {
|
|
1742
|
+
try {
|
|
1743
|
+
const bos = getBehaviorOS();
|
|
1744
|
+
const sessions = bos.listBrainstormSessions();
|
|
1745
|
+
return { content: [{ type: 'text', text: JSON.stringify(sessions, null, 2) }] };
|
|
1746
|
+
}
|
|
1747
|
+
catch (error) {
|
|
1748
|
+
return {
|
|
1749
|
+
content: [
|
|
1750
|
+
{
|
|
1751
|
+
type: 'text',
|
|
1752
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1753
|
+
},
|
|
1754
|
+
],
|
|
1755
|
+
isError: true,
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
});
|
|
1759
|
+
server.registerTool('bos_propose_idea', {
|
|
1760
|
+
description: 'Propose an idea in a brainstorming session',
|
|
1761
|
+
inputSchema: z.object({
|
|
1762
|
+
sessionId: z.string().describe('Brainstorm session ID'),
|
|
1763
|
+
authorId: z.string().describe('Agent proposing the idea'),
|
|
1764
|
+
title: z.string().describe('Idea title'),
|
|
1765
|
+
description: z.string().describe('Idea description'),
|
|
1766
|
+
}),
|
|
1767
|
+
annotations: { readOnlyHint: false, title: 'Propose Idea' },
|
|
1768
|
+
}, async ({ sessionId, authorId, title, description }) => {
|
|
1769
|
+
try {
|
|
1770
|
+
const bos = getBehaviorOS();
|
|
1771
|
+
const engine = bos.getBrainstorm();
|
|
1772
|
+
const result = engine.proposeIdea(sessionId, authorId, title, description);
|
|
1773
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1774
|
+
}
|
|
1775
|
+
catch (error) {
|
|
1776
|
+
return {
|
|
1777
|
+
content: [
|
|
1778
|
+
{
|
|
1779
|
+
type: 'text',
|
|
1780
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1781
|
+
},
|
|
1782
|
+
],
|
|
1783
|
+
isError: true,
|
|
1784
|
+
};
|
|
1785
|
+
}
|
|
1786
|
+
});
|
|
1787
|
+
server.registerTool('bos_vote_idea', {
|
|
1788
|
+
description: 'Vote on an idea in a brainstorming session',
|
|
1789
|
+
inputSchema: z.object({
|
|
1790
|
+
sessionId: z.string().describe('Brainstorm session ID'),
|
|
1791
|
+
ideaId: z.string().describe('Idea ID to vote on'),
|
|
1792
|
+
voterId: z.string().describe('Agent voting'),
|
|
1793
|
+
vote: z.enum(['support', 'oppose', 'neutral']).describe('Vote choice'),
|
|
1794
|
+
reason: z.string().optional(),
|
|
1795
|
+
}),
|
|
1796
|
+
annotations: { readOnlyHint: false, title: 'Vote Idea' },
|
|
1797
|
+
}, async ({ sessionId, ideaId, voterId, vote, reason }) => {
|
|
1798
|
+
try {
|
|
1799
|
+
const bos = getBehaviorOS();
|
|
1800
|
+
const engine = bos.getBrainstorm();
|
|
1801
|
+
const result = engine.voteOnIdea(sessionId, ideaId, voterId, vote, reason);
|
|
1802
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1803
|
+
}
|
|
1804
|
+
catch (error) {
|
|
1805
|
+
return {
|
|
1806
|
+
content: [
|
|
1807
|
+
{
|
|
1808
|
+
type: 'text',
|
|
1809
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1810
|
+
},
|
|
1811
|
+
],
|
|
1812
|
+
isError: true,
|
|
1813
|
+
};
|
|
1814
|
+
}
|
|
1815
|
+
});
|
|
1816
|
+
server.registerTool('bos_create_dynamic_dna', {
|
|
1817
|
+
description: 'Create a dynamic DNA that can be modified at runtime',
|
|
1818
|
+
inputSchema: z.object({
|
|
1819
|
+
dnaName: z.string().describe('Name for the dynamic DNA'),
|
|
1820
|
+
baseDnaName: z.string().describe('Base DNA pattern name'),
|
|
1821
|
+
}),
|
|
1822
|
+
annotations: { readOnlyHint: false, title: 'Create Dynamic DNA' },
|
|
1823
|
+
}, async ({ dnaName, baseDnaName }) => {
|
|
1824
|
+
try {
|
|
1825
|
+
const bos = getBehaviorOS();
|
|
1826
|
+
const dnas = await loadDNAPatterns();
|
|
1827
|
+
const baseDna = dnas[baseDnaName.toLowerCase()];
|
|
1828
|
+
if (!baseDna) {
|
|
1829
|
+
return {
|
|
1830
|
+
content: [{ type: 'text', text: `Base DNA "${baseDnaName}" not found` }],
|
|
1831
|
+
isError: true,
|
|
1832
|
+
};
|
|
1833
|
+
}
|
|
1834
|
+
const manager = bos.getDynamicDNAManager();
|
|
1835
|
+
const dynamic = manager.createDynamicDNA(dnaName, baseDna);
|
|
1836
|
+
return { content: [{ type: 'text', text: JSON.stringify(dynamic, null, 2) }] };
|
|
1837
|
+
}
|
|
1838
|
+
catch (error) {
|
|
1839
|
+
return {
|
|
1840
|
+
content: [
|
|
1841
|
+
{
|
|
1842
|
+
type: 'text',
|
|
1843
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1844
|
+
},
|
|
1845
|
+
],
|
|
1846
|
+
isError: true,
|
|
1847
|
+
};
|
|
1848
|
+
}
|
|
1849
|
+
});
|
|
1850
|
+
server.registerTool('bos_override_rule', {
|
|
1851
|
+
description: 'Override a governance rule in a dynamic DNA',
|
|
1852
|
+
inputSchema: z.object({
|
|
1853
|
+
dnaName: z.string().describe('Dynamic DNA name'),
|
|
1854
|
+
ruleId: z.string().describe('Rule ID to override'),
|
|
1855
|
+
action: z.enum(['block', 'escalate', 'warn', 'log', 'transform']).describe('New action'),
|
|
1856
|
+
reason: z.string().describe('Reason for override'),
|
|
1857
|
+
authorizedBy: z.string().describe('Who authorized'),
|
|
1858
|
+
}),
|
|
1859
|
+
annotations: { destructiveHint: true, title: 'Override Rule' },
|
|
1860
|
+
}, async ({ dnaName, ruleId, action, reason, authorizedBy }) => {
|
|
1861
|
+
try {
|
|
1862
|
+
const bos = getBehaviorOS();
|
|
1863
|
+
const manager = bos.getDynamicDNAManager();
|
|
1864
|
+
const result = manager.addOverride(dnaName, ruleId, action, undefined, reason, authorizedBy);
|
|
1865
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1866
|
+
}
|
|
1867
|
+
catch (error) {
|
|
1868
|
+
return {
|
|
1869
|
+
content: [
|
|
1870
|
+
{
|
|
1871
|
+
type: 'text',
|
|
1872
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1873
|
+
},
|
|
1874
|
+
],
|
|
1875
|
+
isError: true,
|
|
1876
|
+
};
|
|
1877
|
+
}
|
|
1878
|
+
});
|
|
1879
|
+
// ============================================================
|
|
1880
|
+
// BEHAVIORAL UX TOOLS
|
|
1881
|
+
// ============================================================
|
|
1882
|
+
server.registerTool('bos_behavior_profile', {
|
|
1883
|
+
description: 'Generate a human-readable behavioral profile for an agent',
|
|
1884
|
+
inputSchema: z.object({
|
|
1885
|
+
agentId: z.string().describe('Agent ID'),
|
|
1886
|
+
persona: z.string().describe('Agent persona/role'),
|
|
1887
|
+
dnaPattern: z.string().describe('Active DNA pattern name'),
|
|
1888
|
+
}),
|
|
1889
|
+
annotations: { readOnlyHint: true, title: 'Behavior Profile' },
|
|
1890
|
+
}, async ({ agentId, persona, dnaPattern }) => {
|
|
1891
|
+
try {
|
|
1892
|
+
const bos = getBehaviorOS();
|
|
1893
|
+
const dnas = await loadDNAPatterns();
|
|
1894
|
+
const dna = dnas[dnaPattern.toLowerCase()];
|
|
1895
|
+
if (!dna) {
|
|
1896
|
+
return {
|
|
1897
|
+
content: [{ type: 'text', text: `DNA pattern "${dnaPattern}" not found` }],
|
|
1898
|
+
isError: true,
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
const ux = bos.getBehavioralUX();
|
|
1902
|
+
const learnings = bos.getInsights(100);
|
|
1903
|
+
const missions = bos.listMissions(50);
|
|
1904
|
+
const profile = ux.generateProfile(agentId, persona, dnaPattern, dna, learnings, missions);
|
|
1905
|
+
const md = ux.generateMarkdownReport(agentId, profile);
|
|
1906
|
+
return { content: [{ type: 'text', text: md }] };
|
|
1907
|
+
}
|
|
1908
|
+
catch (error) {
|
|
1909
|
+
return {
|
|
1910
|
+
content: [
|
|
1911
|
+
{
|
|
1912
|
+
type: 'text',
|
|
1913
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1914
|
+
},
|
|
1915
|
+
],
|
|
1916
|
+
isError: true,
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
});
|
|
1920
|
+
server.registerTool('bos_behavior_diff', {
|
|
1921
|
+
description: 'Compare two DNA patterns and show behavioral drift with interpretation',
|
|
1922
|
+
inputSchema: z.object({
|
|
1923
|
+
agentId: z.string().describe('Agent ID'),
|
|
1924
|
+
olderDna: z.string().describe('Older DNA pattern name'),
|
|
1925
|
+
newerDna: z.string().describe('Newer DNA pattern name'),
|
|
1926
|
+
}),
|
|
1927
|
+
annotations: { readOnlyHint: true, title: 'Behavior Diff' },
|
|
1928
|
+
}, async ({ agentId, olderDna, newerDna }) => {
|
|
1929
|
+
try {
|
|
1930
|
+
const bos = getBehaviorOS();
|
|
1931
|
+
const dnas = await loadDNAPatterns();
|
|
1932
|
+
const older = dnas[olderDna.toLowerCase()];
|
|
1933
|
+
const newer = dnas[newerDna.toLowerCase()];
|
|
1934
|
+
if (!older || !newer) {
|
|
1935
|
+
return {
|
|
1936
|
+
content: [
|
|
1937
|
+
{
|
|
1938
|
+
type: 'text',
|
|
1939
|
+
text: `DNA patterns not found. Available: ${Object.keys(dnas).join(', ')}`,
|
|
1940
|
+
},
|
|
1941
|
+
],
|
|
1942
|
+
isError: true,
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
const driftEngine = bos.getDriftEngine();
|
|
1946
|
+
const driftInfo = driftEngine.compare(older, newer);
|
|
1947
|
+
const ux = bos.getBehavioralUX();
|
|
1948
|
+
const diff = ux.compareBehaviors(agentId, older, newer, driftInfo);
|
|
1949
|
+
const _profile = ux.getAllDiffs().length > 0 ? ux.getAllDiffs()[ux.getAllDiffs().length - 1] : diff;
|
|
1950
|
+
const md = ux.generateMarkdownReport(agentId, {
|
|
1951
|
+
agentId,
|
|
1952
|
+
persona: 'comparator',
|
|
1953
|
+
dnaPattern: `${olderDna}→${newerDna}`,
|
|
1954
|
+
dnaVersion: `${older.version}→${newer.version}`,
|
|
1955
|
+
traits: [],
|
|
1956
|
+
confidence: 0.5,
|
|
1957
|
+
governanceScore: 50,
|
|
1958
|
+
learnedBehaviors: [],
|
|
1959
|
+
predictedFailures: [],
|
|
1960
|
+
timestamp: new Date().toISOString(),
|
|
1961
|
+
}, diff);
|
|
1962
|
+
return { content: [{ type: 'text', text: md }] };
|
|
1963
|
+
}
|
|
1964
|
+
catch (error) {
|
|
1965
|
+
return {
|
|
1966
|
+
content: [
|
|
1967
|
+
{
|
|
1968
|
+
type: 'text',
|
|
1969
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1970
|
+
},
|
|
1971
|
+
],
|
|
1972
|
+
isError: true,
|
|
1973
|
+
};
|
|
1974
|
+
}
|
|
1975
|
+
});
|
|
1976
|
+
server.registerTool('bos_teach_behavior', {
|
|
1977
|
+
description: 'Teach/correct agent behavior — record a behavioral education',
|
|
1978
|
+
inputSchema: z.object({
|
|
1979
|
+
agentId: z.string().describe('Agent ID'),
|
|
1980
|
+
pattern: z.string().describe('Behavior pattern to correct'),
|
|
1981
|
+
correction: z.string().describe('Correct behavior description'),
|
|
1982
|
+
reason: z.string().describe('Reason for correction'),
|
|
1983
|
+
}),
|
|
1984
|
+
annotations: { readOnlyHint: false, title: 'Teach Behavior' },
|
|
1985
|
+
}, async ({ agentId, pattern, correction, reason }) => {
|
|
1986
|
+
try {
|
|
1987
|
+
const bos = getBehaviorOS();
|
|
1988
|
+
const ux = bos.getBehavioralUX();
|
|
1989
|
+
const education = ux.teachBehavior(agentId, pattern, correction, reason, 'user');
|
|
1990
|
+
// Also record as a learning event
|
|
1991
|
+
bos.recordLearning({
|
|
1992
|
+
type: 'fix',
|
|
1993
|
+
source: 'behavior-education',
|
|
1994
|
+
content: `Educated ${agentId}: ${correction}`,
|
|
1995
|
+
impact: 'high',
|
|
1996
|
+
relatedMission: education.id,
|
|
1997
|
+
});
|
|
1998
|
+
return {
|
|
1999
|
+
content: [
|
|
2000
|
+
{
|
|
2001
|
+
type: 'text',
|
|
2002
|
+
text: `✅ Behavior educated!\n\n**Pattern:** ${pattern}\n**Correction:** ${correction}\n**Reason:** ${reason}\n**ID:** ${education.id}`,
|
|
2003
|
+
},
|
|
2004
|
+
],
|
|
2005
|
+
};
|
|
2006
|
+
}
|
|
2007
|
+
catch (error) {
|
|
2008
|
+
return {
|
|
2009
|
+
content: [
|
|
2010
|
+
{
|
|
2011
|
+
type: 'text',
|
|
2012
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2013
|
+
},
|
|
2014
|
+
],
|
|
2015
|
+
isError: true,
|
|
2016
|
+
};
|
|
2017
|
+
}
|
|
2018
|
+
});
|
|
2019
|
+
server.registerTool('bos_behavior_timeline', {
|
|
2020
|
+
description: 'Get evolution timeline showing how agent behavior changed over time',
|
|
2021
|
+
inputSchema: z
|
|
2022
|
+
.object({
|
|
2023
|
+
agentId: z.string().optional().describe('Filter by agent ID'),
|
|
2024
|
+
limit: z.number().optional().describe('Max entries (default 20)'),
|
|
2025
|
+
})
|
|
2026
|
+
.passthrough()
|
|
2027
|
+
.optional(),
|
|
2028
|
+
annotations: { readOnlyHint: true, title: 'Behavior Timeline' },
|
|
2029
|
+
}, async (params) => {
|
|
2030
|
+
const agentId = params?.agentId;
|
|
2031
|
+
const limit = params?.limit ?? 20;
|
|
2032
|
+
try {
|
|
2033
|
+
const bos = getBehaviorOS();
|
|
2034
|
+
const ux = bos.getBehavioralUX();
|
|
2035
|
+
const timeline = ux.getTimeline(agentId).slice(0, limit);
|
|
2036
|
+
if (timeline.length === 0) {
|
|
2037
|
+
return {
|
|
2038
|
+
content: [
|
|
2039
|
+
{
|
|
2040
|
+
type: 'text',
|
|
2041
|
+
text: agentId
|
|
2042
|
+
? `No behavior events found for agent "${agentId}"`
|
|
2043
|
+
: 'No behavior events recorded yet.',
|
|
2044
|
+
},
|
|
2045
|
+
],
|
|
2046
|
+
};
|
|
2047
|
+
}
|
|
2048
|
+
let md = '# 📈 Evolution Timeline\n\n';
|
|
2049
|
+
for (const entry of timeline) {
|
|
2050
|
+
md += `${entry.emoji} **${entry.timestamp}** — ${entry.event}\n\n`;
|
|
2051
|
+
md += `${entry.description}\n\n`;
|
|
2052
|
+
md += `_Impact: ${entry.impact} | Type: ${entry.type}_\n\n---\n\n`;
|
|
2053
|
+
}
|
|
2054
|
+
return { content: [{ type: 'text', text: md }] };
|
|
2055
|
+
}
|
|
2056
|
+
catch (error) {
|
|
2057
|
+
return {
|
|
2058
|
+
content: [
|
|
2059
|
+
{
|
|
2060
|
+
type: 'text',
|
|
2061
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2062
|
+
},
|
|
2063
|
+
],
|
|
2064
|
+
isError: true,
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
});
|
|
2068
|
+
server.registerTool('bos_behavior_summary', {
|
|
2069
|
+
description: 'Get a quick one-line behavior summary for an agent',
|
|
2070
|
+
inputSchema: z.object({
|
|
2071
|
+
agentId: z.string().describe('Agent ID'),
|
|
2072
|
+
}),
|
|
2073
|
+
annotations: { readOnlyHint: true, title: 'Behavior Summary' },
|
|
2074
|
+
}, async ({ agentId }) => {
|
|
2075
|
+
try {
|
|
2076
|
+
const bos = getBehaviorOS();
|
|
2077
|
+
const ux = bos.getBehavioralUX();
|
|
2078
|
+
const profile = ux.getProfile(agentId);
|
|
2079
|
+
if (!profile) {
|
|
2080
|
+
return {
|
|
2081
|
+
content: [
|
|
2082
|
+
{
|
|
2083
|
+
type: 'text',
|
|
2084
|
+
text: `No profile found for agent "${agentId}". Generate one with bos_behavior_profile first.`,
|
|
2085
|
+
},
|
|
2086
|
+
],
|
|
2087
|
+
};
|
|
2088
|
+
}
|
|
2089
|
+
const summary = ux.getBehaviorSummary(profile);
|
|
2090
|
+
const statusEmoji = ux.getStatusEmoji(profile);
|
|
2091
|
+
return {
|
|
2092
|
+
content: [
|
|
2093
|
+
{ type: 'text', text: summary },
|
|
2094
|
+
{
|
|
2095
|
+
type: 'text',
|
|
2096
|
+
text: `\n\n${statusEmoji} Governança: ${profile.governanceScore.toFixed(0)}% | Confiança: ${(profile.confidence * 100).toFixed(0)}%`,
|
|
2097
|
+
},
|
|
2098
|
+
],
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2101
|
+
catch (error) {
|
|
2102
|
+
return {
|
|
2103
|
+
content: [
|
|
2104
|
+
{
|
|
2105
|
+
type: 'text',
|
|
2106
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2107
|
+
},
|
|
2108
|
+
],
|
|
2109
|
+
isError: true,
|
|
2110
|
+
};
|
|
2111
|
+
}
|
|
2112
|
+
});
|
|
2113
|
+
server.registerTool('bos_behavior_report', {
|
|
2114
|
+
description: 'Generate a full markdown behavioral report for an agent',
|
|
2115
|
+
inputSchema: z.object({
|
|
2116
|
+
agentId: z.string().describe('Agent ID'),
|
|
2117
|
+
persona: z.string().describe('Agent persona'),
|
|
2118
|
+
dnaPattern: z.string().describe('DNA pattern name'),
|
|
2119
|
+
includeDiff: z.boolean().optional().describe('Include latest diff (default false)'),
|
|
2120
|
+
}),
|
|
2121
|
+
annotations: { readOnlyHint: true, title: 'Behavior Report' },
|
|
2122
|
+
}, async ({ agentId, persona, dnaPattern, includeDiff = false }) => {
|
|
2123
|
+
try {
|
|
2124
|
+
const bos = getBehaviorOS();
|
|
2125
|
+
const dnas = await loadDNAPatterns();
|
|
2126
|
+
const dna = dnas[dnaPattern.toLowerCase()];
|
|
2127
|
+
if (!dna) {
|
|
2128
|
+
return {
|
|
2129
|
+
content: [{ type: 'text', text: `DNA "${dnaPattern}" not found` }],
|
|
2130
|
+
isError: true,
|
|
2131
|
+
};
|
|
2132
|
+
}
|
|
2133
|
+
const ux = bos.getBehavioralUX();
|
|
2134
|
+
const learnings = bos.getLearningEvents();
|
|
2135
|
+
const missions = bos.listMissions(50);
|
|
2136
|
+
const profile = ux.generateProfile(agentId, persona, dnaPattern, dna, learnings, missions);
|
|
2137
|
+
const latestDiff = includeDiff
|
|
2138
|
+
? ux.getAllDiffs().find((d) => d.newerDna === dnaPattern)
|
|
2139
|
+
: undefined;
|
|
2140
|
+
const md = ux.generateMarkdownReport(agentId, profile, latestDiff);
|
|
2141
|
+
return { content: [{ type: 'text', text: md }] };
|
|
2142
|
+
}
|
|
2143
|
+
catch (error) {
|
|
2144
|
+
return {
|
|
2145
|
+
content: [
|
|
2146
|
+
{
|
|
2147
|
+
type: 'text',
|
|
2148
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2149
|
+
},
|
|
2150
|
+
],
|
|
2151
|
+
isError: true,
|
|
2152
|
+
};
|
|
2153
|
+
}
|
|
2154
|
+
});
|
|
2155
|
+
// ============================================================
|
|
2156
|
+
// ANTI-PATTERN & PRAGMATIC CONVERSATION TOOLS
|
|
2157
|
+
// ============================================================
|
|
2158
|
+
server.registerTool('bos_scan_antipatterns', {
|
|
2159
|
+
description: 'Scan code/text for anti-patterns (TS errors, lint violations, security issues)',
|
|
2160
|
+
inputSchema: z.object({
|
|
2161
|
+
text: z.string().describe('Code or text to scan'),
|
|
2162
|
+
source: z.string().optional().describe('Source location/file'),
|
|
2163
|
+
}),
|
|
2164
|
+
annotations: { readOnlyHint: true, title: 'Scan Anti-Patterns' },
|
|
2165
|
+
}, async ({ text, source }) => {
|
|
2166
|
+
try {
|
|
2167
|
+
const bos = getBehaviorOS();
|
|
2168
|
+
const engine = bos.getAntipatternEngine();
|
|
2169
|
+
if (!engine)
|
|
2170
|
+
return {
|
|
2171
|
+
content: [{ type: 'text', text: 'Antipattern engine not initialized' }],
|
|
2172
|
+
isError: true,
|
|
2173
|
+
};
|
|
2174
|
+
const result = engine.scan(text, source);
|
|
2175
|
+
let md = `# 🛡️ Anti-Pattern Scan Results\n\n## Summary\n- Critical: ${result.summary.critical}\n- High: ${result.summary.high}\n- Medium: ${result.summary.medium}\n- Low: ${result.summary.low}\n- Info: ${result.summary.info}\n- **Total: ${result.summary.total}**\n\n`;
|
|
2176
|
+
if (result.patterns.length === 0) {
|
|
2177
|
+
md += `✅ **No anti-patterns detected!** Clean code.\n`;
|
|
2178
|
+
}
|
|
2179
|
+
else {
|
|
2180
|
+
for (const p of result.patterns) {
|
|
2181
|
+
const emoji = p.severity === 'critical'
|
|
2182
|
+
? '🔴'
|
|
2183
|
+
: p.severity === 'high'
|
|
2184
|
+
? '🟠'
|
|
2185
|
+
: p.severity === 'medium'
|
|
2186
|
+
? '🟡'
|
|
2187
|
+
: '🟢';
|
|
2188
|
+
md += `${emoji} **${p.severity.toUpperCase()}** — ${p.type}\n\n`;
|
|
2189
|
+
md += `- Pattern: \`${p.pattern}\`\n`;
|
|
2190
|
+
md += `- Description: ${p.description}\n`;
|
|
2191
|
+
md += `- Recommendation: ${p.recommendation}\n\n---\n\n`;
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
return { content: [{ type: 'text', text: md }] };
|
|
2195
|
+
}
|
|
2196
|
+
catch (error) {
|
|
2197
|
+
return {
|
|
2198
|
+
content: [
|
|
2199
|
+
{
|
|
2200
|
+
type: 'text',
|
|
2201
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2202
|
+
},
|
|
2203
|
+
],
|
|
2204
|
+
isError: true,
|
|
2205
|
+
};
|
|
2206
|
+
}
|
|
2207
|
+
});
|
|
2208
|
+
server.registerTool('bos_fix_antipatterns', {
|
|
2209
|
+
description: 'Auto-fix critical and high severity anti-patterns',
|
|
2210
|
+
inputSchema: z.object({}).passthrough().optional(),
|
|
2211
|
+
annotations: { readOnlyHint: false, title: 'Fix Anti-Patterns' },
|
|
2212
|
+
}, async () => {
|
|
2213
|
+
try {
|
|
2214
|
+
const bos = getBehaviorOS();
|
|
2215
|
+
const engine = bos.getAntipatternEngine();
|
|
2216
|
+
if (!engine)
|
|
2217
|
+
return {
|
|
2218
|
+
content: [{ type: 'text', text: 'Antipattern engine not initialized' }],
|
|
2219
|
+
isError: true,
|
|
2220
|
+
};
|
|
2221
|
+
const fixed = engine.autoFixCritical();
|
|
2222
|
+
return {
|
|
2223
|
+
content: [
|
|
2224
|
+
{
|
|
2225
|
+
type: 'text',
|
|
2226
|
+
text: `✅ Fixed ${fixed.length} critical/high anti-patterns automatically.`,
|
|
2227
|
+
},
|
|
2228
|
+
],
|
|
2229
|
+
};
|
|
2230
|
+
}
|
|
2231
|
+
catch (error) {
|
|
2232
|
+
return {
|
|
2233
|
+
content: [
|
|
2234
|
+
{
|
|
2235
|
+
type: 'text',
|
|
2236
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2237
|
+
},
|
|
2238
|
+
],
|
|
2239
|
+
isError: true,
|
|
2240
|
+
};
|
|
2241
|
+
}
|
|
2242
|
+
});
|
|
2243
|
+
server.registerTool('bos_antipattern_report', {
|
|
2244
|
+
description: 'Generate full anti-pattern remediation report',
|
|
2245
|
+
inputSchema: z.object({}).passthrough().optional(),
|
|
2246
|
+
annotations: { readOnlyHint: true, title: 'Anti-Pattern Report' },
|
|
2247
|
+
}, async () => {
|
|
2248
|
+
try {
|
|
2249
|
+
const bos = getBehaviorOS();
|
|
2250
|
+
const engine = bos.getAntipatternEngine();
|
|
2251
|
+
if (!engine)
|
|
2252
|
+
return {
|
|
2253
|
+
content: [{ type: 'text', text: 'Antipattern engine not initialized' }],
|
|
2254
|
+
isError: true,
|
|
2255
|
+
};
|
|
2256
|
+
const report = engine.generateReport();
|
|
2257
|
+
return { content: [{ type: 'text', text: report }] };
|
|
2258
|
+
}
|
|
2259
|
+
catch (error) {
|
|
2260
|
+
return {
|
|
2261
|
+
content: [
|
|
2262
|
+
{
|
|
2263
|
+
type: 'text',
|
|
2264
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2265
|
+
},
|
|
2266
|
+
],
|
|
2267
|
+
isError: true,
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2270
|
+
});
|
|
2271
|
+
server.registerTool('bos_start_conversation', {
|
|
2272
|
+
description: 'Start a pragmatic conversation between agents to solve a problem',
|
|
2273
|
+
inputSchema: z.object({
|
|
2274
|
+
title: z.string().describe('Conversation title'),
|
|
2275
|
+
problem: z.string().describe('Problem description'),
|
|
2276
|
+
participants: z.array(z.string()).describe('Agent IDs participating'),
|
|
2277
|
+
}),
|
|
2278
|
+
annotations: { readOnlyHint: false, title: 'Start Conversation' },
|
|
2279
|
+
}, async ({ title, problem, participants }) => {
|
|
2280
|
+
try {
|
|
2281
|
+
const bos = getBehaviorOS();
|
|
2282
|
+
const engine = bos.getPragmaticConversation();
|
|
2283
|
+
if (!engine)
|
|
2284
|
+
return {
|
|
2285
|
+
content: [{ type: 'text', text: 'Pragmatic conversation engine not initialized' }],
|
|
2286
|
+
isError: true,
|
|
2287
|
+
};
|
|
2288
|
+
const result = engine.startSession({ title, problem, participants });
|
|
2289
|
+
if (!result.success) {
|
|
2290
|
+
return { content: [{ type: 'text', text: `Error: ${result.error}` }], isError: true };
|
|
2291
|
+
}
|
|
2292
|
+
return {
|
|
2293
|
+
content: [
|
|
2294
|
+
{
|
|
2295
|
+
type: 'text',
|
|
2296
|
+
text: `✅ Pragmatic conversation started!\n\n**Session ID:** ${result.session?.id}\n**Title:** ${title}\n**Problem:** ${problem}\n**Participants:** ${participants.join(', ')}`,
|
|
2297
|
+
},
|
|
2298
|
+
],
|
|
2299
|
+
};
|
|
2300
|
+
}
|
|
2301
|
+
catch (error) {
|
|
2302
|
+
return {
|
|
2303
|
+
content: [
|
|
2304
|
+
{
|
|
2305
|
+
type: 'text',
|
|
2306
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2307
|
+
},
|
|
2308
|
+
],
|
|
2309
|
+
isError: true,
|
|
2310
|
+
};
|
|
2311
|
+
}
|
|
2312
|
+
});
|
|
2313
|
+
server.registerTool('bos_add_conversation_turn', {
|
|
2314
|
+
description: 'Add a turn to a pragmatic conversation',
|
|
2315
|
+
inputSchema: z.object({
|
|
2316
|
+
sessionId: z.string().describe('Conversation session ID'),
|
|
2317
|
+
agentId: z.string().describe('Agent contributing'),
|
|
2318
|
+
content: z.string().describe('Message content'),
|
|
2319
|
+
role: z
|
|
2320
|
+
.enum(['system', 'user', 'agent', 'expert'])
|
|
2321
|
+
.optional()
|
|
2322
|
+
.describe('Role of the contributor'),
|
|
2323
|
+
}),
|
|
2324
|
+
annotations: { readOnlyHint: false, title: 'Add Turn' },
|
|
2325
|
+
}, async ({ sessionId, agentId, content, role = 'agent' }) => {
|
|
2326
|
+
try {
|
|
2327
|
+
const bos = getBehaviorOS();
|
|
2328
|
+
const engine = bos.getPragmaticConversation();
|
|
2329
|
+
if (!engine)
|
|
2330
|
+
return {
|
|
2331
|
+
content: [{ type: 'text', text: 'Pragmatic conversation engine not initialized' }],
|
|
2332
|
+
isError: true,
|
|
2333
|
+
};
|
|
2334
|
+
const result = engine.addTurn(sessionId, agentId, content, role);
|
|
2335
|
+
if (!result.success) {
|
|
2336
|
+
return { content: [{ type: 'text', text: `Error: ${result.error}` }], isError: true };
|
|
2337
|
+
}
|
|
2338
|
+
return {
|
|
2339
|
+
content: [
|
|
2340
|
+
{
|
|
2341
|
+
type: 'text',
|
|
2342
|
+
text: `✅ Turn added by ${agentId}\n\n**Content:** ${content.slice(0, 100)}${content.length > 100 ? '...' : ''}`,
|
|
2343
|
+
},
|
|
2344
|
+
],
|
|
2345
|
+
};
|
|
2346
|
+
}
|
|
2347
|
+
catch (error) {
|
|
2348
|
+
return {
|
|
2349
|
+
content: [
|
|
2350
|
+
{
|
|
2351
|
+
type: 'text',
|
|
2352
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2353
|
+
},
|
|
2354
|
+
],
|
|
2355
|
+
isError: true,
|
|
2356
|
+
};
|
|
2357
|
+
}
|
|
2358
|
+
});
|
|
2359
|
+
server.registerTool('bos_resolve_conversation', {
|
|
2360
|
+
description: 'Resolve a conversation with solution and lessons learned',
|
|
2361
|
+
inputSchema: z.object({
|
|
2362
|
+
sessionId: z.string().describe('Conversation session ID'),
|
|
2363
|
+
solution: z.string().describe('Solution found'),
|
|
2364
|
+
lessonsLearned: z.array(z.string()).describe('Lessons to persist'),
|
|
2365
|
+
resolvedBy: z.string().describe('Agent who resolved'),
|
|
2366
|
+
}),
|
|
2367
|
+
annotations: { readOnlyHint: false, title: 'Resolve Conversation' },
|
|
2368
|
+
}, async ({ sessionId, solution, lessonsLearned, resolvedBy }) => {
|
|
2369
|
+
try {
|
|
2370
|
+
const bos = getBehaviorOS();
|
|
2371
|
+
const engine = bos.getPragmaticConversation();
|
|
2372
|
+
if (!engine)
|
|
2373
|
+
return {
|
|
2374
|
+
content: [{ type: 'text', text: 'Pragmatic conversation engine not initialized' }],
|
|
2375
|
+
isError: true,
|
|
2376
|
+
};
|
|
2377
|
+
const result = engine.resolveConversation(sessionId, solution, lessonsLearned, resolvedBy);
|
|
2378
|
+
if (!result.success) {
|
|
2379
|
+
return { content: [{ type: 'text', text: `Error: ${result.error}` }], isError: true };
|
|
2380
|
+
}
|
|
2381
|
+
// Record as learning (DRY: reuse the facade's governed recordLearning).
|
|
2382
|
+
bos.recordLearning({
|
|
2383
|
+
type: 'fix',
|
|
2384
|
+
source: 'pragmatic-conversation',
|
|
2385
|
+
content: `Solved: ${solution}`,
|
|
2386
|
+
impact: 'high',
|
|
2387
|
+
relatedMission: sessionId,
|
|
2388
|
+
});
|
|
2389
|
+
return {
|
|
2390
|
+
content: [
|
|
2391
|
+
{
|
|
2392
|
+
type: 'text',
|
|
2393
|
+
text: `✅ Conversation resolved!\n\n**Solution:** ${solution}\n**Lessons:** ${lessonsLearned.length} recorded`,
|
|
2394
|
+
},
|
|
2395
|
+
],
|
|
2396
|
+
};
|
|
2397
|
+
}
|
|
2398
|
+
catch (error) {
|
|
2399
|
+
return {
|
|
2400
|
+
content: [
|
|
2401
|
+
{
|
|
2402
|
+
type: 'text',
|
|
2403
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2404
|
+
},
|
|
2405
|
+
],
|
|
2406
|
+
isError: true,
|
|
2407
|
+
};
|
|
2408
|
+
}
|
|
2409
|
+
});
|
|
2410
|
+
server.registerTool('bos_conversation_report', {
|
|
2411
|
+
description: 'Generate full report for a pragmatic conversation',
|
|
2412
|
+
inputSchema: z.object({
|
|
2413
|
+
sessionId: z.string().describe('Conversation session ID'),
|
|
2414
|
+
}),
|
|
2415
|
+
annotations: { readOnlyHint: true, title: 'Conversation Report' },
|
|
2416
|
+
}, async ({ sessionId }) => {
|
|
2417
|
+
try {
|
|
2418
|
+
const bos = getBehaviorOS();
|
|
2419
|
+
const engine = bos.getPragmaticConversation();
|
|
2420
|
+
if (!engine)
|
|
2421
|
+
return {
|
|
2422
|
+
content: [{ type: 'text', text: 'Pragmatic conversation engine not initialized' }],
|
|
2423
|
+
isError: true,
|
|
2424
|
+
};
|
|
2425
|
+
const report = engine.generateReport(sessionId);
|
|
2426
|
+
if (!report) {
|
|
2427
|
+
return {
|
|
2428
|
+
content: [{ type: 'text', text: `Session "${sessionId}" not found` }],
|
|
2429
|
+
isError: true,
|
|
2430
|
+
};
|
|
2431
|
+
}
|
|
2432
|
+
return { content: [{ type: 'text', text: report }] };
|
|
2433
|
+
}
|
|
2434
|
+
catch (error) {
|
|
2435
|
+
return {
|
|
2436
|
+
content: [
|
|
2437
|
+
{
|
|
2438
|
+
type: 'text',
|
|
2439
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2440
|
+
},
|
|
2441
|
+
],
|
|
2442
|
+
isError: true,
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2445
|
+
});
|
|
2446
|
+
server.registerTool('bos_list_conversations', {
|
|
2447
|
+
description: 'List all pragmatic conversations',
|
|
2448
|
+
inputSchema: z.object({}).passthrough().optional(),
|
|
2449
|
+
annotations: { readOnlyHint: true, title: 'List Conversations' },
|
|
2450
|
+
}, async () => {
|
|
2451
|
+
try {
|
|
2452
|
+
const bos = getBehaviorOS();
|
|
2453
|
+
const engine = bos.getPragmaticConversation();
|
|
2454
|
+
if (!engine)
|
|
2455
|
+
return {
|
|
2456
|
+
content: [{ type: 'text', text: 'Pragmatic conversation engine not initialized' }],
|
|
2457
|
+
isError: true,
|
|
2458
|
+
};
|
|
2459
|
+
const sessions = engine.listSessions();
|
|
2460
|
+
return { content: [{ type: 'text', text: JSON.stringify(sessions, null, 2) }] };
|
|
2461
|
+
}
|
|
2462
|
+
catch (error) {
|
|
2463
|
+
return {
|
|
2464
|
+
content: [
|
|
2465
|
+
{
|
|
2466
|
+
type: 'text',
|
|
2467
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2468
|
+
},
|
|
2469
|
+
],
|
|
2470
|
+
isError: true,
|
|
2471
|
+
};
|
|
2472
|
+
}
|
|
2473
|
+
});
|
|
2474
|
+
// ============================================================
|
|
2475
|
+
// ROLE & POLICY GOVERNANCE TOOLS
|
|
2476
|
+
// ============================================================
|
|
2477
|
+
server.registerTool('bos_register_role', {
|
|
2478
|
+
description: 'Register a role with attached policy-rule ids for role-based governance',
|
|
2479
|
+
inputSchema: z.object({
|
|
2480
|
+
id: z.string().describe('Role id (how actors resolve to this role)'),
|
|
2481
|
+
name: z.string().describe('Role name'),
|
|
2482
|
+
displayName: z.string().describe('Human-readable display name'),
|
|
2483
|
+
responsibilities: z.array(z.string()).describe('Responsibility list'),
|
|
2484
|
+
policies: z.array(z.string()).optional().describe('Policy-rule ids attached to this role'),
|
|
2485
|
+
canAudit: z.boolean().optional().describe('Whether the role may audit'),
|
|
2486
|
+
canEnforce: z.boolean().optional().describe('Whether the role may enforce'),
|
|
2487
|
+
canTeach: z.boolean().optional().describe('Whether the role may teach behaviors'),
|
|
2488
|
+
}),
|
|
2489
|
+
annotations: { readOnlyHint: false, title: 'Register Role' },
|
|
2490
|
+
}, async ({ id, name, displayName, responsibilities, policies, canAudit, canEnforce, canTeach }) => {
|
|
2491
|
+
try {
|
|
2492
|
+
const bos = getBehaviorOS();
|
|
2493
|
+
const result = bos.registerRole({
|
|
2494
|
+
id,
|
|
2495
|
+
name,
|
|
2496
|
+
displayName,
|
|
2497
|
+
responsibilities,
|
|
2498
|
+
policies,
|
|
2499
|
+
canAudit: canAudit ?? false,
|
|
2500
|
+
canEnforce: canEnforce ?? false,
|
|
2501
|
+
canTeach: canTeach ?? false,
|
|
2502
|
+
});
|
|
2503
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
2504
|
+
}
|
|
2505
|
+
catch (error) {
|
|
2506
|
+
return {
|
|
2507
|
+
content: [
|
|
2508
|
+
{
|
|
2509
|
+
type: 'text',
|
|
2510
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2511
|
+
},
|
|
2512
|
+
],
|
|
2513
|
+
isError: true,
|
|
2514
|
+
};
|
|
2515
|
+
}
|
|
2516
|
+
});
|
|
2517
|
+
server.registerTool('bos_define_policy', {
|
|
2518
|
+
description: 'Define a policy rule (condition/action/severity/applies-to) used by registered roles',
|
|
2519
|
+
inputSchema: z.object({
|
|
2520
|
+
id: z.string().describe('Policy-rule id'),
|
|
2521
|
+
name: z.string().describe('Policy name'),
|
|
2522
|
+
description: z.string().describe('What the policy demands and why'),
|
|
2523
|
+
condition: z
|
|
2524
|
+
.string()
|
|
2525
|
+
.describe("Action matcher: '*' for any action, or an action prefix (e.g. 'deploy', 'git push')"),
|
|
2526
|
+
action: z
|
|
2527
|
+
.enum(['require', 'warn', 'block', 'auto-fix'])
|
|
2528
|
+
.describe('Deterministic enforcement action'),
|
|
2529
|
+
severity: z
|
|
2530
|
+
.enum(['critical', 'high', 'medium', 'low', 'info'])
|
|
2531
|
+
.describe('Severity of a violation'),
|
|
2532
|
+
appliesTo: z
|
|
2533
|
+
.array(z.enum(['persona', 'role', 'agent', 'project']))
|
|
2534
|
+
.describe('Which actor facets the rule governs'),
|
|
2535
|
+
effectiveFrom: z.string().describe('ISO date from which the rule is effective'),
|
|
2536
|
+
effectiveUntil: z.string().optional().describe('Optional ISO expiry date'),
|
|
2537
|
+
}),
|
|
2538
|
+
annotations: { readOnlyHint: false, title: 'Define Policy' },
|
|
2539
|
+
}, async ({ id, name, description, condition, action, severity, appliesTo, effectiveFrom, effectiveUntil, }) => {
|
|
2540
|
+
try {
|
|
2541
|
+
const bos = getBehaviorOS();
|
|
2542
|
+
const result = bos.defineRolePolicy({
|
|
2543
|
+
id,
|
|
2544
|
+
name,
|
|
2545
|
+
description,
|
|
2546
|
+
condition,
|
|
2547
|
+
action,
|
|
2548
|
+
severity,
|
|
2549
|
+
appliesTo,
|
|
2550
|
+
effectiveFrom,
|
|
2551
|
+
effectiveUntil,
|
|
2552
|
+
});
|
|
2553
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
2554
|
+
}
|
|
2555
|
+
catch (error) {
|
|
2556
|
+
return {
|
|
2557
|
+
content: [
|
|
2558
|
+
{
|
|
2559
|
+
type: 'text',
|
|
2560
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2561
|
+
},
|
|
2562
|
+
],
|
|
2563
|
+
isError: true,
|
|
2564
|
+
};
|
|
2565
|
+
}
|
|
2566
|
+
});
|
|
2567
|
+
server.registerTool('bos_evaluate_role_action', {
|
|
2568
|
+
description: 'Evaluate an action against the role/policy registry — returns the deterministic decision',
|
|
2569
|
+
inputSchema: z.object({
|
|
2570
|
+
role: z.string().optional().describe('Actor role id or persona'),
|
|
2571
|
+
agentId: z.string().optional().describe('Actor agent id'),
|
|
2572
|
+
action: z.string().describe('The action about to be performed (e.g. deploy, git push)'),
|
|
2573
|
+
}),
|
|
2574
|
+
annotations: { readOnlyHint: true, title: 'Evaluate Role Action' },
|
|
2575
|
+
}, async ({ role, agentId, action }) => {
|
|
2576
|
+
try {
|
|
2577
|
+
const bos = getBehaviorOS();
|
|
2578
|
+
const decision = bos.evaluateRoleAction({ role, agentId }, action);
|
|
2579
|
+
return { content: [{ type: 'text', text: JSON.stringify(decision, null, 2) }] };
|
|
2580
|
+
}
|
|
2581
|
+
catch (error) {
|
|
2582
|
+
return {
|
|
2583
|
+
content: [
|
|
2584
|
+
{
|
|
2585
|
+
type: 'text',
|
|
2586
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2587
|
+
},
|
|
2588
|
+
],
|
|
2589
|
+
isError: true,
|
|
2590
|
+
};
|
|
2591
|
+
}
|
|
2592
|
+
});
|
|
2593
|
+
server.registerTool('bos_role_policy_report', {
|
|
2594
|
+
description: 'Per-role audit report: decision counts by action and by role, plus the last decision',
|
|
2595
|
+
inputSchema: z.object({}),
|
|
2596
|
+
annotations: { readOnlyHint: true, title: 'Role Policy Report' },
|
|
2597
|
+
}, async () => {
|
|
2598
|
+
try {
|
|
2599
|
+
const bos = getBehaviorOS();
|
|
2600
|
+
const report = bos.getRolePolicyEngine().getReport();
|
|
2601
|
+
return { content: [{ type: 'text', text: JSON.stringify(report, null, 2) }] };
|
|
2602
|
+
}
|
|
2603
|
+
catch (error) {
|
|
2604
|
+
return {
|
|
2605
|
+
content: [
|
|
2606
|
+
{
|
|
2607
|
+
type: 'text',
|
|
2608
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2609
|
+
},
|
|
2610
|
+
],
|
|
2611
|
+
isError: true,
|
|
2612
|
+
};
|
|
2613
|
+
}
|
|
2614
|
+
});
|
|
2615
|
+
server.registerTool('bos_list_roles', {
|
|
2616
|
+
description: 'List registered roles and policy rules of the role/policy engine',
|
|
2617
|
+
inputSchema: z.object({}),
|
|
2618
|
+
annotations: { readOnlyHint: true, title: 'List Roles' },
|
|
2619
|
+
}, async () => {
|
|
2620
|
+
try {
|
|
2621
|
+
const bos = getBehaviorOS();
|
|
2622
|
+
const engine = bos.getRolePolicyEngine();
|
|
2623
|
+
const result = { roles: engine.listRoles(), policies: engine.listPolicies() };
|
|
2624
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
2625
|
+
}
|
|
2626
|
+
catch (error) {
|
|
2627
|
+
return {
|
|
2628
|
+
content: [
|
|
2629
|
+
{
|
|
2630
|
+
type: 'text',
|
|
2631
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2632
|
+
},
|
|
2633
|
+
],
|
|
2634
|
+
isError: true,
|
|
2635
|
+
};
|
|
2636
|
+
}
|
|
2637
|
+
});
|
|
2638
|
+
// ============================================================
|
|
2639
|
+
// COGNITIVE LOAD BALANCER TOOLS (Feature 5: Balanceamento Cognitivo)
|
|
2640
|
+
// ============================================================
|
|
2641
|
+
server.registerTool('bos_register_agent_capacity', {
|
|
2642
|
+
description: 'Register an agent with cognitive capacity and skills for load balancing',
|
|
2643
|
+
inputSchema: z.object({
|
|
2644
|
+
agentId: z.string().describe('Agent ID'),
|
|
2645
|
+
maxCapacity: z
|
|
2646
|
+
.number()
|
|
2647
|
+
.min(0)
|
|
2648
|
+
.max(1)
|
|
2649
|
+
.default(0.8)
|
|
2650
|
+
.describe('Maximum capacity before overload (0-1)'),
|
|
2651
|
+
skills: z.array(z.string()).default([]).describe('Agent skills'),
|
|
2652
|
+
completionRate: z
|
|
2653
|
+
.number()
|
|
2654
|
+
.min(0)
|
|
2655
|
+
.max(1)
|
|
2656
|
+
.default(0.8)
|
|
2657
|
+
.describe('Historical task completion rate'),
|
|
2658
|
+
fatigue: z.number().min(0).max(1).default(0).describe('Current fatigue factor (0-1)'),
|
|
2659
|
+
}),
|
|
2660
|
+
annotations: { readOnlyHint: false, title: 'Register Agent Capacity' },
|
|
2661
|
+
}, async ({ agentId, maxCapacity, skills, completionRate, fatigue }) => {
|
|
2662
|
+
try {
|
|
2663
|
+
const bos = getBehaviorOS();
|
|
2664
|
+
bos.getCognitiveLoadBalancer().registerAgent(agentId, {
|
|
2665
|
+
maxCapacity,
|
|
2666
|
+
skills,
|
|
2667
|
+
completionRate,
|
|
2668
|
+
fatigue,
|
|
2669
|
+
});
|
|
2670
|
+
return {
|
|
2671
|
+
content: [{ type: 'text', text: `✅ Agent "${agentId}" registered for load balancing` }],
|
|
2672
|
+
};
|
|
2673
|
+
}
|
|
2674
|
+
catch (error) {
|
|
2675
|
+
return {
|
|
2676
|
+
content: [
|
|
2677
|
+
{
|
|
2678
|
+
type: 'text',
|
|
2679
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2680
|
+
},
|
|
2681
|
+
],
|
|
2682
|
+
isError: true,
|
|
2683
|
+
};
|
|
2684
|
+
}
|
|
2685
|
+
});
|
|
2686
|
+
server.registerTool('bos_assign_task', {
|
|
2687
|
+
description: 'Assign a task to an agent, tracking cognitive load',
|
|
2688
|
+
inputSchema: z.object({
|
|
2689
|
+
agentId: z.string().describe('Agent ID'),
|
|
2690
|
+
taskId: z.string().describe('Task ID'),
|
|
2691
|
+
}),
|
|
2692
|
+
annotations: { readOnlyHint: false, title: 'Assign Task' },
|
|
2693
|
+
}, async ({ agentId, taskId }) => {
|
|
2694
|
+
try {
|
|
2695
|
+
const bos = getBehaviorOS();
|
|
2696
|
+
bos.getCognitiveLoadBalancer().assignTask(agentId, taskId);
|
|
2697
|
+
return {
|
|
2698
|
+
content: [{ type: 'text', text: `✅ Task "${taskId}" assigned to agent "${agentId}"` }],
|
|
2699
|
+
};
|
|
2700
|
+
}
|
|
2701
|
+
catch (error) {
|
|
2702
|
+
return {
|
|
2703
|
+
content: [
|
|
2704
|
+
{
|
|
2705
|
+
type: 'text',
|
|
2706
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2707
|
+
},
|
|
2708
|
+
],
|
|
2709
|
+
isError: true,
|
|
2710
|
+
};
|
|
2711
|
+
}
|
|
2712
|
+
});
|
|
2713
|
+
server.registerTool('bos_complete_task', {
|
|
2714
|
+
description: 'Mark a task as completed, reducing the agent cognitive load',
|
|
2715
|
+
inputSchema: z.object({
|
|
2716
|
+
agentId: z.string().describe('Agent ID'),
|
|
2717
|
+
taskId: z.string().describe('Task ID'),
|
|
2718
|
+
}),
|
|
2719
|
+
annotations: { readOnlyHint: false, title: 'Complete Task' },
|
|
2720
|
+
}, async ({ agentId, taskId }) => {
|
|
2721
|
+
try {
|
|
2722
|
+
const bos = getBehaviorOS();
|
|
2723
|
+
bos.getCognitiveLoadBalancer().completeTask(agentId, taskId);
|
|
2724
|
+
return {
|
|
2725
|
+
content: [{ type: 'text', text: `✅ Task "${taskId}" completed for agent "${agentId}"` }],
|
|
2726
|
+
};
|
|
2727
|
+
}
|
|
2728
|
+
catch (error) {
|
|
2729
|
+
return {
|
|
2730
|
+
content: [
|
|
2731
|
+
{
|
|
2732
|
+
type: 'text',
|
|
2733
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2734
|
+
},
|
|
2735
|
+
],
|
|
2736
|
+
isError: true,
|
|
2737
|
+
};
|
|
2738
|
+
}
|
|
2739
|
+
});
|
|
2740
|
+
server.registerTool('bos_analyze_workload', {
|
|
2741
|
+
description: 'Analyze workload distribution and get balancing recommendations',
|
|
2742
|
+
inputSchema: z
|
|
2743
|
+
.object({
|
|
2744
|
+
maxLoadThreshold: z.number().optional().describe('Max load threshold (default 0.75)'),
|
|
2745
|
+
minLoadThreshold: z.number().optional().describe('Min load threshold (default 0.25)'),
|
|
2746
|
+
considerSkills: z.boolean().optional().describe('Consider skills in analysis'),
|
|
2747
|
+
considerFatigue: z.boolean().optional().describe('Consider fatigue in analysis'),
|
|
2748
|
+
})
|
|
2749
|
+
.passthrough()
|
|
2750
|
+
.optional(),
|
|
2751
|
+
annotations: { readOnlyHint: true, title: 'Analyze Workload' },
|
|
2752
|
+
}, async (params) => {
|
|
2753
|
+
try {
|
|
2754
|
+
const bos = getBehaviorOS();
|
|
2755
|
+
const summary = bos.getCognitiveLoadBalancer().analyzeWorkload({
|
|
2756
|
+
maxLoadThreshold: params?.maxLoadThreshold ?? 0.75,
|
|
2757
|
+
minLoadThreshold: params?.minLoadThreshold ?? 0.25,
|
|
2758
|
+
considerSkills: params?.considerSkills ?? true,
|
|
2759
|
+
considerFatigue: params?.considerFatigue ?? true,
|
|
2760
|
+
});
|
|
2761
|
+
return { content: [{ type: 'text', text: JSON.stringify(summary, null, 2) }] };
|
|
2762
|
+
}
|
|
2763
|
+
catch (error) {
|
|
2764
|
+
return {
|
|
2765
|
+
content: [
|
|
2766
|
+
{
|
|
2767
|
+
type: 'text',
|
|
2768
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2769
|
+
},
|
|
2770
|
+
],
|
|
2771
|
+
isError: true,
|
|
2772
|
+
};
|
|
2773
|
+
}
|
|
2774
|
+
});
|
|
2775
|
+
server.registerTool('bos_recommend_assignment', {
|
|
2776
|
+
description: 'Recommend the best agent for a task based on load and skills',
|
|
2777
|
+
inputSchema: z.object({
|
|
2778
|
+
taskId: z.string().describe('Task ID'),
|
|
2779
|
+
requiredSkills: z.array(z.string()).describe('Required skills for the task'),
|
|
2780
|
+
}),
|
|
2781
|
+
annotations: { readOnlyHint: true, title: 'Recommend Task Assignment' },
|
|
2782
|
+
}, async ({ taskId, requiredSkills }) => {
|
|
2783
|
+
try {
|
|
2784
|
+
const bos = getBehaviorOS();
|
|
2785
|
+
const recommendation = bos
|
|
2786
|
+
.getCognitiveLoadBalancer()
|
|
2787
|
+
.recommendAssignment(taskId, requiredSkills);
|
|
2788
|
+
return { content: [{ type: 'text', text: JSON.stringify(recommendation, null, 2) }] };
|
|
2789
|
+
}
|
|
2790
|
+
catch (error) {
|
|
2791
|
+
return {
|
|
2792
|
+
content: [
|
|
2793
|
+
{
|
|
2794
|
+
type: 'text',
|
|
2795
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2796
|
+
},
|
|
2797
|
+
],
|
|
2798
|
+
isError: true,
|
|
2799
|
+
};
|
|
2800
|
+
}
|
|
2801
|
+
});
|
|
2802
|
+
// ============================================================
|
|
2803
|
+
// NATURAL LANGUAGE RULES TOOLS (Feature 6: Regras em Língua Natural)
|
|
2804
|
+
// ============================================================
|
|
2805
|
+
server.registerTool('bos_parse_rule', {
|
|
2806
|
+
description: 'Parse a natural language governance rule into structured form',
|
|
2807
|
+
inputSchema: z.object({
|
|
2808
|
+
rule: z
|
|
2809
|
+
.string()
|
|
2810
|
+
.describe('Natural language rule, e.g. "Block production deploys when tests are failing"'),
|
|
2811
|
+
ruleId: z.string().optional().describe('Optional custom rule ID'),
|
|
2812
|
+
}),
|
|
2813
|
+
annotations: { readOnlyHint: true, title: 'Parse Natural Language Rule' },
|
|
2814
|
+
}, async ({ rule, ruleId }) => {
|
|
2815
|
+
try {
|
|
2816
|
+
const bos = getBehaviorOS();
|
|
2817
|
+
const result = bos.getNaturalLanguageEngine().parseRule(rule, ruleId ? { ruleId } : {});
|
|
2818
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
2819
|
+
}
|
|
2820
|
+
catch (error) {
|
|
2821
|
+
return {
|
|
2822
|
+
content: [
|
|
2823
|
+
{
|
|
2824
|
+
type: 'text',
|
|
2825
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2826
|
+
},
|
|
2827
|
+
],
|
|
2828
|
+
isError: true,
|
|
2829
|
+
};
|
|
2830
|
+
}
|
|
2831
|
+
});
|
|
2832
|
+
server.registerTool('bos_parse_rules_batch', {
|
|
2833
|
+
description: 'Parse multiple natural language rules in a single call',
|
|
2834
|
+
inputSchema: z.object({
|
|
2835
|
+
rules: z.array(z.string()).describe('Array of natural language rules to parse'),
|
|
2836
|
+
}),
|
|
2837
|
+
annotations: { readOnlyHint: true, title: 'Parse Rules Batch' },
|
|
2838
|
+
}, async ({ rules }) => {
|
|
2839
|
+
try {
|
|
2840
|
+
const bos = getBehaviorOS();
|
|
2841
|
+
const results = bos.getNaturalLanguageEngine().parseRulesBatch(rules);
|
|
2842
|
+
return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
|
|
2843
|
+
}
|
|
2844
|
+
catch (error) {
|
|
2845
|
+
return {
|
|
2846
|
+
content: [
|
|
2847
|
+
{
|
|
2848
|
+
type: 'text',
|
|
2849
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2850
|
+
},
|
|
2851
|
+
],
|
|
2852
|
+
isError: true,
|
|
2853
|
+
};
|
|
2854
|
+
}
|
|
2855
|
+
});
|
|
2856
|
+
server.registerTool('bos_apply_natural_language_rules', {
|
|
2857
|
+
description: 'Parse and apply natural language rules to a DNA pattern',
|
|
2858
|
+
inputSchema: z.object({
|
|
2859
|
+
dnaName: z.string().describe('DNA pattern name to modify'),
|
|
2860
|
+
rules: z.array(z.string()).describe('Natural language rules to apply'),
|
|
2861
|
+
}),
|
|
2862
|
+
annotations: { readOnlyHint: false, title: 'Apply NL Rules to DNA' },
|
|
2863
|
+
}, async ({ dnaName, rules }) => {
|
|
2864
|
+
try {
|
|
2865
|
+
const bos = getBehaviorOS();
|
|
2866
|
+
const dnas = await loadDNAPatterns();
|
|
2867
|
+
const dna = dnas[dnaName.toLowerCase()];
|
|
2868
|
+
if (!dna) {
|
|
2869
|
+
return {
|
|
2870
|
+
content: [{ type: 'text', text: `DNA pattern "${dnaName}" not found` }],
|
|
2871
|
+
isError: true,
|
|
2872
|
+
};
|
|
2873
|
+
}
|
|
2874
|
+
const result = bos.getNaturalLanguageEngine().applyRules(dna, rules);
|
|
2875
|
+
return {
|
|
2876
|
+
content: [
|
|
2877
|
+
{
|
|
2878
|
+
type: 'text',
|
|
2879
|
+
text: JSON.stringify({ applied: result.applied, errors: result.errors }, null, 2),
|
|
2880
|
+
},
|
|
2881
|
+
],
|
|
2882
|
+
};
|
|
2883
|
+
}
|
|
2884
|
+
catch (error) {
|
|
2885
|
+
return {
|
|
2886
|
+
content: [
|
|
2887
|
+
{
|
|
2888
|
+
type: 'text',
|
|
2889
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2890
|
+
},
|
|
2891
|
+
],
|
|
2892
|
+
isError: true,
|
|
2893
|
+
};
|
|
2894
|
+
}
|
|
2895
|
+
});
|
|
2896
|
+
// ============================================================
|
|
2897
|
+
// TEMPORAL GOVERNANCE TOOLS (Feature 7: Governança Temporal)
|
|
2898
|
+
// ============================================================
|
|
2899
|
+
server.registerTool('bos_add_temporal_rule', {
|
|
2900
|
+
description: 'Add a time-based governance rule that is only active during specific time windows (absolute, recurring/cron). Temporal rules wrap existing DNA governance rules with temporal constraints.',
|
|
2901
|
+
inputSchema: z.object({
|
|
2902
|
+
baseRuleId: z.string().describe('ID of the existing DNA governance rule to wrap'),
|
|
2903
|
+
rule: z.object({
|
|
2904
|
+
id: z.string().describe('ID of the temporal rule'),
|
|
2905
|
+
level: z.enum(['critical', 'high', 'medium', 'low']).describe('Severity level'),
|
|
2906
|
+
action: z
|
|
2907
|
+
.enum(['block', 'escalate', 'warn', 'log', 'transform'])
|
|
2908
|
+
.describe('Action to take when active'),
|
|
2909
|
+
conditions: z
|
|
2910
|
+
.array(z.object({ type: z.string(), value: z.unknown() }))
|
|
2911
|
+
.describe('Rule conditions'),
|
|
2912
|
+
description: z.string().optional().describe('Human-readable description'),
|
|
2913
|
+
}),
|
|
2914
|
+
scheduleType: z.enum(['absolute', 'recurring', 'cron']).describe('Schedule type'),
|
|
2915
|
+
startTime: z.string().optional().describe('ISO timestamp (for absolute)'),
|
|
2916
|
+
endTime: z.string().optional().describe('ISO timestamp (for absolute)'),
|
|
2917
|
+
schedule: z.string().optional().describe('Cron expression (e.g. "0 9-17 * * 1-5")'),
|
|
2918
|
+
reason: z.string().describe('Why this temporal rule exists'),
|
|
2919
|
+
authorizedBy: z.string().describe('Who authorized it'),
|
|
2920
|
+
}),
|
|
2921
|
+
annotations: { readOnlyHint: false, title: 'Add Temporal Rule' },
|
|
2922
|
+
}, async ({ baseRuleId, rule, scheduleType, startTime, endTime, schedule, reason, authorizedBy, }) => {
|
|
2923
|
+
try {
|
|
2924
|
+
const bos = getBehaviorOS();
|
|
2925
|
+
const result = bos.addTemporalRule({
|
|
2926
|
+
baseRuleId,
|
|
2927
|
+
rule,
|
|
2928
|
+
scheduleType,
|
|
2929
|
+
startTime,
|
|
2930
|
+
endTime,
|
|
2931
|
+
schedule,
|
|
2932
|
+
reason,
|
|
2933
|
+
authorizedBy,
|
|
2934
|
+
});
|
|
2935
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
2936
|
+
}
|
|
2937
|
+
catch (error) {
|
|
2938
|
+
return {
|
|
2939
|
+
content: [
|
|
2940
|
+
{
|
|
2941
|
+
type: 'text',
|
|
2942
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2943
|
+
},
|
|
2944
|
+
],
|
|
2945
|
+
isError: true,
|
|
2946
|
+
};
|
|
2947
|
+
}
|
|
2948
|
+
});
|
|
2949
|
+
server.registerTool('bos_check_temporal', {
|
|
2950
|
+
description: 'Evaluate temporal rules against a context to see which are active and which recommendations apply.',
|
|
2951
|
+
inputSchema: z.object({
|
|
2952
|
+
action: z.string().describe('The action being evaluated'),
|
|
2953
|
+
agentId: z.string().describe('Agent ID'),
|
|
2954
|
+
context: z.record(z.string(), z.unknown()).optional().describe('Context bag'),
|
|
2955
|
+
missionId: z.string().optional().describe('Related mission ID'),
|
|
2956
|
+
}),
|
|
2957
|
+
annotations: { readOnlyHint: true, title: 'Check Temporal Rules' },
|
|
2958
|
+
}, async ({ action, agentId, context, missionId }) => {
|
|
2959
|
+
try {
|
|
2960
|
+
const bos = getBehaviorOS();
|
|
2961
|
+
const result = bos.evaluateTemporal({
|
|
2962
|
+
action,
|
|
2963
|
+
agentId,
|
|
2964
|
+
context: context ?? {},
|
|
2965
|
+
...(missionId ? { missionId } : {}),
|
|
2966
|
+
});
|
|
2967
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
2968
|
+
}
|
|
2969
|
+
catch (error) {
|
|
2970
|
+
return {
|
|
2971
|
+
content: [
|
|
2972
|
+
{
|
|
2973
|
+
type: 'text',
|
|
2974
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
2975
|
+
},
|
|
2976
|
+
],
|
|
2977
|
+
isError: true,
|
|
2978
|
+
};
|
|
2979
|
+
}
|
|
2980
|
+
});
|
|
2981
|
+
server.registerTool('bos_evaluate_dna_temporal', {
|
|
2982
|
+
description: 'Evaluate a DNA pattern with temporal overrides applied, returning the effective ruleset.',
|
|
2983
|
+
inputSchema: z.object({
|
|
2984
|
+
dnaName: z.string().describe('DNA pattern name (e.g. "lean-factory")'),
|
|
2985
|
+
action: z.string().describe('The action being evaluated'),
|
|
2986
|
+
agentId: z.string().describe('Agent ID'),
|
|
2987
|
+
context: z.record(z.string(), z.unknown()).optional().describe('Context bag'),
|
|
2988
|
+
}),
|
|
2989
|
+
annotations: { readOnlyHint: true, title: 'Evaluate DNA with Temporal Rules' },
|
|
2990
|
+
}, async ({ dnaName, action, agentId, context }) => {
|
|
2991
|
+
try {
|
|
2992
|
+
const bos = getBehaviorOS();
|
|
2993
|
+
const dnas = await loadDNAPatterns();
|
|
2994
|
+
const dna = dnas[dnaName.toLowerCase()];
|
|
2995
|
+
if (!dna) {
|
|
2996
|
+
return {
|
|
2997
|
+
content: [{ type: 'text', text: `DNA pattern "${dnaName}" not found` }],
|
|
2998
|
+
isError: true,
|
|
2999
|
+
};
|
|
3000
|
+
}
|
|
3001
|
+
const result = bos.evaluateDNAWithTemporal(dna, {
|
|
3002
|
+
action,
|
|
3003
|
+
agentId,
|
|
3004
|
+
context: context ?? {},
|
|
3005
|
+
});
|
|
3006
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
3007
|
+
}
|
|
3008
|
+
catch (error) {
|
|
3009
|
+
return {
|
|
3010
|
+
content: [
|
|
3011
|
+
{
|
|
3012
|
+
type: 'text',
|
|
3013
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
3014
|
+
},
|
|
3015
|
+
],
|
|
3016
|
+
isError: true,
|
|
3017
|
+
};
|
|
3018
|
+
}
|
|
3019
|
+
});
|
|
3020
|
+
// ============================================================
|
|
3021
|
+
// KNOWLEDGE GRAPH TOOLS (G-001)
|
|
3022
|
+
// ============================================================
|
|
3023
|
+
server.registerTool('bos_add_knowledge_node', {
|
|
3024
|
+
description: 'Add a node to the knowledge graph (agent, mission, rule, learning, decision, etc.)',
|
|
3025
|
+
inputSchema: z.object({
|
|
3026
|
+
id: z.string().describe('Unique node ID'),
|
|
3027
|
+
type: z
|
|
3028
|
+
.enum([
|
|
3029
|
+
'agent',
|
|
3030
|
+
'mission',
|
|
3031
|
+
'rule',
|
|
3032
|
+
'learning',
|
|
3033
|
+
'decision',
|
|
3034
|
+
'pattern',
|
|
3035
|
+
'dna',
|
|
3036
|
+
'audit',
|
|
3037
|
+
'task',
|
|
3038
|
+
])
|
|
3039
|
+
.describe('Node type'),
|
|
3040
|
+
label: z.string().describe('Human-readable label'),
|
|
3041
|
+
properties: z.record(z.string(), z.unknown()).optional().describe('Structured properties'),
|
|
3042
|
+
}),
|
|
3043
|
+
annotations: { readOnlyHint: false, title: 'Add Knowledge Node' },
|
|
3044
|
+
}, async ({ id, type, label, properties }) => {
|
|
3045
|
+
try {
|
|
3046
|
+
const bos = getBehaviorOS();
|
|
3047
|
+
bos.addKnowledgeNode({
|
|
3048
|
+
id,
|
|
3049
|
+
type,
|
|
3050
|
+
label,
|
|
3051
|
+
properties: properties ?? {},
|
|
3052
|
+
createdAt: new Date().toISOString(),
|
|
3053
|
+
updatedAt: new Date().toISOString(),
|
|
3054
|
+
});
|
|
3055
|
+
return { content: [{ type: 'text', text: `Node "${id}" added to knowledge graph` }] };
|
|
3056
|
+
}
|
|
3057
|
+
catch (error) {
|
|
3058
|
+
return {
|
|
3059
|
+
content: [
|
|
3060
|
+
{
|
|
3061
|
+
type: 'text',
|
|
3062
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
3063
|
+
},
|
|
3064
|
+
],
|
|
3065
|
+
isError: true,
|
|
3066
|
+
};
|
|
3067
|
+
}
|
|
3068
|
+
});
|
|
3069
|
+
server.registerTool('bos_add_knowledge_edge', {
|
|
3070
|
+
description: 'Add an edge to the knowledge graph representing a relationship between nodes',
|
|
3071
|
+
inputSchema: z.object({
|
|
3072
|
+
id: z.string().describe('Unique edge ID'),
|
|
3073
|
+
from: z.string().describe('Source node ID'),
|
|
3074
|
+
to: z.string().describe('Target node ID'),
|
|
3075
|
+
type: z
|
|
3076
|
+
.enum([
|
|
3077
|
+
'GOVERNED_BY',
|
|
3078
|
+
'PART_OF',
|
|
3079
|
+
'CAUSES',
|
|
3080
|
+
'RELATED_TO',
|
|
3081
|
+
'TRIGGERED',
|
|
3082
|
+
'EVIDENCE_FOR',
|
|
3083
|
+
'TRANSFORMS',
|
|
3084
|
+
'SIMILAR_TO',
|
|
3085
|
+
'OBSERVED_IN',
|
|
3086
|
+
'IMPLEMENTS',
|
|
3087
|
+
])
|
|
3088
|
+
.describe('Relationship type'),
|
|
3089
|
+
weight: z.number().min(0).max(1).default(1).optional().describe('Edge weight (0-1)'),
|
|
3090
|
+
properties: z.record(z.string(), z.unknown()).optional().describe('Edge properties'),
|
|
3091
|
+
}),
|
|
3092
|
+
annotations: { readOnlyHint: false, title: 'Add Knowledge Edge' },
|
|
3093
|
+
}, async ({ id, from, to, type, weight, properties }) => {
|
|
3094
|
+
try {
|
|
3095
|
+
const bos = getBehaviorOS();
|
|
3096
|
+
bos.addKnowledgeEdge({
|
|
3097
|
+
id,
|
|
3098
|
+
from,
|
|
3099
|
+
to,
|
|
3100
|
+
type,
|
|
3101
|
+
weight: weight ?? 1,
|
|
3102
|
+
properties: properties ?? undefined,
|
|
3103
|
+
createdAt: new Date().toISOString(),
|
|
3104
|
+
});
|
|
3105
|
+
return {
|
|
3106
|
+
content: [{ type: 'text', text: `Edge "${id}" added: ${from} --[${type}]--> ${to}` }],
|
|
3107
|
+
};
|
|
3108
|
+
}
|
|
3109
|
+
catch (error) {
|
|
3110
|
+
return {
|
|
3111
|
+
content: [
|
|
3112
|
+
{
|
|
3113
|
+
type: 'text',
|
|
3114
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
3115
|
+
},
|
|
3116
|
+
],
|
|
3117
|
+
isError: true,
|
|
3118
|
+
};
|
|
3119
|
+
}
|
|
3120
|
+
});
|
|
3121
|
+
server.registerTool('bos_query_knowledge_graph', {
|
|
3122
|
+
description: 'Query the knowledge graph with traversal and filtering',
|
|
3123
|
+
inputSchema: z.object({
|
|
3124
|
+
startNode: z.string().optional().describe('Starting node ID or "*" for all'),
|
|
3125
|
+
nodeType: z
|
|
3126
|
+
.enum([
|
|
3127
|
+
'agent',
|
|
3128
|
+
'mission',
|
|
3129
|
+
'rule',
|
|
3130
|
+
'learning',
|
|
3131
|
+
'decision',
|
|
3132
|
+
'pattern',
|
|
3133
|
+
'dna',
|
|
3134
|
+
'audit',
|
|
3135
|
+
'task',
|
|
3136
|
+
'any',
|
|
3137
|
+
])
|
|
3138
|
+
.optional()
|
|
3139
|
+
.describe('Filter by node type'),
|
|
3140
|
+
edgeType: z
|
|
3141
|
+
.enum([
|
|
3142
|
+
'GOVERNED_BY',
|
|
3143
|
+
'PART_OF',
|
|
3144
|
+
'CAUSES',
|
|
3145
|
+
'RELATED_TO',
|
|
3146
|
+
'TRIGGERED',
|
|
3147
|
+
'EVIDENCE_FOR',
|
|
3148
|
+
'TRANSFORMS',
|
|
3149
|
+
'SIMILAR_TO',
|
|
3150
|
+
'OBSERVED_IN',
|
|
3151
|
+
'IMPLEMENTS',
|
|
3152
|
+
'any',
|
|
3153
|
+
])
|
|
3154
|
+
.optional()
|
|
3155
|
+
.describe('Filter by edge type'),
|
|
3156
|
+
depth: z.number().min(1).max(10).default(3).optional().describe('Maximum traversal depth'),
|
|
3157
|
+
properties: z.record(z.string(), z.unknown()).optional().describe('Property filters'),
|
|
3158
|
+
sortBy: z.enum(['createdAt', 'weight', 'label']).optional().describe('Sort results'),
|
|
3159
|
+
}),
|
|
3160
|
+
annotations: { readOnlyHint: true, title: 'Query Knowledge Graph' },
|
|
3161
|
+
}, async ({ startNode, nodeType, edgeType, depth, properties, sortBy }) => {
|
|
3162
|
+
try {
|
|
3163
|
+
const bos = getBehaviorOS();
|
|
3164
|
+
const result = bos.queryKnowledgeGraph({
|
|
3165
|
+
startNode: startNode === '*' ? undefined : startNode,
|
|
3166
|
+
nodeType: nodeType ?? 'any',
|
|
3167
|
+
edgeType: edgeType ?? 'any',
|
|
3168
|
+
depth: depth ?? 3,
|
|
3169
|
+
properties: properties ?? undefined,
|
|
3170
|
+
sortBy: sortBy ?? 'createdAt',
|
|
3171
|
+
sortOrder: 'desc',
|
|
3172
|
+
});
|
|
3173
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
3174
|
+
}
|
|
3175
|
+
catch (error) {
|
|
3176
|
+
return {
|
|
3177
|
+
content: [
|
|
3178
|
+
{
|
|
3179
|
+
type: 'text',
|
|
3180
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
3181
|
+
},
|
|
3182
|
+
],
|
|
3183
|
+
isError: true,
|
|
3184
|
+
};
|
|
3185
|
+
}
|
|
3186
|
+
});
|
|
3187
|
+
server.registerTool('bos_get_knowledge_graph_stats', {
|
|
3188
|
+
description: 'Get statistics about the knowledge graph (node/edge counts, types, average degree)',
|
|
3189
|
+
inputSchema: z.object({}),
|
|
3190
|
+
annotations: { readOnlyHint: true, title: 'Knowledge Graph Stats' },
|
|
3191
|
+
}, async () => {
|
|
3192
|
+
try {
|
|
3193
|
+
const bos = getBehaviorOS();
|
|
3194
|
+
const stats = bos.getKnowledgeGraphStats();
|
|
3195
|
+
return { content: [{ type: 'text', text: JSON.stringify(stats, null, 2) }] };
|
|
3196
|
+
}
|
|
3197
|
+
catch (error) {
|
|
3198
|
+
return {
|
|
3199
|
+
content: [
|
|
3200
|
+
{
|
|
3201
|
+
type: 'text',
|
|
3202
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
3203
|
+
},
|
|
3204
|
+
],
|
|
3205
|
+
isError: true,
|
|
3206
|
+
};
|
|
3207
|
+
}
|
|
3208
|
+
});
|
|
3209
|
+
// ============================================================
|
|
3210
|
+
// META-COGNITION TOOLS (G-002)
|
|
3211
|
+
// ============================================================
|
|
3212
|
+
server.registerTool('bos_create_meta_decision', {
|
|
3213
|
+
description: 'Create a meta-decision from a governance evaluation with reasoning path, confidence, and bias detection',
|
|
3214
|
+
inputSchema: z.object({
|
|
3215
|
+
decision: z.record(z.string(), z.unknown()).describe('Governance decision'),
|
|
3216
|
+
context: z.record(z.string(), z.unknown()).optional().describe('Context bag'),
|
|
3217
|
+
agentId: z.string().optional().describe('Agent ID'),
|
|
3218
|
+
dnaPattern: z.string().optional().describe('DNA pattern name'),
|
|
3219
|
+
}),
|
|
3220
|
+
annotations: { readOnlyHint: false, title: 'Create Meta Decision' },
|
|
3221
|
+
}, async ({ decision, context, agentId, dnaPattern }) => {
|
|
3222
|
+
try {
|
|
3223
|
+
const bos = getBehaviorOS();
|
|
3224
|
+
const result = bos.createMetaDecision(decision, {
|
|
3225
|
+
context: context ?? {},
|
|
3226
|
+
agentId: agentId ?? bos.resolveAgentIdentity().agentId ?? 'unknown',
|
|
3227
|
+
dnaPattern,
|
|
3228
|
+
});
|
|
3229
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
3230
|
+
}
|
|
3231
|
+
catch (error) {
|
|
3232
|
+
return {
|
|
3233
|
+
content: [
|
|
3234
|
+
{
|
|
3235
|
+
type: 'text',
|
|
3236
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
3237
|
+
},
|
|
3238
|
+
],
|
|
3239
|
+
isError: true,
|
|
3240
|
+
};
|
|
3241
|
+
}
|
|
3242
|
+
});
|
|
3243
|
+
server.registerTool('bos_get_meta_cognition_stats', {
|
|
3244
|
+
description: 'Get statistics about the meta-cognition engine',
|
|
3245
|
+
inputSchema: z.object({}),
|
|
3246
|
+
annotations: { readOnlyHint: true, title: 'Meta Cognition Stats' },
|
|
3247
|
+
}, async () => {
|
|
3248
|
+
try {
|
|
3249
|
+
const bos = getBehaviorOS();
|
|
3250
|
+
const stats = bos.getMetaCognitionStats();
|
|
3251
|
+
return { content: [{ type: 'text', text: JSON.stringify(stats, null, 2) }] };
|
|
3252
|
+
}
|
|
3253
|
+
catch (error) {
|
|
3254
|
+
return {
|
|
3255
|
+
content: [
|
|
3256
|
+
{
|
|
3257
|
+
type: 'text',
|
|
3258
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
3259
|
+
},
|
|
3260
|
+
],
|
|
3261
|
+
isError: true,
|
|
3262
|
+
};
|
|
3263
|
+
}
|
|
3264
|
+
});
|
|
3265
|
+
// ============================================================
|
|
3266
|
+
// PERSISTENT MEMORY TOOLS (G-003)
|
|
3267
|
+
// ============================================================
|
|
3268
|
+
server.registerTool('bos_save_memory', {
|
|
3269
|
+
description: 'Save a memory (knowledge graph node) to persistent storage',
|
|
3270
|
+
inputSchema: z.object({
|
|
3271
|
+
id: z.string().describe('Unique node ID'),
|
|
3272
|
+
type: z
|
|
3273
|
+
.enum([
|
|
3274
|
+
'agent',
|
|
3275
|
+
'mission',
|
|
3276
|
+
'rule',
|
|
3277
|
+
'learning',
|
|
3278
|
+
'decision',
|
|
3279
|
+
'pattern',
|
|
3280
|
+
'dna',
|
|
3281
|
+
'audit',
|
|
3282
|
+
'task',
|
|
3283
|
+
])
|
|
3284
|
+
.describe('Node type'),
|
|
3285
|
+
label: z.string().describe('Human-readable label'),
|
|
3286
|
+
properties: z.record(z.string(), z.unknown()).optional().describe('Structured properties'),
|
|
3287
|
+
relevanceScore: z.number().min(0).max(1).optional().describe('Relevance score (0-1)'),
|
|
3288
|
+
expiresAt: z.string().optional().describe('Optional expiration timestamp (ISO)'),
|
|
3289
|
+
}),
|
|
3290
|
+
annotations: { readOnlyHint: false, title: 'Save Memory' },
|
|
3291
|
+
}, async ({ id, type, label, properties, relevanceScore, expiresAt }) => {
|
|
3292
|
+
try {
|
|
3293
|
+
const bos = getBehaviorOS();
|
|
3294
|
+
bos.saveMemory({
|
|
3295
|
+
id,
|
|
3296
|
+
type,
|
|
3297
|
+
label,
|
|
3298
|
+
properties: properties ?? {},
|
|
3299
|
+
createdAt: new Date().toISOString(),
|
|
3300
|
+
updatedAt: new Date().toISOString(),
|
|
3301
|
+
}, { relevanceScore, expiresAt });
|
|
3302
|
+
return { content: [{ type: 'text', text: `Memory "${id}" saved to persistent storage` }] };
|
|
3303
|
+
}
|
|
3304
|
+
catch (error) {
|
|
3305
|
+
return {
|
|
3306
|
+
content: [
|
|
3307
|
+
{
|
|
3308
|
+
type: 'text',
|
|
3309
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
3310
|
+
},
|
|
3311
|
+
],
|
|
3312
|
+
isError: true,
|
|
3313
|
+
};
|
|
3314
|
+
}
|
|
3315
|
+
});
|
|
3316
|
+
server.registerTool('bos_recall_memory', {
|
|
3317
|
+
description: 'Recall a memory by node ID from persistent storage',
|
|
3318
|
+
inputSchema: z.object({
|
|
3319
|
+
nodeId: z.string().describe('Node ID to recall'),
|
|
3320
|
+
}),
|
|
3321
|
+
annotations: { readOnlyHint: true, title: 'Recall Memory' },
|
|
3322
|
+
}, async ({ nodeId }) => {
|
|
3323
|
+
try {
|
|
3324
|
+
const bos = getBehaviorOS();
|
|
3325
|
+
const result = bos.recallMemory(nodeId);
|
|
3326
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
3327
|
+
}
|
|
3328
|
+
catch (error) {
|
|
3329
|
+
return {
|
|
3330
|
+
content: [
|
|
3331
|
+
{
|
|
3332
|
+
type: 'text',
|
|
3333
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
3334
|
+
},
|
|
3335
|
+
],
|
|
3336
|
+
isError: true,
|
|
3337
|
+
};
|
|
3338
|
+
}
|
|
3339
|
+
});
|
|
3340
|
+
server.registerTool('bos_recall_relevant', {
|
|
3341
|
+
description: 'Recall memories relevant to a context via graph traversal',
|
|
3342
|
+
inputSchema: z.object({
|
|
3343
|
+
startNodeId: z.string().describe('Starting node ID for traversal'),
|
|
3344
|
+
maxDepth: z.number().min(1).max(10).default(3).optional().describe('Maximum traversal depth'),
|
|
3345
|
+
minScore: z
|
|
3346
|
+
.number()
|
|
3347
|
+
.min(0)
|
|
3348
|
+
.max(1)
|
|
3349
|
+
.default(0.3)
|
|
3350
|
+
.optional()
|
|
3351
|
+
.describe('Minimum relevance score'),
|
|
3352
|
+
nodeTypes: z.array(z.string()).optional().describe('Filter by node types'),
|
|
3353
|
+
}),
|
|
3354
|
+
annotations: { readOnlyHint: true, title: 'Recall Relevant Memories' },
|
|
3355
|
+
}, async ({ startNodeId, maxDepth, minScore, nodeTypes }) => {
|
|
3356
|
+
try {
|
|
3357
|
+
const bos = getBehaviorOS();
|
|
3358
|
+
const result = bos.recallRelevant(startNodeId, { maxDepth, minScore, nodeTypes });
|
|
3359
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
3360
|
+
}
|
|
3361
|
+
catch (error) {
|
|
3362
|
+
return {
|
|
3363
|
+
content: [
|
|
3364
|
+
{
|
|
3365
|
+
type: 'text',
|
|
3366
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
3367
|
+
},
|
|
3368
|
+
],
|
|
3369
|
+
isError: true,
|
|
3370
|
+
};
|
|
3371
|
+
}
|
|
3372
|
+
});
|
|
3373
|
+
server.registerTool('bos_reconstruct_context', {
|
|
3374
|
+
description: 'Reconstruct agent context from past missions, decisions, and learnings',
|
|
3375
|
+
inputSchema: z.object({
|
|
3376
|
+
agentId: z.string().describe('Agent ID to reconstruct context for'),
|
|
3377
|
+
}),
|
|
3378
|
+
annotations: { readOnlyHint: true, title: 'Reconstruct Agent Context' },
|
|
3379
|
+
}, async ({ agentId }) => {
|
|
3380
|
+
try {
|
|
3381
|
+
const bos = getBehaviorOS();
|
|
3382
|
+
const result = bos.reconstructContext(agentId);
|
|
3383
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
3384
|
+
}
|
|
3385
|
+
catch (error) {
|
|
3386
|
+
return {
|
|
3387
|
+
content: [
|
|
3388
|
+
{
|
|
3389
|
+
type: 'text',
|
|
3390
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
3391
|
+
},
|
|
3392
|
+
],
|
|
3393
|
+
isError: true,
|
|
3394
|
+
};
|
|
3395
|
+
}
|
|
3396
|
+
});
|
|
3397
|
+
server.registerTool('bos_consolidate_memory', {
|
|
3398
|
+
description: 'Consolidate in-memory knowledge graph state to persistent storage',
|
|
3399
|
+
inputSchema: z.object({}),
|
|
3400
|
+
annotations: { readOnlyHint: false, title: 'Consolidate Memory' },
|
|
3401
|
+
}, async () => {
|
|
3402
|
+
try {
|
|
3403
|
+
const bos = getBehaviorOS();
|
|
3404
|
+
const result = bos.consolidateMemory();
|
|
3405
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
3406
|
+
}
|
|
3407
|
+
catch (error) {
|
|
3408
|
+
return {
|
|
3409
|
+
content: [
|
|
3410
|
+
{
|
|
3411
|
+
type: 'text',
|
|
3412
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
3413
|
+
},
|
|
3414
|
+
],
|
|
3415
|
+
isError: true,
|
|
3416
|
+
};
|
|
3417
|
+
}
|
|
3418
|
+
});
|
|
3419
|
+
// ============================================================
|
|
3420
|
+
// START SERVER
|
|
3421
|
+
// ============================================================
|
|
3422
|
+
async function main() {
|
|
3423
|
+
const transport = new StdioServerTransport();
|
|
3424
|
+
await server.connect(transport);
|
|
3425
|
+
console.error('Behavior-OS MCP Server running on stdio');
|
|
3426
|
+
}
|
|
3427
|
+
// Only bootstrap the stdio server when this module is executed directly, not
|
|
3428
|
+
// when it is imported by tests (which use InMemoryTransport instead).
|
|
3429
|
+
// pathToFileURL keeps this check synchronous: the previous `createRequire`
|
|
3430
|
+
// implementation required this ESM module from itself, which throws
|
|
3431
|
+
// ERR_REQUIRE_ASYNC_MODULE on Node 24 and left the server unstarted.
|
|
3432
|
+
const isEntry = typeof process !== 'undefined' &&
|
|
3433
|
+
typeof process.argv[1] === 'string' &&
|
|
3434
|
+
import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
3435
|
+
if (isEntry) {
|
|
3436
|
+
main().catch((error) => {
|
|
3437
|
+
console.error('Failed to start server:', error);
|
|
3438
|
+
process.exit(1);
|
|
3439
|
+
});
|
|
3440
|
+
}
|
|
3441
|
+
//# sourceMappingURL=server.js.map
|