frinus-mcp 3.1.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/README.md +405 -0
- package/dist/capture/interaction-capture.d.ts +64 -0
- package/dist/capture/interaction-capture.js +186 -0
- package/dist/client/agent-client.d.ts +34 -0
- package/dist/client/agent-client.js +71 -0
- package/dist/client/cp-client.d.ts +20 -0
- package/dist/client/cp-client.js +58 -0
- package/dist/client/memory-client.d.ts +200 -0
- package/dist/client/memory-client.js +460 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +103 -0
- package/dist/tools/definitions.d.ts +8 -0
- package/dist/tools/definitions.js +1548 -0
- package/dist/tools/handlers.d.ts +7 -0
- package/dist/tools/handlers.js +1609 -0
- package/dist/types/index.d.ts +279 -0
- package/dist/types/index.js +4 -0
- package/manifest.json +76 -0
- package/package.json +57 -0
|
@@ -0,0 +1,1609 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool handlers for the Frinus MCP Server.
|
|
3
|
+
*
|
|
4
|
+
* Maps tool names to handler functions that execute the tool logic
|
|
5
|
+
* and return formatted MCP tool results.
|
|
6
|
+
*/
|
|
7
|
+
import axios from "axios";
|
|
8
|
+
import { getResolvedTenantOrgId } from "../client/memory-client.js";
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Helper: 401/403 error handling
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
export function handleApiError(error) {
|
|
13
|
+
const status = error?.response?.status;
|
|
14
|
+
if (status === 401) {
|
|
15
|
+
return {
|
|
16
|
+
content: [{ type: "text", text: "Authentication error (401). Check API key configuration." }],
|
|
17
|
+
isError: true,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (status === 403) {
|
|
21
|
+
return {
|
|
22
|
+
content: [{ type: "text", text: "Permission denied. You don't have access to this scope." }],
|
|
23
|
+
isError: true,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (status === 504) {
|
|
27
|
+
const detail = error?.response?.data?.detail || "Embedding model timeout";
|
|
28
|
+
return {
|
|
29
|
+
content: [{ type: "text", text: `Search timeout (504): ${detail}. The embedding model may be loading or overloaded.` }],
|
|
30
|
+
isError: true,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (status === 502) {
|
|
34
|
+
return {
|
|
35
|
+
content: [{ type: "text", text: "Service unavailable (502). The Memory Engine may be restarting." }],
|
|
36
|
+
isError: true,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return null; // Not a known error, let it propagate
|
|
40
|
+
}
|
|
41
|
+
const handlers = {
|
|
42
|
+
// ==========================================================================
|
|
43
|
+
// Core Memory
|
|
44
|
+
// ==========================================================================
|
|
45
|
+
async memory_store(args, { memoryClient, resolvedUserId }) {
|
|
46
|
+
try {
|
|
47
|
+
const createdByUser = args.created_by_user_id || resolvedUserId || undefined;
|
|
48
|
+
const userId = args.user_id || resolvedUserId || undefined;
|
|
49
|
+
// Auto-scope inference: if scope not provided, pick the narrowest
|
|
50
|
+
// scope justified by the available context. Caller can always pass
|
|
51
|
+
// an explicit `scope` to override this heuristic.
|
|
52
|
+
const meta = args.metadata || {};
|
|
53
|
+
const universeId = args.universe_id || meta.universe_id || undefined;
|
|
54
|
+
let resolvedScope = args.scope;
|
|
55
|
+
if (!resolvedScope) {
|
|
56
|
+
if (userId || createdByUser) {
|
|
57
|
+
resolvedScope = "user";
|
|
58
|
+
}
|
|
59
|
+
else if (universeId) {
|
|
60
|
+
resolvedScope = "universe";
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
resolvedScope = "organization";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const result = await memoryClient.storeMemory({
|
|
67
|
+
agent_id: args.agent_id,
|
|
68
|
+
content: args.content,
|
|
69
|
+
memory_type: args.memory_type,
|
|
70
|
+
scope: resolvedScope,
|
|
71
|
+
importance: args.importance,
|
|
72
|
+
user_id: userId,
|
|
73
|
+
created_by_user_id: createdByUser,
|
|
74
|
+
metadata: args.metadata,
|
|
75
|
+
context_id: args.context_id,
|
|
76
|
+
credential_ref: args.credential_ref,
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
content: [{
|
|
80
|
+
type: "text",
|
|
81
|
+
text: `Memory stored successfully.\nID: ${result.id}\nType: ${result.memory_type}\nScope: ${result.scope}`,
|
|
82
|
+
}],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
const apiErr = handleApiError(error);
|
|
87
|
+
if (apiErr)
|
|
88
|
+
return apiErr;
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
async memory_search(args, { memoryClient }) {
|
|
93
|
+
let results;
|
|
94
|
+
try {
|
|
95
|
+
results = await memoryClient.searchMemories({
|
|
96
|
+
query_text: args.query_text,
|
|
97
|
+
agent_id: args.agent_id,
|
|
98
|
+
memory_types: args.memory_types,
|
|
99
|
+
limit: args.limit,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
const apiErr = handleApiError(error);
|
|
104
|
+
if (apiErr)
|
|
105
|
+
return apiErr;
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
if (!results || results.length === 0) {
|
|
109
|
+
return { content: [{ type: "text", text: "No memories found matching the query." }] };
|
|
110
|
+
}
|
|
111
|
+
let formatted = "Found memories:\n\n";
|
|
112
|
+
results.forEach((mem, i) => {
|
|
113
|
+
formatted += `${i + 1}. [${mem.memory_type}] (id: ${mem.id}) (similarity: ${(mem.similarity || 0).toFixed(2)})\n`;
|
|
114
|
+
formatted += ` ${mem.content}\n\n`;
|
|
115
|
+
});
|
|
116
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
117
|
+
},
|
|
118
|
+
async memory_get(args, { memoryClient }) {
|
|
119
|
+
try {
|
|
120
|
+
const result = await memoryClient.getMemory(args.memory_id);
|
|
121
|
+
let formatted = `Memory Details:\n`;
|
|
122
|
+
formatted += `ID: ${result.id}\n`;
|
|
123
|
+
formatted += `Type: ${result.memory_type}\n`;
|
|
124
|
+
formatted += `Scope: ${result.scope}\n`;
|
|
125
|
+
formatted += `Level: ${result.hierarchy_level || 'raw'}\n`;
|
|
126
|
+
formatted += `Importance: ${result.importance?.toFixed(2) || 'N/A'}\n`;
|
|
127
|
+
formatted += `Relevance: ${result.relevance_score?.toFixed(2) || 'N/A'}\n`;
|
|
128
|
+
formatted += `Created: ${result.created_at}\n`;
|
|
129
|
+
formatted += `\nContent:\n${result.content}`;
|
|
130
|
+
if (result.metadata && Object.keys(result.metadata).length > 0) {
|
|
131
|
+
formatted += `\n\nMetadata: ${JSON.stringify(result.metadata, null, 2)}`;
|
|
132
|
+
}
|
|
133
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
const apiErr = handleApiError(error);
|
|
137
|
+
if (apiErr)
|
|
138
|
+
return apiErr;
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
async memory_list(args, { memoryClient }) {
|
|
143
|
+
try {
|
|
144
|
+
const results = await memoryClient.getAgentMemories(args.agent_id, args.memory_type, args.limit || 50);
|
|
145
|
+
if (!results || results.length === 0) {
|
|
146
|
+
return { content: [{ type: "text", text: "No memories found for this agent." }] };
|
|
147
|
+
}
|
|
148
|
+
let formatted = `Memories for agent ${args.agent_id}:\n\n`;
|
|
149
|
+
results.forEach((mem, i) => {
|
|
150
|
+
const content = mem.content.length > 200 ? mem.content.slice(0, 200) + "..." : mem.content;
|
|
151
|
+
formatted += `${i + 1}. [${mem.memory_type}] (id: ${mem.id}) (importance: ${(mem.importance || 0).toFixed(2)})\n`;
|
|
152
|
+
formatted += ` ${content}\n\n`;
|
|
153
|
+
});
|
|
154
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
const apiErr = handleApiError(error);
|
|
158
|
+
if (apiErr)
|
|
159
|
+
return apiErr;
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
async memory_delete(args, { memoryClient }) {
|
|
164
|
+
const result = await memoryClient.deleteMemory(args.memory_id);
|
|
165
|
+
return {
|
|
166
|
+
content: [{
|
|
167
|
+
type: "text",
|
|
168
|
+
text: `Memory deleted.\nID: ${args.memory_id}\nStatus: ${result.status || 'deleted'}`,
|
|
169
|
+
}],
|
|
170
|
+
};
|
|
171
|
+
},
|
|
172
|
+
async memory_get_context(args, { memoryClient }) {
|
|
173
|
+
const result = await memoryClient.buildContext({
|
|
174
|
+
agent_id: args.agent_id,
|
|
175
|
+
task_description: args.task_description,
|
|
176
|
+
max_tokens: args.max_tokens,
|
|
177
|
+
});
|
|
178
|
+
if (!result.context) {
|
|
179
|
+
return { content: [{ type: "text", text: "No relevant context found for this task." }] };
|
|
180
|
+
}
|
|
181
|
+
return { content: [{ type: "text", text: `Context from memory:\n\n${result.context}` }] };
|
|
182
|
+
},
|
|
183
|
+
// ==========================================================================
|
|
184
|
+
// Dynamic Relevance
|
|
185
|
+
// ==========================================================================
|
|
186
|
+
async memory_reinforce(args, { memoryClient }) {
|
|
187
|
+
const result = await memoryClient.reinforceMemory(args.memory_id, args.boost);
|
|
188
|
+
return {
|
|
189
|
+
content: [{
|
|
190
|
+
type: "text",
|
|
191
|
+
text: `Memory reinforced.\nID: ${args.memory_id}\nNew relevance: ${result.relevance_score?.toFixed(3) || 'updated'}`,
|
|
192
|
+
}],
|
|
193
|
+
};
|
|
194
|
+
},
|
|
195
|
+
async memory_weaken(args, { memoryClient }) {
|
|
196
|
+
const result = await memoryClient.weakenMemory(args.memory_id, args.penalty);
|
|
197
|
+
return {
|
|
198
|
+
content: [{
|
|
199
|
+
type: "text",
|
|
200
|
+
text: `Memory weakened.\nID: ${args.memory_id}\nNew relevance: ${result.relevance_score?.toFixed(3) || 'updated'}`,
|
|
201
|
+
}],
|
|
202
|
+
};
|
|
203
|
+
},
|
|
204
|
+
// ==========================================================================
|
|
205
|
+
// Working Memory
|
|
206
|
+
// ==========================================================================
|
|
207
|
+
async working_memory_get(args, { memoryClient }) {
|
|
208
|
+
const result = await memoryClient.getWorkingMemory(args.context_id);
|
|
209
|
+
if (!result.items || result.items.length === 0) {
|
|
210
|
+
return { content: [{ type: "text", text: `No working memory for context: ${args.context_id}` }] };
|
|
211
|
+
}
|
|
212
|
+
let formatted = `Working Memory [${args.context_id}] (${result.count}/${result.max_items} items):\n\n`;
|
|
213
|
+
result.items.forEach((item, i) => {
|
|
214
|
+
formatted += `${i + 1}. ${item.content}\n`;
|
|
215
|
+
if (item.created_at)
|
|
216
|
+
formatted += ` Created: ${item.created_at}\n`;
|
|
217
|
+
formatted += "\n";
|
|
218
|
+
});
|
|
219
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
220
|
+
},
|
|
221
|
+
async working_memory_add(args, { memoryClient }) {
|
|
222
|
+
const result = await memoryClient.addWorkingMemory({
|
|
223
|
+
context_id: args.context_id,
|
|
224
|
+
content: args.content,
|
|
225
|
+
agent_id: args.agent_id,
|
|
226
|
+
ttl_seconds: args.ttl_seconds,
|
|
227
|
+
});
|
|
228
|
+
return {
|
|
229
|
+
content: [{
|
|
230
|
+
type: "text",
|
|
231
|
+
text: `Working memory updated.\nContext: ${args.context_id}\nID: ${result.id}`,
|
|
232
|
+
}],
|
|
233
|
+
};
|
|
234
|
+
},
|
|
235
|
+
async working_memory_clear(args, { memoryClient }) {
|
|
236
|
+
const result = await memoryClient.clearWorkingMemory(args.context_id);
|
|
237
|
+
return {
|
|
238
|
+
content: [{
|
|
239
|
+
type: "text",
|
|
240
|
+
text: `Working memory cleared.\nContext: ${args.context_id}\nItems removed: ${result.items_cleared}`,
|
|
241
|
+
}],
|
|
242
|
+
};
|
|
243
|
+
},
|
|
244
|
+
// ==========================================================================
|
|
245
|
+
// Session Management
|
|
246
|
+
// ==========================================================================
|
|
247
|
+
async session_start(args, { memoryClient, capture }) {
|
|
248
|
+
try {
|
|
249
|
+
const result = await memoryClient.startSession({
|
|
250
|
+
agent_id: args.agent_id,
|
|
251
|
+
parent_session_id: args.parent_session_id,
|
|
252
|
+
});
|
|
253
|
+
// Replace auto-generated session ID with the formal one
|
|
254
|
+
capture.sessionId = result.session_id;
|
|
255
|
+
// P1 BOOT auto-populate: seed working memory with the session id and
|
|
256
|
+
// agent role so subsequent `working_memory_get(agent:<id>)` calls
|
|
257
|
+
// return something meaningful instead of "No working memory".
|
|
258
|
+
// Fire-and-forget; failures must never break session_start.
|
|
259
|
+
const agentId = args.agent_id;
|
|
260
|
+
if (agentId) {
|
|
261
|
+
const seed = `SESSION_ID: ${result.session_id}\n` +
|
|
262
|
+
`AGENT_ID: ${agentId}\n` +
|
|
263
|
+
(result.agent_context ? `ROLE: ${String(result.agent_context).slice(0, 400)}\n` : "") +
|
|
264
|
+
`BOOT: P1 executed at ${new Date().toISOString()}`;
|
|
265
|
+
memoryClient
|
|
266
|
+
.addWorkingMemory({
|
|
267
|
+
context_id: `agent:${agentId}`,
|
|
268
|
+
content: seed,
|
|
269
|
+
agent_id: agentId,
|
|
270
|
+
ttl_seconds: 3600,
|
|
271
|
+
})
|
|
272
|
+
.catch((err) => {
|
|
273
|
+
console.error('[MCP] P1 BOOT working_memory seed failed:', err?.message || err);
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
let msg = `Session started: ${result.session_id}\n`;
|
|
277
|
+
if (result.parent_session_id)
|
|
278
|
+
msg += `Parent session: ${result.parent_session_id}\n`;
|
|
279
|
+
msg += "\n";
|
|
280
|
+
if (result.agent_context)
|
|
281
|
+
msg += `## Agent Context\n${result.agent_context}\n\n`;
|
|
282
|
+
if (result.permissions && result.permissions.length > 0) {
|
|
283
|
+
msg += `## Your Permissions\n`;
|
|
284
|
+
for (const p of result.permissions) {
|
|
285
|
+
const resource = p.resource || "unknown";
|
|
286
|
+
const permission = p.actions
|
|
287
|
+
? (Array.isArray(p.actions) ? p.actions.join(", ") : p.actions)
|
|
288
|
+
: p.permission || "none";
|
|
289
|
+
msg += `- ${resource}: ${permission}\n`;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return { content: [{ type: "text", text: msg }] };
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
const authError = handleApiError(error);
|
|
296
|
+
if (authError)
|
|
297
|
+
return authError;
|
|
298
|
+
if (error?.response?.status === 400) {
|
|
299
|
+
return {
|
|
300
|
+
content: [{ type: "text", text: `Validation error: ${error.response.data?.detail || "invalid request"}` }],
|
|
301
|
+
isError: true,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
throw error;
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
async session_end(args, { memoryClient }) {
|
|
308
|
+
try {
|
|
309
|
+
const result = await memoryClient.endSession(args.session_id);
|
|
310
|
+
return {
|
|
311
|
+
content: [{
|
|
312
|
+
type: "text",
|
|
313
|
+
text: `Session ended: ${args.session_id}\nStatus: ${result.status || 'ended'}`,
|
|
314
|
+
}],
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
const authError = handleApiError(error);
|
|
319
|
+
if (authError)
|
|
320
|
+
return authError;
|
|
321
|
+
throw error;
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
async session_context(args, { memoryClient }) {
|
|
325
|
+
const result = await memoryClient.getSessionContext({
|
|
326
|
+
session_id: args.session_id,
|
|
327
|
+
query: args.query,
|
|
328
|
+
agent_id: args.agent_id,
|
|
329
|
+
max_working_memory: args.max_working_memory,
|
|
330
|
+
max_long_term: args.max_long_term,
|
|
331
|
+
include_topics: args.include_topics,
|
|
332
|
+
});
|
|
333
|
+
let formatted = `Session Context: ${args.session_id}\n`;
|
|
334
|
+
formatted += `=====================================\n\n`;
|
|
335
|
+
if (result.topics && result.topics.length > 0) {
|
|
336
|
+
formatted += `Topics: ${result.topics.join(', ')}\n\n`;
|
|
337
|
+
}
|
|
338
|
+
if (result.working_memory && result.working_memory.length > 0) {
|
|
339
|
+
formatted += `Working Memory (${result.working_memory.length} items):\n`;
|
|
340
|
+
result.working_memory.forEach((item, i) => {
|
|
341
|
+
formatted += ` ${i + 1}. ${item.content?.slice(0, 100)}...\n`;
|
|
342
|
+
});
|
|
343
|
+
formatted += `\n`;
|
|
344
|
+
}
|
|
345
|
+
if (result.long_term_memories && result.long_term_memories.length > 0) {
|
|
346
|
+
formatted += `Long-Term Memories (${result.long_term_memories.length} items):\n`;
|
|
347
|
+
result.long_term_memories.forEach((mem, i) => {
|
|
348
|
+
formatted += ` ${i + 1}. [${mem.memory_type}] (id: ${mem.id}) ${mem.content?.slice(0, 100)}...\n`;
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
352
|
+
},
|
|
353
|
+
async session_summary(args, { memoryClient }) {
|
|
354
|
+
const result = await memoryClient.getSessionSummary(args.session_id);
|
|
355
|
+
let formatted = `Session Summary: ${args.session_id}\n`;
|
|
356
|
+
formatted += `===================================\n\n`;
|
|
357
|
+
formatted += `Item Count: ${result.item_count || 0}\n`;
|
|
358
|
+
if (result.main_topics && result.main_topics.length > 0) {
|
|
359
|
+
formatted += `Main Topics: ${result.main_topics.join(', ')}\n`;
|
|
360
|
+
}
|
|
361
|
+
if (result.recent_items && result.recent_items.length > 0) {
|
|
362
|
+
formatted += `\nRecent Items:\n`;
|
|
363
|
+
result.recent_items.forEach((item, i) => {
|
|
364
|
+
formatted += ` ${i + 1}. [${item.direction}] ${item.content?.slice(0, 100)}...\n`;
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
368
|
+
},
|
|
369
|
+
async session_clear(args, { memoryClient }) {
|
|
370
|
+
const result = await memoryClient.clearSession(args.session_id);
|
|
371
|
+
return {
|
|
372
|
+
content: [{
|
|
373
|
+
type: "text",
|
|
374
|
+
text: `Session cleared: ${args.session_id}\nItems removed: ${result.items_cleared || 0}`,
|
|
375
|
+
}],
|
|
376
|
+
};
|
|
377
|
+
},
|
|
378
|
+
// ==========================================================================
|
|
379
|
+
// Stream
|
|
380
|
+
// ==========================================================================
|
|
381
|
+
async stream_capture(args, { memoryClient }) {
|
|
382
|
+
const result = await memoryClient.captureStream({
|
|
383
|
+
session_id: args.session_id,
|
|
384
|
+
content: args.content,
|
|
385
|
+
direction: args.direction,
|
|
386
|
+
agent_id: args.agent_id,
|
|
387
|
+
importance: args.importance,
|
|
388
|
+
});
|
|
389
|
+
return {
|
|
390
|
+
content: [{
|
|
391
|
+
type: "text",
|
|
392
|
+
text: `Captured to stream.\nSession: ${args.session_id}\nID: ${result.id}`,
|
|
393
|
+
}],
|
|
394
|
+
};
|
|
395
|
+
},
|
|
396
|
+
async stream_get_session(args, { memoryClient }) {
|
|
397
|
+
const result = await memoryClient.getSessionItems(args.session_id, args.limit);
|
|
398
|
+
const items = result.items || [];
|
|
399
|
+
if (!items || items.length === 0) {
|
|
400
|
+
return { content: [{ type: "text", text: `No items found for session ${args.session_id}` }] };
|
|
401
|
+
}
|
|
402
|
+
let formatted = `Session Items: ${args.session_id}\n`;
|
|
403
|
+
formatted += `Count: ${result.count}\n\n`;
|
|
404
|
+
items.forEach((item, i) => {
|
|
405
|
+
formatted += `${i + 1}. [${item.direction}] (importance: ${item.importance?.toFixed(2) || 'N/A'})\n`;
|
|
406
|
+
formatted += ` ${item.content?.slice(0, 150)}${item.content?.length > 150 ? '...' : ''}\n`;
|
|
407
|
+
if (item.timestamp)
|
|
408
|
+
formatted += ` Time: ${item.timestamp}\n`;
|
|
409
|
+
formatted += `\n`;
|
|
410
|
+
});
|
|
411
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
412
|
+
},
|
|
413
|
+
async stream_get_recent(args, { memoryClient }) {
|
|
414
|
+
const result = await memoryClient.getRecentStreamItems(args.agent_id, args.limit, args.include_forgotten);
|
|
415
|
+
const items = result.items || [];
|
|
416
|
+
if (!items || items.length === 0) {
|
|
417
|
+
return { content: [{ type: "text", text: "No recent stream items found." }] };
|
|
418
|
+
}
|
|
419
|
+
let formatted = `Recent Stream Items (${result.count})\n`;
|
|
420
|
+
formatted += `============================\n\n`;
|
|
421
|
+
items.forEach((item, i) => {
|
|
422
|
+
formatted += `${i + 1}. [${item.direction}] Session: ${item.session_id || 'N/A'}\n`;
|
|
423
|
+
formatted += ` Importance: ${item.importance?.toFixed(2) || 'N/A'}`;
|
|
424
|
+
if (item.processed)
|
|
425
|
+
formatted += ` | Processed`;
|
|
426
|
+
if (item.consolidated)
|
|
427
|
+
formatted += ` | Consolidated`;
|
|
428
|
+
if (item.forgotten)
|
|
429
|
+
formatted += ` | Forgotten`;
|
|
430
|
+
formatted += `\n`;
|
|
431
|
+
formatted += ` ${item.content?.slice(0, 150)}${item.content?.length > 150 ? '...' : ''}\n`;
|
|
432
|
+
if (item.timestamp)
|
|
433
|
+
formatted += ` Time: ${item.timestamp}\n`;
|
|
434
|
+
formatted += `\n`;
|
|
435
|
+
});
|
|
436
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
437
|
+
},
|
|
438
|
+
async stream_process(args, { memoryClient }) {
|
|
439
|
+
const result = await memoryClient.processStream(args.batch_size);
|
|
440
|
+
let formatted = `Stream Processing Complete\n`;
|
|
441
|
+
formatted += `==========================\n\n`;
|
|
442
|
+
formatted += `Processed: ${result.processed}\n`;
|
|
443
|
+
formatted += `Promoted to Memories: ${result.promoted}\n`;
|
|
444
|
+
formatted += `Discarded: ${result.discarded}\n`;
|
|
445
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
446
|
+
},
|
|
447
|
+
// ==========================================================================
|
|
448
|
+
// Selective Attention
|
|
449
|
+
// ==========================================================================
|
|
450
|
+
async search_with_attention(args, { memoryClient, resolvedUserId }) {
|
|
451
|
+
let results;
|
|
452
|
+
try {
|
|
453
|
+
results = await memoryClient.searchWithAttention({
|
|
454
|
+
query_text: args.query_text,
|
|
455
|
+
task_type: args.task_type,
|
|
456
|
+
limit: args.limit,
|
|
457
|
+
agent_id: args.agent_id || undefined,
|
|
458
|
+
user_id: args.user_id || resolvedUserId || undefined,
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
catch (error) {
|
|
462
|
+
const apiErr = handleApiError(error);
|
|
463
|
+
if (apiErr)
|
|
464
|
+
return apiErr;
|
|
465
|
+
throw error;
|
|
466
|
+
}
|
|
467
|
+
if (!results || !results.memories || results.memories.length === 0) {
|
|
468
|
+
return { content: [{ type: "text", text: "No memories found." }] };
|
|
469
|
+
}
|
|
470
|
+
let formatted = `Search results (task: ${results.task_type_used || args.task_type || 'general'}):\n`;
|
|
471
|
+
if (results.weights_used) {
|
|
472
|
+
formatted += `Weights: procedural=${results.weights_used.procedural}, semantic=${results.weights_used.semantic}, episodic=${results.weights_used.episodic}\n`;
|
|
473
|
+
}
|
|
474
|
+
formatted += `\n`;
|
|
475
|
+
results.memories.forEach((mem, i) => {
|
|
476
|
+
formatted += `${i + 1}. [${mem.memory_type}] (id: ${mem.id}) (score: ${mem.weighted_score?.toFixed(2) || mem.similarity?.toFixed(2)})\n`;
|
|
477
|
+
formatted += ` ${mem.content?.slice(0, 200)}...\n\n`;
|
|
478
|
+
});
|
|
479
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
480
|
+
},
|
|
481
|
+
// ==========================================================================
|
|
482
|
+
// User / Registration
|
|
483
|
+
// ==========================================================================
|
|
484
|
+
async user_register(args, { cpUrl }) {
|
|
485
|
+
const email = args.email;
|
|
486
|
+
const password = args.password;
|
|
487
|
+
const firstName = args.first_name;
|
|
488
|
+
const lastName = args.last_name;
|
|
489
|
+
const orgName = args.org_name;
|
|
490
|
+
const orgSlug = args.org_slug;
|
|
491
|
+
try {
|
|
492
|
+
const registerRes = await axios.post(`${cpUrl}/api/v1/auth/register`, {
|
|
493
|
+
email, password, first_name: firstName, last_name: lastName,
|
|
494
|
+
org_name: orgName, org_slug: orgSlug,
|
|
495
|
+
});
|
|
496
|
+
const data = registerRes.data;
|
|
497
|
+
// Handle new RegisterPendingResponse (email verification required)
|
|
498
|
+
if (data.pending || data.message) {
|
|
499
|
+
const text = `Registro iniciado com sucesso! Verifique seu email (${email}) para ativar a conta. Apos confirmacao, faca login e obtenha sua API key.`;
|
|
500
|
+
return { content: [{ type: "text", text }] };
|
|
501
|
+
}
|
|
502
|
+
// Fallback: legacy response format (direct registration without email verification)
|
|
503
|
+
let text = `Registration successful!\n`;
|
|
504
|
+
if (data.user)
|
|
505
|
+
text += `User: ${data.user.name} (${data.user.email})\n`;
|
|
506
|
+
if (data.organization) {
|
|
507
|
+
text += `Organization: ${data.organization.name} (${data.organization.slug})\n`;
|
|
508
|
+
if (data.organization.api_key)
|
|
509
|
+
text += `API Key: ${data.organization.api_key}\n`;
|
|
510
|
+
text += `Tenant DB: frinus_tenant_${data.organization.slug.replace(/-/g, '_')}\n`;
|
|
511
|
+
}
|
|
512
|
+
text += `\nUse the returned API key in FRINUS_API_KEY to authenticate.`;
|
|
513
|
+
return { content: [{ type: "text", text }] };
|
|
514
|
+
}
|
|
515
|
+
catch (error) {
|
|
516
|
+
const status = error?.response?.status;
|
|
517
|
+
const detail = error?.response?.data?.detail;
|
|
518
|
+
let message;
|
|
519
|
+
if (status && status >= 400 && status < 500) {
|
|
520
|
+
message = typeof detail === 'string' ? detail : `Client error (HTTP ${status}): ${JSON.stringify(detail) || 'Bad request'}`;
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
message = typeof detail === 'string' ? detail : error.message || 'Registration failed';
|
|
524
|
+
}
|
|
525
|
+
return {
|
|
526
|
+
content: [{ type: "text", text: `Registration failed: ${message}` }],
|
|
527
|
+
isError: true,
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
},
|
|
531
|
+
async user_get_context(args, { memoryClient }) {
|
|
532
|
+
const taskDescription = args.task_description || "general context";
|
|
533
|
+
const userEmail = args.user_email;
|
|
534
|
+
const result = await memoryClient.getUserContext(taskDescription, userEmail);
|
|
535
|
+
let formatted = `User Context:\n\n`;
|
|
536
|
+
if (result.context) {
|
|
537
|
+
formatted += result.context;
|
|
538
|
+
}
|
|
539
|
+
else if (result.memories && result.memories.length > 0) {
|
|
540
|
+
result.memories.forEach((mem, i) => {
|
|
541
|
+
formatted += `${i + 1}. [${mem.memory_type}] ${mem.content}\n\n`;
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
else {
|
|
545
|
+
formatted += "No relevant context found.";
|
|
546
|
+
}
|
|
547
|
+
if (userEmail)
|
|
548
|
+
formatted += `\n\nUser: ${userEmail}`;
|
|
549
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
550
|
+
},
|
|
551
|
+
// ==========================================================================
|
|
552
|
+
// Maintenance
|
|
553
|
+
// ==========================================================================
|
|
554
|
+
async heartbeat_tick(args, { memoryClient }) {
|
|
555
|
+
const result = await memoryClient.heartbeatTick({
|
|
556
|
+
agent_id: args.agent_id,
|
|
557
|
+
context_id: args.context_id,
|
|
558
|
+
session_id: args.session_id,
|
|
559
|
+
});
|
|
560
|
+
let formatted = `Heartbeat Tick\n`;
|
|
561
|
+
formatted += `Interaction Count: ${result.interaction_count}\n\n`;
|
|
562
|
+
if (result.actions_taken && result.actions_taken.length > 0) {
|
|
563
|
+
formatted += `Actions Taken:\n`;
|
|
564
|
+
result.actions_taken.forEach((action, i) => {
|
|
565
|
+
const status = action.success ? 'OK' : 'FAILED';
|
|
566
|
+
formatted += ` ${i + 1}. ${action.action}: ${status}\n`;
|
|
567
|
+
if (action.details && Object.keys(action.details).length > 0) {
|
|
568
|
+
formatted += ` Details: ${JSON.stringify(action.details)}\n`;
|
|
569
|
+
}
|
|
570
|
+
if (action.error)
|
|
571
|
+
formatted += ` Error: ${action.error}\n`;
|
|
572
|
+
});
|
|
573
|
+
formatted += `\n`;
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
formatted += `No actions triggered this tick.\n\n`;
|
|
577
|
+
}
|
|
578
|
+
if (result.next_scheduled_actions && Object.keys(result.next_scheduled_actions).length > 0) {
|
|
579
|
+
formatted += `Next Scheduled Actions:\n`;
|
|
580
|
+
for (const [action, count] of Object.entries(result.next_scheduled_actions)) {
|
|
581
|
+
formatted += ` ${action}: at interaction ${count}\n`;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
585
|
+
},
|
|
586
|
+
async sleep_run(args, { memoryClient }) {
|
|
587
|
+
const result = await memoryClient.runSleepCycle(args.phases);
|
|
588
|
+
let formatted = `Sleep Cycle ${result.status || 'completed'}:\n`;
|
|
589
|
+
formatted += `Cycle ID: ${result.cycle_id}\n`;
|
|
590
|
+
formatted += `Duration: ${result.duration_seconds?.toFixed(2)}s\n`;
|
|
591
|
+
formatted += `Total Memories Processed: ${result.total_memories_processed || 0}\n\n`;
|
|
592
|
+
formatted += `Results:\n`;
|
|
593
|
+
formatted += `- Flagged for Review: ${result.memories_flagged_for_review || 0}\n`;
|
|
594
|
+
formatted += `- Conflicts Detected: ${result.conflicts_detected || 0}\n`;
|
|
595
|
+
formatted += `- Conflicts Resolved: ${result.conflicts_resolved || 0}\n`;
|
|
596
|
+
formatted += `- Memories Consolidated: ${result.memories_consolidated || 0}\n`;
|
|
597
|
+
formatted += `- Memories Marked Obsolete: ${result.memories_marked_obsolete || 0}\n`;
|
|
598
|
+
formatted += `- Relevance Adjustments: ${result.relevance_adjustments || 0}\n`;
|
|
599
|
+
formatted += `- Transfer Suggestions: ${result.transfer_suggestions || 0}\n`;
|
|
600
|
+
formatted += `- Synced to Graph: ${result.memories_synced_to_graph || 0}\n`;
|
|
601
|
+
if (result.phases_completed && result.phases_completed.length > 0) {
|
|
602
|
+
formatted += `\nPhases Completed: ${result.phases_completed.join(', ')}\n`;
|
|
603
|
+
}
|
|
604
|
+
if (result.errors && result.errors.length > 0) {
|
|
605
|
+
formatted += `\nErrors: ${result.errors.length}\n`;
|
|
606
|
+
result.errors.forEach((err, i) => {
|
|
607
|
+
formatted += ` ${i + 1}. ${err}\n`;
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
611
|
+
},
|
|
612
|
+
// ==========================================================================
|
|
613
|
+
// Consolidation (Active Forgetting)
|
|
614
|
+
// ==========================================================================
|
|
615
|
+
async consolidation_detect_conflicts(args, { memoryClient }) {
|
|
616
|
+
const result = await memoryClient.detectConflicts(args.similarity_threshold, args.limit);
|
|
617
|
+
const conflicts = result.conflicts || [];
|
|
618
|
+
if (!conflicts || conflicts.length === 0) {
|
|
619
|
+
return { content: [{ type: "text", text: "No conflicts detected." }] };
|
|
620
|
+
}
|
|
621
|
+
let formatted = `Detected ${result.conflicts_found || conflicts.length} potential conflicts:\n\n`;
|
|
622
|
+
conflicts.forEach((conflict, i) => {
|
|
623
|
+
formatted += `${i + 1}. Similarity: ${conflict.similarity?.toFixed(2)}\n`;
|
|
624
|
+
formatted += ` Memory 1: ${conflict.memory1?.content?.slice(0, 100)}...\n`;
|
|
625
|
+
formatted += ` Memory 2: ${conflict.memory2?.content?.slice(0, 100)}...\n\n`;
|
|
626
|
+
});
|
|
627
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
628
|
+
},
|
|
629
|
+
async consolidation_resolve_conflict(args, { memoryClient }) {
|
|
630
|
+
const result = await memoryClient.resolveConflict({
|
|
631
|
+
keep_id: args.keep_id,
|
|
632
|
+
supersede_id: args.supersede_id,
|
|
633
|
+
resolution_note: args.resolution_note,
|
|
634
|
+
});
|
|
635
|
+
return {
|
|
636
|
+
content: [{
|
|
637
|
+
type: "text",
|
|
638
|
+
text: `Conflict resolved.\nKept: ${result.keep_id || args.keep_id}\nSuperseded: ${result.supersede_id || args.supersede_id}\nNote: ${args.resolution_note}`,
|
|
639
|
+
}],
|
|
640
|
+
};
|
|
641
|
+
},
|
|
642
|
+
async consolidation_detect_redundant(args, { memoryClient }) {
|
|
643
|
+
const result = await memoryClient.detectRedundant(args.similarity_threshold, args.limit);
|
|
644
|
+
const groups = result.groups || [];
|
|
645
|
+
if (!groups || groups.length === 0) {
|
|
646
|
+
return { content: [{ type: "text", text: "No redundant memories detected." }] };
|
|
647
|
+
}
|
|
648
|
+
let formatted = `Detected ${result.groups_found || groups.length} redundant memory groups (${result.total_redundant || 0} total memories):\n\n`;
|
|
649
|
+
groups.forEach((group, i) => {
|
|
650
|
+
formatted += `${i + 1}. Group similarity: ${group.avg_similarity?.toFixed(2) || 'N/A'}\n`;
|
|
651
|
+
formatted += ` Memories: ${group.memory_ids?.length || 0}\n`;
|
|
652
|
+
if (group.memories && group.memories.length > 0) {
|
|
653
|
+
formatted += ` Preview: ${group.memories[0]?.content?.slice(0, 100)}...\n`;
|
|
654
|
+
}
|
|
655
|
+
formatted += `\n`;
|
|
656
|
+
});
|
|
657
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
658
|
+
},
|
|
659
|
+
// ==========================================================================
|
|
660
|
+
// Hierarchical Memory
|
|
661
|
+
// ==========================================================================
|
|
662
|
+
async hierarchy_consolidate(args, { memoryClient }) {
|
|
663
|
+
const result = await memoryClient.hierarchyConsolidate({
|
|
664
|
+
memory_ids: args.memory_ids,
|
|
665
|
+
summary_content: args.summary_content,
|
|
666
|
+
agent_id: args.agent_id,
|
|
667
|
+
});
|
|
668
|
+
return {
|
|
669
|
+
content: [{
|
|
670
|
+
type: "text",
|
|
671
|
+
text: `Consolidation complete.\nSummary ID: ${result.summary_id}\nLevel: ${result.hierarchy_level}\nSource Memories: ${result.source_count}\nContent Preview: ${result.content_preview || result.summary_content?.slice(0, 100)}...`,
|
|
672
|
+
}],
|
|
673
|
+
};
|
|
674
|
+
},
|
|
675
|
+
async hierarchy_get_tree(args, { memoryClient }) {
|
|
676
|
+
const result = await memoryClient.getHierarchyTree(args.memory_id);
|
|
677
|
+
let formatted = `Hierarchy Tree for ${args.memory_id}:\n\n`;
|
|
678
|
+
if (result.memory) {
|
|
679
|
+
formatted += `Current Memory:\n`;
|
|
680
|
+
formatted += ` Level: ${result.memory.hierarchy_level || 'raw'}\n`;
|
|
681
|
+
formatted += ` Content: ${result.memory.content?.slice(0, 100)}...\n\n`;
|
|
682
|
+
}
|
|
683
|
+
if (result.parents && result.parents.length > 0) {
|
|
684
|
+
formatted += `Parents (${result.parents.length}):\n`;
|
|
685
|
+
result.parents.forEach((p, i) => {
|
|
686
|
+
formatted += ` ${i + 1}. [${p.hierarchy_level}] ${p.content?.slice(0, 80)}...\n`;
|
|
687
|
+
});
|
|
688
|
+
formatted += '\n';
|
|
689
|
+
}
|
|
690
|
+
if (result.children && result.children.length > 0) {
|
|
691
|
+
formatted += `Children (${result.children.length}):\n`;
|
|
692
|
+
result.children.forEach((c, i) => {
|
|
693
|
+
formatted += ` ${i + 1}. [${c.hierarchy_level}] ${c.content?.slice(0, 80)}...\n`;
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
if (!result.parents?.length && !result.children?.length) {
|
|
697
|
+
formatted += 'No hierarchy relationships found.';
|
|
698
|
+
}
|
|
699
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
700
|
+
},
|
|
701
|
+
// ==========================================================================
|
|
702
|
+
// Agent CRUD
|
|
703
|
+
// ==========================================================================
|
|
704
|
+
async agent_create(args, { agentClient }) {
|
|
705
|
+
const result = await agentClient.createAgent({
|
|
706
|
+
name: args.name,
|
|
707
|
+
template_id: args.template_id,
|
|
708
|
+
universe_id: args.universe_id,
|
|
709
|
+
persona: args.persona,
|
|
710
|
+
team_id: args.team_id,
|
|
711
|
+
is_team_lead: args.is_team_lead,
|
|
712
|
+
});
|
|
713
|
+
return {
|
|
714
|
+
content: [{
|
|
715
|
+
type: "text",
|
|
716
|
+
text: `Agent created.\nID: ${result.id}\nName: ${result.name}\nStatus: ${result.status || 'active'}`,
|
|
717
|
+
}],
|
|
718
|
+
};
|
|
719
|
+
},
|
|
720
|
+
async agent_list(args, { agentClient }) {
|
|
721
|
+
const results = await agentClient.listAgents(args.org_id);
|
|
722
|
+
const agents = Array.isArray(results) ? results : (results.agents || results.items || []);
|
|
723
|
+
if (!agents || agents.length === 0) {
|
|
724
|
+
return { content: [{ type: "text", text: "No agents found." }] };
|
|
725
|
+
}
|
|
726
|
+
let formatted = "Agents:\n\n";
|
|
727
|
+
agents.forEach((a, i) => {
|
|
728
|
+
formatted += `${i + 1}. ${a.name} (ID: ${a.id})\n`;
|
|
729
|
+
formatted += ` Status: ${a.status || 'active'} | Universe: ${a.universe_id || 'none'}\n\n`;
|
|
730
|
+
});
|
|
731
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
732
|
+
},
|
|
733
|
+
async agent_get(args, { agentClient }) {
|
|
734
|
+
const result = await agentClient.getAgent(args.agent_id);
|
|
735
|
+
let formatted = `Agent Details:\n`;
|
|
736
|
+
formatted += `ID: ${result.id}\n`;
|
|
737
|
+
formatted += `Name: ${result.name}\n`;
|
|
738
|
+
formatted += `Status: ${result.status || 'active'}\n`;
|
|
739
|
+
formatted += `Universe: ${result.universe_id || 'none'}\n`;
|
|
740
|
+
formatted += `Team: ${result.team_id || 'none'}\n`;
|
|
741
|
+
formatted += `Team Lead: ${result.is_team_lead || false}\n`;
|
|
742
|
+
if (result.persona) {
|
|
743
|
+
formatted += `\nPersona:\n`;
|
|
744
|
+
if (result.persona.personality)
|
|
745
|
+
formatted += ` Personality: ${result.persona.personality}\n`;
|
|
746
|
+
if (result.persona.instructions)
|
|
747
|
+
formatted += ` Instructions: ${result.persona.instructions}\n`;
|
|
748
|
+
if (result.persona.greeting)
|
|
749
|
+
formatted += ` Greeting: ${result.persona.greeting}\n`;
|
|
750
|
+
if (result.persona.language)
|
|
751
|
+
formatted += ` Language: ${result.persona.language}\n`;
|
|
752
|
+
if (result.persona.specialization)
|
|
753
|
+
formatted += ` Specialization: ${result.persona.specialization}\n`;
|
|
754
|
+
if (result.persona.forbidden_topics && result.persona.forbidden_topics.length > 0) {
|
|
755
|
+
formatted += ` Forbidden Topics: ${result.persona.forbidden_topics.join(', ')}\n`;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
if (result.created_at)
|
|
759
|
+
formatted += `\nCreated: ${result.created_at}`;
|
|
760
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
761
|
+
},
|
|
762
|
+
async agent_update(args, { agentClient }) {
|
|
763
|
+
const data = {};
|
|
764
|
+
if (args.name !== undefined)
|
|
765
|
+
data.name = args.name;
|
|
766
|
+
if (args.personality !== undefined)
|
|
767
|
+
data.personality = args.personality;
|
|
768
|
+
if (args.instructions !== undefined)
|
|
769
|
+
data.instructions = args.instructions;
|
|
770
|
+
if (args.greeting !== undefined)
|
|
771
|
+
data.greeting = args.greeting;
|
|
772
|
+
if (args.forbidden_topics !== undefined)
|
|
773
|
+
data.forbidden_topics = args.forbidden_topics;
|
|
774
|
+
if (args.language !== undefined)
|
|
775
|
+
data.language = args.language;
|
|
776
|
+
if (args.specialization !== undefined)
|
|
777
|
+
data.specialization = args.specialization;
|
|
778
|
+
const result = await agentClient.updateAgentPersona(args.agent_id, data);
|
|
779
|
+
return {
|
|
780
|
+
content: [{
|
|
781
|
+
type: "text",
|
|
782
|
+
text: `Agent updated.\nID: ${args.agent_id}\nName: ${result.name || 'unchanged'}`,
|
|
783
|
+
}],
|
|
784
|
+
};
|
|
785
|
+
},
|
|
786
|
+
async agent_delete(args, { agentClient }) {
|
|
787
|
+
await agentClient.deleteAgent(args.agent_id);
|
|
788
|
+
return {
|
|
789
|
+
content: [{
|
|
790
|
+
type: "text",
|
|
791
|
+
text: `Agent deleted.\nID: ${args.agent_id}`,
|
|
792
|
+
}],
|
|
793
|
+
};
|
|
794
|
+
},
|
|
795
|
+
async agent_bootstrap(args, { agentClient }) {
|
|
796
|
+
try {
|
|
797
|
+
const result = await agentClient.bootstrapAgent(args.agent_id);
|
|
798
|
+
const identity = result.identity || {};
|
|
799
|
+
const runtime = result.runtime || {};
|
|
800
|
+
let formatted = `# Agent Bootstrap\n\n`;
|
|
801
|
+
formatted += `## Identity\n`;
|
|
802
|
+
if (identity.id)
|
|
803
|
+
formatted += `ID: ${identity.id}\n`;
|
|
804
|
+
if (identity.name)
|
|
805
|
+
formatted += `Name: ${identity.name}\n`;
|
|
806
|
+
if (identity.role || identity.specialization)
|
|
807
|
+
formatted += `Role: ${identity.role || identity.specialization}\n`;
|
|
808
|
+
if (identity.universe_id)
|
|
809
|
+
formatted += `Universe: ${identity.universe_id}\n`;
|
|
810
|
+
if (identity.team_id)
|
|
811
|
+
formatted += `Team: ${identity.team_id}\n`;
|
|
812
|
+
if (result.persona_version !== undefined)
|
|
813
|
+
formatted += `Persona Version: ${result.persona_version}\n`;
|
|
814
|
+
formatted += `\n`;
|
|
815
|
+
if (result.system_prompt) {
|
|
816
|
+
formatted += `## System Prompt\n${result.system_prompt}\n\n`;
|
|
817
|
+
}
|
|
818
|
+
formatted += `## Runtime Context\n`;
|
|
819
|
+
const workingMemory = runtime.working_memory || [];
|
|
820
|
+
if (workingMemory.length > 0) {
|
|
821
|
+
formatted += `\n### Working Memory (${workingMemory.length})\n`;
|
|
822
|
+
workingMemory.forEach((item, i) => {
|
|
823
|
+
const content = typeof item === "string" ? item : (item.content || JSON.stringify(item));
|
|
824
|
+
formatted += `${i + 1}. ${content?.slice(0, 200)}\n`;
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
const recentMemories = runtime.recent_memories || [];
|
|
828
|
+
if (recentMemories.length > 0) {
|
|
829
|
+
formatted += `\n### Recent Memories (${recentMemories.length})\n`;
|
|
830
|
+
recentMemories.forEach((mem, i) => {
|
|
831
|
+
formatted += `${i + 1}. [${mem.memory_type || "memory"}] ${mem.content?.slice(0, 150)}\n`;
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
const skills = runtime.skills || [];
|
|
835
|
+
if (skills.length > 0) {
|
|
836
|
+
formatted += `\n### Skills (${skills.length})\n`;
|
|
837
|
+
skills.forEach((s) => {
|
|
838
|
+
const name = s.skill_name || s.name || s.slug || s;
|
|
839
|
+
formatted += `- ${name}${s.permission ? ` (${s.permission})` : ""}\n`;
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
const delegateTargets = runtime.delegate_targets || [];
|
|
843
|
+
if (delegateTargets.length > 0) {
|
|
844
|
+
formatted += `\n### Delegate Targets (${delegateTargets.length})\n`;
|
|
845
|
+
delegateTargets.forEach((t) => {
|
|
846
|
+
const name = t.name || t.agent_name || t.id || t;
|
|
847
|
+
formatted += `- ${name}${t.specialization ? ` — ${t.specialization}` : ""}${t.id && t.name ? ` (${t.id})` : ""}\n`;
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
851
|
+
}
|
|
852
|
+
catch (error) {
|
|
853
|
+
const apiErr = handleApiError(error);
|
|
854
|
+
if (apiErr)
|
|
855
|
+
return apiErr;
|
|
856
|
+
throw error;
|
|
857
|
+
}
|
|
858
|
+
},
|
|
859
|
+
async agent_reorient(args, { memoryClient }) {
|
|
860
|
+
try {
|
|
861
|
+
const result = await memoryClient.reorientAgent(args.agent_id, args.focus);
|
|
862
|
+
const identity = result.identity || {};
|
|
863
|
+
let formatted = `# Agent Reorient\n\n`;
|
|
864
|
+
formatted += `## Identity\n`;
|
|
865
|
+
if (identity.id)
|
|
866
|
+
formatted += `ID: ${identity.id}\n`;
|
|
867
|
+
if (identity.name)
|
|
868
|
+
formatted += `Name: ${identity.name}\n`;
|
|
869
|
+
if (identity.role || identity.specialization)
|
|
870
|
+
formatted += `Role: ${identity.role || identity.specialization}\n`;
|
|
871
|
+
if (result.persona_version !== undefined)
|
|
872
|
+
formatted += `Persona Version: ${result.persona_version}\n`;
|
|
873
|
+
formatted += `\n`;
|
|
874
|
+
const workingMemory = result.working_memory || [];
|
|
875
|
+
if (workingMemory.length > 0) {
|
|
876
|
+
formatted += `## Working Memory (${workingMemory.length})\n`;
|
|
877
|
+
workingMemory.forEach((item, i) => {
|
|
878
|
+
const content = typeof item === "string" ? item : (item.content || JSON.stringify(item));
|
|
879
|
+
formatted += `${i + 1}. ${content?.slice(0, 200)}\n`;
|
|
880
|
+
});
|
|
881
|
+
formatted += `\n`;
|
|
882
|
+
}
|
|
883
|
+
if (result.active_task) {
|
|
884
|
+
const t = result.active_task;
|
|
885
|
+
formatted += `## Active Task\n`;
|
|
886
|
+
formatted += `[${t.status || "unknown"}] ${t.title || t.id}${t.id && t.title ? ` (${t.id})` : ""}\n`;
|
|
887
|
+
if (t.description)
|
|
888
|
+
formatted += `${t.description?.slice(0, 200)}\n`;
|
|
889
|
+
formatted += `\n`;
|
|
890
|
+
}
|
|
891
|
+
const relevantMemories = result.relevant_memories || [];
|
|
892
|
+
if (relevantMemories.length > 0) {
|
|
893
|
+
formatted += `## Relevant Memories (${relevantMemories.length})\n`;
|
|
894
|
+
relevantMemories.forEach((mem, i) => {
|
|
895
|
+
formatted += `${i + 1}. [${mem.memory_type || "memory"}] (id: ${mem.id}) ${mem.content?.slice(0, 150)}\n`;
|
|
896
|
+
});
|
|
897
|
+
formatted += `\n`;
|
|
898
|
+
}
|
|
899
|
+
const recentHigh = result.recent_high_importance || [];
|
|
900
|
+
if (recentHigh.length > 0) {
|
|
901
|
+
formatted += `## Recent High-Importance (${recentHigh.length})\n`;
|
|
902
|
+
recentHigh.forEach((mem, i) => {
|
|
903
|
+
formatted += `${i + 1}. [${mem.memory_type || "memory"}] (importance: ${mem.importance?.toFixed(2) || "N/A"}) ${mem.content?.slice(0, 150)}\n`;
|
|
904
|
+
});
|
|
905
|
+
formatted += `\n`;
|
|
906
|
+
}
|
|
907
|
+
if (result.config && Object.keys(result.config).length > 0) {
|
|
908
|
+
formatted += `## Config\n${JSON.stringify(result.config, null, 2)}\n`;
|
|
909
|
+
}
|
|
910
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
911
|
+
}
|
|
912
|
+
catch (error) {
|
|
913
|
+
const apiErr = handleApiError(error);
|
|
914
|
+
if (apiErr)
|
|
915
|
+
return apiErr;
|
|
916
|
+
throw error;
|
|
917
|
+
}
|
|
918
|
+
},
|
|
919
|
+
// ==========================================================================
|
|
920
|
+
// Universe CRUD
|
|
921
|
+
// ==========================================================================
|
|
922
|
+
async universe_create(args, { cpClient }) {
|
|
923
|
+
const orgId = getResolvedTenantOrgId();
|
|
924
|
+
if (!orgId) {
|
|
925
|
+
return {
|
|
926
|
+
content: [{ type: "text", text: "Error: No organization context. API key may not be linked to an org." }],
|
|
927
|
+
isError: true,
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
const result = await cpClient.createUniverse(orgId, {
|
|
931
|
+
name: args.name,
|
|
932
|
+
slug: args.slug,
|
|
933
|
+
description: args.description,
|
|
934
|
+
});
|
|
935
|
+
return {
|
|
936
|
+
content: [{
|
|
937
|
+
type: "text",
|
|
938
|
+
text: `Universe created.\nID: ${result.id}\nName: ${result.name}\nSlug: ${result.slug}`,
|
|
939
|
+
}],
|
|
940
|
+
};
|
|
941
|
+
},
|
|
942
|
+
async universe_list(_args, { cpClient }) {
|
|
943
|
+
const orgId = getResolvedTenantOrgId();
|
|
944
|
+
if (!orgId) {
|
|
945
|
+
return {
|
|
946
|
+
content: [{ type: "text", text: "Error: No organization context. API key may not be linked to an org." }],
|
|
947
|
+
isError: true,
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
const results = await cpClient.listUniverses(orgId);
|
|
951
|
+
const universes = Array.isArray(results) ? results : (results.universes || results.items || []);
|
|
952
|
+
if (!universes || universes.length === 0) {
|
|
953
|
+
return { content: [{ type: "text", text: "No universes found." }] };
|
|
954
|
+
}
|
|
955
|
+
let formatted = "Universes:\n\n";
|
|
956
|
+
universes.forEach((u, i) => {
|
|
957
|
+
formatted += `${i + 1}. ${u.name} (ID: ${u.id})\n`;
|
|
958
|
+
formatted += ` Slug: ${u.slug}`;
|
|
959
|
+
if (u.description)
|
|
960
|
+
formatted += ` | ${u.description}`;
|
|
961
|
+
formatted += `\n\n`;
|
|
962
|
+
});
|
|
963
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
964
|
+
},
|
|
965
|
+
async universe_update(args, { cpClient }) {
|
|
966
|
+
const orgId = getResolvedTenantOrgId();
|
|
967
|
+
if (!orgId) {
|
|
968
|
+
return {
|
|
969
|
+
content: [{ type: "text", text: "Error: No organization context. API key may not be linked to an org." }],
|
|
970
|
+
isError: true,
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
const data = {};
|
|
974
|
+
if (args.name !== undefined)
|
|
975
|
+
data.name = args.name;
|
|
976
|
+
if (args.description !== undefined)
|
|
977
|
+
data.description = args.description;
|
|
978
|
+
const result = await cpClient.updateUniverse(orgId, args.universe_id, data);
|
|
979
|
+
return {
|
|
980
|
+
content: [{
|
|
981
|
+
type: "text",
|
|
982
|
+
text: `Universe updated.\nID: ${args.universe_id}\nName: ${result.name || 'unchanged'}`,
|
|
983
|
+
}],
|
|
984
|
+
};
|
|
985
|
+
},
|
|
986
|
+
async universe_delete(args, { cpClient }) {
|
|
987
|
+
const orgId = getResolvedTenantOrgId();
|
|
988
|
+
if (!orgId) {
|
|
989
|
+
return {
|
|
990
|
+
content: [{ type: "text", text: "Error: No organization context. API key may not be linked to an org." }],
|
|
991
|
+
isError: true,
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
await cpClient.deleteUniverse(orgId, args.universe_id);
|
|
995
|
+
return {
|
|
996
|
+
content: [{
|
|
997
|
+
type: "text",
|
|
998
|
+
text: `Universe deleted.\nID: ${args.universe_id}`,
|
|
999
|
+
}],
|
|
1000
|
+
};
|
|
1001
|
+
},
|
|
1002
|
+
// ==========================================================================
|
|
1003
|
+
// Knowledge Graph - Concepts
|
|
1004
|
+
// ==========================================================================
|
|
1005
|
+
async concept_create(args, { memoryClient }) {
|
|
1006
|
+
const result = await memoryClient.createConcept({
|
|
1007
|
+
name: args.name,
|
|
1008
|
+
universe_id: args.universe_id,
|
|
1009
|
+
description: args.description,
|
|
1010
|
+
});
|
|
1011
|
+
return {
|
|
1012
|
+
content: [{
|
|
1013
|
+
type: "text",
|
|
1014
|
+
text: `Concept created.\nID: ${result.concept_id}\nName: ${result.name}\nUniverse: ${result.universe_id}`,
|
|
1015
|
+
}],
|
|
1016
|
+
};
|
|
1017
|
+
},
|
|
1018
|
+
async concept_list(args, { memoryClient }) {
|
|
1019
|
+
const results = await memoryClient.listUniverseConcepts(args.universe_id);
|
|
1020
|
+
const concepts = Array.isArray(results) ? results : (results.concepts || results.items || []);
|
|
1021
|
+
if (!concepts || concepts.length === 0) {
|
|
1022
|
+
return { content: [{ type: "text", text: "No concepts found for this universe." }] };
|
|
1023
|
+
}
|
|
1024
|
+
let formatted = "Concepts:\n\n";
|
|
1025
|
+
concepts.forEach((c, i) => {
|
|
1026
|
+
formatted += `${i + 1}. ${c.name} (ID: ${c.concept_id})\n`;
|
|
1027
|
+
if (c.description)
|
|
1028
|
+
formatted += ` ${c.description}\n`;
|
|
1029
|
+
formatted += `\n`;
|
|
1030
|
+
});
|
|
1031
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
1032
|
+
},
|
|
1033
|
+
async concept_update(args, { memoryClient }) {
|
|
1034
|
+
const data = {};
|
|
1035
|
+
if (args.name !== undefined)
|
|
1036
|
+
data.name = args.name;
|
|
1037
|
+
if (args.description !== undefined)
|
|
1038
|
+
data.description = args.description;
|
|
1039
|
+
const result = await memoryClient.updateConcept(args.concept_id, data);
|
|
1040
|
+
return {
|
|
1041
|
+
content: [{
|
|
1042
|
+
type: "text",
|
|
1043
|
+
text: `Concept updated.\nID: ${args.concept_id}\nName: ${result.name || 'unchanged'}`,
|
|
1044
|
+
}],
|
|
1045
|
+
};
|
|
1046
|
+
},
|
|
1047
|
+
async concept_delete(args, { memoryClient }) {
|
|
1048
|
+
await memoryClient.deleteConcept(args.concept_id);
|
|
1049
|
+
return {
|
|
1050
|
+
content: [{
|
|
1051
|
+
type: "text",
|
|
1052
|
+
text: `Concept deleted (cascade: themes, topics, points).\nID: ${args.concept_id}`,
|
|
1053
|
+
}],
|
|
1054
|
+
};
|
|
1055
|
+
},
|
|
1056
|
+
// ==========================================================================
|
|
1057
|
+
// Knowledge Graph - Themes
|
|
1058
|
+
// ==========================================================================
|
|
1059
|
+
async theme_create(args, { memoryClient }) {
|
|
1060
|
+
const result = await memoryClient.createTheme({
|
|
1061
|
+
name: args.name,
|
|
1062
|
+
concept_id: args.concept_id,
|
|
1063
|
+
description: args.description,
|
|
1064
|
+
});
|
|
1065
|
+
return {
|
|
1066
|
+
content: [{
|
|
1067
|
+
type: "text",
|
|
1068
|
+
text: `Theme created.\nID: ${result.theme_id}\nName: ${result.name}\nConcept: ${result.concept_id}`,
|
|
1069
|
+
}],
|
|
1070
|
+
};
|
|
1071
|
+
},
|
|
1072
|
+
async theme_list(args, { memoryClient }) {
|
|
1073
|
+
const results = await memoryClient.listConceptThemes(args.concept_id);
|
|
1074
|
+
const themes = Array.isArray(results) ? results : (results.themes || results.items || []);
|
|
1075
|
+
if (!themes || themes.length === 0) {
|
|
1076
|
+
return { content: [{ type: "text", text: "No themes found for this concept." }] };
|
|
1077
|
+
}
|
|
1078
|
+
let formatted = "Themes:\n\n";
|
|
1079
|
+
themes.forEach((t, i) => {
|
|
1080
|
+
formatted += `${i + 1}. ${t.name} (ID: ${t.theme_id})\n`;
|
|
1081
|
+
if (t.description)
|
|
1082
|
+
formatted += ` ${t.description}\n`;
|
|
1083
|
+
formatted += `\n`;
|
|
1084
|
+
});
|
|
1085
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
1086
|
+
},
|
|
1087
|
+
async theme_update(args, { memoryClient }) {
|
|
1088
|
+
const data = {};
|
|
1089
|
+
if (args.name !== undefined)
|
|
1090
|
+
data.name = args.name;
|
|
1091
|
+
if (args.description !== undefined)
|
|
1092
|
+
data.description = args.description;
|
|
1093
|
+
const result = await memoryClient.updateTheme(args.theme_id, data);
|
|
1094
|
+
return {
|
|
1095
|
+
content: [{
|
|
1096
|
+
type: "text",
|
|
1097
|
+
text: `Theme updated.\nID: ${args.theme_id}\nName: ${result.name || 'unchanged'}`,
|
|
1098
|
+
}],
|
|
1099
|
+
};
|
|
1100
|
+
},
|
|
1101
|
+
async theme_delete(args, { memoryClient }) {
|
|
1102
|
+
await memoryClient.deleteTheme(args.theme_id);
|
|
1103
|
+
return {
|
|
1104
|
+
content: [{
|
|
1105
|
+
type: "text",
|
|
1106
|
+
text: `Theme deleted (cascade: topics, points).\nID: ${args.theme_id}`,
|
|
1107
|
+
}],
|
|
1108
|
+
};
|
|
1109
|
+
},
|
|
1110
|
+
// ==========================================================================
|
|
1111
|
+
// Knowledge Graph - Topics
|
|
1112
|
+
// ==========================================================================
|
|
1113
|
+
async topic_create(args, { memoryClient }) {
|
|
1114
|
+
const result = await memoryClient.createTopic({
|
|
1115
|
+
name: args.name,
|
|
1116
|
+
theme_id: args.theme_id,
|
|
1117
|
+
description: args.description,
|
|
1118
|
+
status: args.status,
|
|
1119
|
+
});
|
|
1120
|
+
return {
|
|
1121
|
+
content: [{
|
|
1122
|
+
type: "text",
|
|
1123
|
+
text: `Topic created.\nID: ${result.topic_id}\nName: ${result.name}\nTheme: ${result.theme_id}\nStatus: ${result.status || 'pending'}`,
|
|
1124
|
+
}],
|
|
1125
|
+
};
|
|
1126
|
+
},
|
|
1127
|
+
async topic_list(args, { memoryClient }) {
|
|
1128
|
+
const results = await memoryClient.listThemeTopics(args.theme_id);
|
|
1129
|
+
const topics = Array.isArray(results) ? results : (results.topics || results.items || []);
|
|
1130
|
+
if (!topics || topics.length === 0) {
|
|
1131
|
+
return { content: [{ type: "text", text: "No topics found for this theme." }] };
|
|
1132
|
+
}
|
|
1133
|
+
let formatted = "Topics:\n\n";
|
|
1134
|
+
topics.forEach((t, i) => {
|
|
1135
|
+
formatted += `${i + 1}. ${t.name} (ID: ${t.topic_id})\n`;
|
|
1136
|
+
formatted += ` Status: ${t.status || 'pending'}`;
|
|
1137
|
+
if (t.description)
|
|
1138
|
+
formatted += ` | ${t.description}`;
|
|
1139
|
+
formatted += `\n\n`;
|
|
1140
|
+
});
|
|
1141
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
1142
|
+
},
|
|
1143
|
+
async topic_update(args, { memoryClient }) {
|
|
1144
|
+
const hasMetadata = args.name !== undefined || args.description !== undefined;
|
|
1145
|
+
const hasStatus = args.status !== undefined;
|
|
1146
|
+
let result;
|
|
1147
|
+
if (hasMetadata) {
|
|
1148
|
+
const data = {};
|
|
1149
|
+
if (args.name !== undefined)
|
|
1150
|
+
data.name = args.name;
|
|
1151
|
+
if (args.description !== undefined)
|
|
1152
|
+
data.description = args.description;
|
|
1153
|
+
result = await memoryClient.updateTopic(args.topic_id, data);
|
|
1154
|
+
}
|
|
1155
|
+
if (hasStatus) {
|
|
1156
|
+
result = await memoryClient.updateTopicStatus(args.topic_id, args.status);
|
|
1157
|
+
}
|
|
1158
|
+
return {
|
|
1159
|
+
content: [{
|
|
1160
|
+
type: "text",
|
|
1161
|
+
text: `Topic updated.\nID: ${args.topic_id}\nName: ${result?.name || 'unchanged'}\nStatus: ${result?.status || args.status || 'unchanged'}`,
|
|
1162
|
+
}],
|
|
1163
|
+
};
|
|
1164
|
+
},
|
|
1165
|
+
async topic_delete(args, { memoryClient }) {
|
|
1166
|
+
await memoryClient.deleteTopic(args.topic_id);
|
|
1167
|
+
return {
|
|
1168
|
+
content: [{
|
|
1169
|
+
type: "text",
|
|
1170
|
+
text: `Topic deleted (cascade: points).\nID: ${args.topic_id}`,
|
|
1171
|
+
}],
|
|
1172
|
+
};
|
|
1173
|
+
},
|
|
1174
|
+
// ==========================================================================
|
|
1175
|
+
// Knowledge Graph - Points
|
|
1176
|
+
// ==========================================================================
|
|
1177
|
+
async point_create(args, { memoryClient }) {
|
|
1178
|
+
const result = await memoryClient.createPoint({
|
|
1179
|
+
name: args.name,
|
|
1180
|
+
topic_id: args.topic_id,
|
|
1181
|
+
description: args.description,
|
|
1182
|
+
status: args.status,
|
|
1183
|
+
});
|
|
1184
|
+
return {
|
|
1185
|
+
content: [{
|
|
1186
|
+
type: "text",
|
|
1187
|
+
text: `Point created.\nID: ${result.point_id}\nName: ${result.name}\nTopic: ${result.topic_id}\nStatus: ${result.status || 'pending'}`,
|
|
1188
|
+
}],
|
|
1189
|
+
};
|
|
1190
|
+
},
|
|
1191
|
+
async point_list(args, { memoryClient }) {
|
|
1192
|
+
const results = await memoryClient.listTopicPoints(args.topic_id);
|
|
1193
|
+
const points = Array.isArray(results) ? results : (results.points || results.items || []);
|
|
1194
|
+
if (!points || points.length === 0) {
|
|
1195
|
+
return { content: [{ type: "text", text: "No points found for this topic." }] };
|
|
1196
|
+
}
|
|
1197
|
+
let formatted = "Points:\n\n";
|
|
1198
|
+
points.forEach((p, i) => {
|
|
1199
|
+
formatted += `${i + 1}. ${p.name} (ID: ${p.point_id})\n`;
|
|
1200
|
+
formatted += ` Status: ${p.status || 'pending'}`;
|
|
1201
|
+
if (p.description)
|
|
1202
|
+
formatted += ` | ${p.description}`;
|
|
1203
|
+
formatted += `\n\n`;
|
|
1204
|
+
});
|
|
1205
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
1206
|
+
},
|
|
1207
|
+
async point_update(args, { memoryClient }) {
|
|
1208
|
+
const hasMetadata = args.name !== undefined || args.description !== undefined || args.content !== undefined;
|
|
1209
|
+
const hasStatus = args.status !== undefined;
|
|
1210
|
+
let result;
|
|
1211
|
+
if (hasMetadata) {
|
|
1212
|
+
const data = {};
|
|
1213
|
+
if (args.name !== undefined)
|
|
1214
|
+
data.name = args.name;
|
|
1215
|
+
if (args.description !== undefined)
|
|
1216
|
+
data.description = args.description;
|
|
1217
|
+
if (args.content !== undefined)
|
|
1218
|
+
data.content = args.content;
|
|
1219
|
+
result = await memoryClient.updatePoint(args.point_id, data);
|
|
1220
|
+
}
|
|
1221
|
+
if (hasStatus) {
|
|
1222
|
+
result = await memoryClient.updatePointStatus(args.point_id, args.status);
|
|
1223
|
+
}
|
|
1224
|
+
return {
|
|
1225
|
+
content: [{
|
|
1226
|
+
type: "text",
|
|
1227
|
+
text: `Point updated.\nID: ${args.point_id}\nName: ${result?.name || 'unchanged'}\nStatus: ${result?.status || args.status || 'unchanged'}`,
|
|
1228
|
+
}],
|
|
1229
|
+
};
|
|
1230
|
+
},
|
|
1231
|
+
async point_delete(args, { memoryClient }) {
|
|
1232
|
+
await memoryClient.deletePoint(args.point_id);
|
|
1233
|
+
return {
|
|
1234
|
+
content: [{
|
|
1235
|
+
type: "text",
|
|
1236
|
+
text: `Point deleted.\nID: ${args.point_id}`,
|
|
1237
|
+
}],
|
|
1238
|
+
};
|
|
1239
|
+
},
|
|
1240
|
+
// ==========================================================================
|
|
1241
|
+
// Knowledge Hierarchy
|
|
1242
|
+
// ==========================================================================
|
|
1243
|
+
async universe_hierarchy(args, { memoryClient }) {
|
|
1244
|
+
const result = await memoryClient.getUniverseHierarchy(args.universe_id);
|
|
1245
|
+
let formatted = `Knowledge Hierarchy for Universe: ${args.universe_id}\n`;
|
|
1246
|
+
formatted += `${"=".repeat(50)}\n\n`;
|
|
1247
|
+
const concepts = Array.isArray(result) ? result : (result.concepts || result.hierarchy || []);
|
|
1248
|
+
if (!concepts || concepts.length === 0) {
|
|
1249
|
+
formatted += "No knowledge hierarchy found for this universe.";
|
|
1250
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
1251
|
+
}
|
|
1252
|
+
concepts.forEach((concept, ci) => {
|
|
1253
|
+
formatted += `[L0] ${ci + 1}. ${concept.name} (ID: ${concept.concept_id || concept.id})\n`;
|
|
1254
|
+
if (concept.description)
|
|
1255
|
+
formatted += ` ${concept.description}\n`;
|
|
1256
|
+
const themes = concept.themes || [];
|
|
1257
|
+
themes.forEach((theme, ti) => {
|
|
1258
|
+
formatted += ` [L1] ${ci + 1}.${ti + 1}. ${theme.name} (ID: ${theme.theme_id || theme.id})\n`;
|
|
1259
|
+
if (theme.description)
|
|
1260
|
+
formatted += ` ${theme.description}\n`;
|
|
1261
|
+
const topics = theme.topics || [];
|
|
1262
|
+
topics.forEach((topic, toi) => {
|
|
1263
|
+
const statusIcon = topic.status === 'completed' ? '[x]' : topic.status === 'in_progress' ? '[~]' : '[ ]';
|
|
1264
|
+
formatted += ` [L2] ${statusIcon} ${ci + 1}.${ti + 1}.${toi + 1}. ${topic.name} (ID: ${topic.topic_id || topic.id})\n`;
|
|
1265
|
+
const points = topic.points || [];
|
|
1266
|
+
points.forEach((point, pi) => {
|
|
1267
|
+
const pIcon = point.status === 'completed' ? '[x]' : '[ ]';
|
|
1268
|
+
formatted += ` [L3] ${pIcon} ${ci + 1}.${ti + 1}.${toi + 1}.${pi + 1}. ${point.name} (ID: ${point.point_id || point.id})\n`;
|
|
1269
|
+
});
|
|
1270
|
+
});
|
|
1271
|
+
});
|
|
1272
|
+
formatted += `\n`;
|
|
1273
|
+
});
|
|
1274
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
1275
|
+
},
|
|
1276
|
+
// ==========================================================================
|
|
1277
|
+
// Training
|
|
1278
|
+
// ==========================================================================
|
|
1279
|
+
async training_teach(args, { memoryClient }) {
|
|
1280
|
+
const result = await memoryClient.trainingTeach({
|
|
1281
|
+
content: args.content,
|
|
1282
|
+
type: args.type,
|
|
1283
|
+
importance: args.importance,
|
|
1284
|
+
universe_id: args.universe_id,
|
|
1285
|
+
});
|
|
1286
|
+
return {
|
|
1287
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
1288
|
+
};
|
|
1289
|
+
},
|
|
1290
|
+
async training_qa(args, { memoryClient }) {
|
|
1291
|
+
const result = await memoryClient.trainingQa({
|
|
1292
|
+
pairs: args.pairs,
|
|
1293
|
+
importance: args.importance,
|
|
1294
|
+
universe_id: args.universe_id,
|
|
1295
|
+
});
|
|
1296
|
+
return {
|
|
1297
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
1298
|
+
};
|
|
1299
|
+
},
|
|
1300
|
+
async training_stats(_args, { memoryClient }) {
|
|
1301
|
+
const result = await memoryClient.trainingStats();
|
|
1302
|
+
return {
|
|
1303
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
1304
|
+
};
|
|
1305
|
+
},
|
|
1306
|
+
async training_gaps(_args, { memoryClient }) {
|
|
1307
|
+
const result = await memoryClient.trainingGaps();
|
|
1308
|
+
return {
|
|
1309
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
1310
|
+
};
|
|
1311
|
+
},
|
|
1312
|
+
async training_recent(args, { memoryClient }) {
|
|
1313
|
+
const result = await memoryClient.trainingRecent({
|
|
1314
|
+
limit: args.limit,
|
|
1315
|
+
});
|
|
1316
|
+
return {
|
|
1317
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
1318
|
+
};
|
|
1319
|
+
},
|
|
1320
|
+
// -------------------------------------------------------------------------
|
|
1321
|
+
// Agent Invocation
|
|
1322
|
+
// -------------------------------------------------------------------------
|
|
1323
|
+
async agent_invoke(args, { agentClient }) {
|
|
1324
|
+
const result = await agentClient.invokeAgent({
|
|
1325
|
+
agent_id: args.agent_id,
|
|
1326
|
+
agent_name: args.agent_name,
|
|
1327
|
+
message: args.message,
|
|
1328
|
+
context: args.context,
|
|
1329
|
+
timeout_seconds: args.timeout_seconds,
|
|
1330
|
+
});
|
|
1331
|
+
const lines = [
|
|
1332
|
+
`Agent: ${result.agent_name || result.agent_id}`,
|
|
1333
|
+
`Status: ${result.status}`,
|
|
1334
|
+
`Duration: ${result.duration_ms}ms`,
|
|
1335
|
+
``,
|
|
1336
|
+
`Result:`,
|
|
1337
|
+
result.result,
|
|
1338
|
+
];
|
|
1339
|
+
if (result.tool_calls?.length > 0) {
|
|
1340
|
+
lines.push(``, `Tools used: ${result.tool_calls.map((t) => t.name).join(", ")}`);
|
|
1341
|
+
}
|
|
1342
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
1343
|
+
},
|
|
1344
|
+
// -------------------------------------------------------------------------
|
|
1345
|
+
// Task Management
|
|
1346
|
+
// -------------------------------------------------------------------------
|
|
1347
|
+
async task_create(args, { memoryClient }) {
|
|
1348
|
+
const result = await memoryClient.createTask({
|
|
1349
|
+
title: args.title,
|
|
1350
|
+
description: args.description,
|
|
1351
|
+
assigned_agent_id: args.assigned_agent_id,
|
|
1352
|
+
parent_task_id: args.parent_task_id,
|
|
1353
|
+
priority: args.priority,
|
|
1354
|
+
input_data: args.input_data,
|
|
1355
|
+
});
|
|
1356
|
+
const t = result;
|
|
1357
|
+
const lines = [
|
|
1358
|
+
`Task created: ${t.id}`,
|
|
1359
|
+
`Title: ${t.title}`,
|
|
1360
|
+
`Status: ${t.status}`,
|
|
1361
|
+
t.assigned_agent_id ? `Assigned to: ${t.assigned_agent_id}` : null,
|
|
1362
|
+
t.parent_task_id ? `Parent task: ${t.parent_task_id}` : null,
|
|
1363
|
+
`Priority: ${t.priority}`,
|
|
1364
|
+
`Created: ${t.created_at}`,
|
|
1365
|
+
].filter(Boolean);
|
|
1366
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
1367
|
+
},
|
|
1368
|
+
async task_get(args, { memoryClient }) {
|
|
1369
|
+
const result = await memoryClient.getTask(args.task_id);
|
|
1370
|
+
return {
|
|
1371
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
1372
|
+
};
|
|
1373
|
+
},
|
|
1374
|
+
async task_list(args, { memoryClient }) {
|
|
1375
|
+
const result = await memoryClient.listTasks({
|
|
1376
|
+
status: args.status,
|
|
1377
|
+
assigned_agent_id: args.assigned_agent_id,
|
|
1378
|
+
parent_task_id: args.parent_task_id,
|
|
1379
|
+
limit: args.limit,
|
|
1380
|
+
});
|
|
1381
|
+
const tasks = result.tasks || [];
|
|
1382
|
+
if (tasks.length === 0) {
|
|
1383
|
+
return { content: [{ type: "text", text: "No tasks found." }] };
|
|
1384
|
+
}
|
|
1385
|
+
const lines = tasks.map((t) => `[${t.status}] ${t.title} (${t.id}) priority=${t.priority}${t.assigned_agent_id ? ` → agent:${t.assigned_agent_id}` : ""}`);
|
|
1386
|
+
return {
|
|
1387
|
+
content: [{ type: "text", text: `${result.total} tasks:\n${lines.join("\n")}` }],
|
|
1388
|
+
};
|
|
1389
|
+
},
|
|
1390
|
+
async task_update(args, { memoryClient }) {
|
|
1391
|
+
const result = await memoryClient.updateTask(args.task_id, {
|
|
1392
|
+
status: args.status,
|
|
1393
|
+
output_data: args.output_data,
|
|
1394
|
+
error_message: args.error_message,
|
|
1395
|
+
});
|
|
1396
|
+
const t = result;
|
|
1397
|
+
const lines = [
|
|
1398
|
+
`Task updated: ${t.id}`,
|
|
1399
|
+
`Title: ${t.title}`,
|
|
1400
|
+
`Status: ${t.status}`,
|
|
1401
|
+
t.completed_at ? `Completed: ${t.completed_at}` : null,
|
|
1402
|
+
t.error_message ? `Error: ${t.error_message}` : null,
|
|
1403
|
+
].filter(Boolean);
|
|
1404
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
1405
|
+
},
|
|
1406
|
+
// -------------------------------------------------------------------------
|
|
1407
|
+
// Skills
|
|
1408
|
+
// -------------------------------------------------------------------------
|
|
1409
|
+
async skill_list(args, { memoryClient }) {
|
|
1410
|
+
const result = await memoryClient.listSkills(args.category);
|
|
1411
|
+
const skills = result.skills || result;
|
|
1412
|
+
const lines = [`Found ${Array.isArray(skills) ? skills.length : 0} skills:\n`];
|
|
1413
|
+
if (Array.isArray(skills)) {
|
|
1414
|
+
for (const s of skills) {
|
|
1415
|
+
lines.push(`- **${s.name}** (${s.slug}) [${s.category || 'uncategorized'}]`);
|
|
1416
|
+
if (s.description)
|
|
1417
|
+
lines.push(` ${s.description}`);
|
|
1418
|
+
lines.push(` Handler: ${s.handler_type} | Active: ${s.is_active}`);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
1422
|
+
},
|
|
1423
|
+
async skill_agent_list(args, { memoryClient }) {
|
|
1424
|
+
const agentId = args.agent_id;
|
|
1425
|
+
const skills = await memoryClient.getAgentSkills(agentId);
|
|
1426
|
+
const list = Array.isArray(skills) ? skills : [];
|
|
1427
|
+
const lines = [`Agent ${agentId} has ${list.length} skills:\n`];
|
|
1428
|
+
for (const s of list) {
|
|
1429
|
+
lines.push(`- **${s.skill_name}** (${s.skill_slug}) — permission: ${s.permission}`);
|
|
1430
|
+
}
|
|
1431
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
1432
|
+
},
|
|
1433
|
+
async skill_assign(args, { memoryClient }) {
|
|
1434
|
+
const result = await memoryClient.assignSkill(args.agent_id, args.skill_id, args.permission);
|
|
1435
|
+
return {
|
|
1436
|
+
content: [{
|
|
1437
|
+
type: "text",
|
|
1438
|
+
text: `Skill ${result.skill_name} assigned to agent ${result.agent_id} with permission: ${result.permission}`,
|
|
1439
|
+
}],
|
|
1440
|
+
};
|
|
1441
|
+
},
|
|
1442
|
+
async skill_remove(args, { memoryClient }) {
|
|
1443
|
+
await memoryClient.removeSkill(args.agent_id, args.skill_id);
|
|
1444
|
+
return {
|
|
1445
|
+
content: [{ type: "text", text: `Skill ${args.skill_id} removed from agent ${args.agent_id}` }],
|
|
1446
|
+
};
|
|
1447
|
+
},
|
|
1448
|
+
// ==========================================================================
|
|
1449
|
+
// Credential Vault
|
|
1450
|
+
// ==========================================================================
|
|
1451
|
+
async credential_store(args, { cpClient }) {
|
|
1452
|
+
try {
|
|
1453
|
+
const ref = args.ref;
|
|
1454
|
+
const data = args.data;
|
|
1455
|
+
const metadata = args.metadata;
|
|
1456
|
+
const result = await cpClient.storeCredential(ref, data, metadata);
|
|
1457
|
+
return {
|
|
1458
|
+
content: [{
|
|
1459
|
+
type: "text",
|
|
1460
|
+
text: `Credential stored successfully.\nRef: ${ref}\nID: ${result.id}`,
|
|
1461
|
+
}],
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
catch (error) {
|
|
1465
|
+
const apiErr = handleApiError(error);
|
|
1466
|
+
if (apiErr)
|
|
1467
|
+
return apiErr;
|
|
1468
|
+
throw error;
|
|
1469
|
+
}
|
|
1470
|
+
},
|
|
1471
|
+
async credential_get(args, { cpClient }) {
|
|
1472
|
+
try {
|
|
1473
|
+
const ref = args.ref;
|
|
1474
|
+
const result = await cpClient.getCredential(ref);
|
|
1475
|
+
return {
|
|
1476
|
+
content: [{
|
|
1477
|
+
type: "text",
|
|
1478
|
+
text: JSON.stringify(result, null, 2),
|
|
1479
|
+
}],
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
catch (error) {
|
|
1483
|
+
if (error?.response?.status === 404) {
|
|
1484
|
+
return {
|
|
1485
|
+
content: [{
|
|
1486
|
+
type: "text",
|
|
1487
|
+
text: `No credential found for ref '${args.ref}'`,
|
|
1488
|
+
}],
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
const apiErr = handleApiError(error);
|
|
1492
|
+
if (apiErr)
|
|
1493
|
+
return apiErr;
|
|
1494
|
+
throw error;
|
|
1495
|
+
}
|
|
1496
|
+
},
|
|
1497
|
+
async credential_list(_args, { cpClient }) {
|
|
1498
|
+
try {
|
|
1499
|
+
const result = await cpClient.listCredentials();
|
|
1500
|
+
const items = result.items || [];
|
|
1501
|
+
if (items.length === 0) {
|
|
1502
|
+
return {
|
|
1503
|
+
content: [{
|
|
1504
|
+
type: "text",
|
|
1505
|
+
text: "No credentials stored.",
|
|
1506
|
+
}],
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
const lines = items.map((c) => `- ${c.integration_type} (created: ${c.created_at})`);
|
|
1510
|
+
return {
|
|
1511
|
+
content: [{
|
|
1512
|
+
type: "text",
|
|
1513
|
+
text: `Stored credentials (${items.length}):\n${lines.join("\n")}`,
|
|
1514
|
+
}],
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
catch (error) {
|
|
1518
|
+
const apiErr = handleApiError(error);
|
|
1519
|
+
if (apiErr)
|
|
1520
|
+
return apiErr;
|
|
1521
|
+
throw error;
|
|
1522
|
+
}
|
|
1523
|
+
},
|
|
1524
|
+
async credential_delete(args, { cpClient }) {
|
|
1525
|
+
try {
|
|
1526
|
+
const ref = args.ref;
|
|
1527
|
+
await cpClient.deleteCredential(ref);
|
|
1528
|
+
return {
|
|
1529
|
+
content: [{
|
|
1530
|
+
type: "text",
|
|
1531
|
+
text: `Credential '${ref}' deleted successfully.`,
|
|
1532
|
+
}],
|
|
1533
|
+
};
|
|
1534
|
+
}
|
|
1535
|
+
catch (error) {
|
|
1536
|
+
if (error?.response?.status === 404) {
|
|
1537
|
+
return {
|
|
1538
|
+
content: [{
|
|
1539
|
+
type: "text",
|
|
1540
|
+
text: `No credential found for ref '${args.ref}'`,
|
|
1541
|
+
}],
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
const apiErr = handleApiError(error);
|
|
1545
|
+
if (apiErr)
|
|
1546
|
+
return apiErr;
|
|
1547
|
+
throw error;
|
|
1548
|
+
}
|
|
1549
|
+
},
|
|
1550
|
+
};
|
|
1551
|
+
// ---------------------------------------------------------------------------
|
|
1552
|
+
// Dispatcher: looks up the handler and executes it with error handling
|
|
1553
|
+
// ---------------------------------------------------------------------------
|
|
1554
|
+
/**
|
|
1555
|
+
* Execute a tool by name. Wraps the individual handler with standard error
|
|
1556
|
+
* handling (auth errors, validation errors, unknown tools).
|
|
1557
|
+
*/
|
|
1558
|
+
export async function dispatchTool(name, args, deps) {
|
|
1559
|
+
const handler = handlers[name];
|
|
1560
|
+
if (!handler) {
|
|
1561
|
+
return {
|
|
1562
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
1563
|
+
isError: true,
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
try {
|
|
1567
|
+
try {
|
|
1568
|
+
return await handler(args, deps);
|
|
1569
|
+
}
|
|
1570
|
+
catch (error) {
|
|
1571
|
+
// Check for 401/403/502/504 auth/infra errors first
|
|
1572
|
+
const authError = handleApiError(error);
|
|
1573
|
+
if (authError)
|
|
1574
|
+
return authError;
|
|
1575
|
+
let message;
|
|
1576
|
+
const detail = error.response?.data?.detail;
|
|
1577
|
+
if (Array.isArray(detail)) {
|
|
1578
|
+
// Pydantic validation errors come as an array of objects
|
|
1579
|
+
message = detail.map((d) => {
|
|
1580
|
+
const loc = d.loc ? d.loc.join('.') : 'unknown';
|
|
1581
|
+
return `${loc}: ${d.msg}`;
|
|
1582
|
+
}).join('; ');
|
|
1583
|
+
}
|
|
1584
|
+
else if (typeof detail === 'string') {
|
|
1585
|
+
message = detail;
|
|
1586
|
+
}
|
|
1587
|
+
else if (typeof detail === 'object' && detail !== null) {
|
|
1588
|
+
message = JSON.stringify(detail);
|
|
1589
|
+
}
|
|
1590
|
+
else {
|
|
1591
|
+
message = error.message || "Unknown error";
|
|
1592
|
+
}
|
|
1593
|
+
console.error('[MCP] Tool error:', name, message);
|
|
1594
|
+
return {
|
|
1595
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1596
|
+
isError: true,
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
catch (unexpected) {
|
|
1601
|
+
// Outermost safety net: guarantee a valid ToolResult even on truly
|
|
1602
|
+
// unexpected errors (e.g. TypeError in the error-handling block above).
|
|
1603
|
+
console.error('[MCP] Unhandled tool error:', name, unexpected);
|
|
1604
|
+
return {
|
|
1605
|
+
content: [{ type: "text", text: `Tool error: ${unexpected instanceof Error ? unexpected.message : String(unexpected)}` }],
|
|
1606
|
+
isError: true,
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
}
|