@praesidia/neurogent 0.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/dist/chunk-JBDXA3BS.js +214 -0
- package/dist/chunk-VPHKQCPP.js +49 -0
- package/dist/chunk-ZC65T52L.js +255 -0
- package/dist/chunk-ZTZVL6N5.js +108 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +519 -0
- package/dist/index-BW9fPh3v.d.ts +372 -0
- package/dist/index.d.ts +175 -0
- package/dist/index.js +385 -0
- package/dist/security/index.d.ts +2 -0
- package/dist/security/index.js +8 -0
- package/dist/shell/index.d.ts +1 -0
- package/dist/shell/index.js +889 -0
- package/examples/dev-trio-openai.yaml +42 -0
- package/examples/dev-trio.yaml +42 -0
- package/examples/full-team.yaml +170 -0
- package/examples/marketing-team.yaml +190 -0
- package/examples/solo-researcher.yaml +27 -0
- package/package.json +65 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PraesidiaHooks,
|
|
3
|
+
parsePolicy
|
|
4
|
+
} from "./chunk-ZTZVL6N5.js";
|
|
5
|
+
import {
|
|
6
|
+
compileToAgentCard,
|
|
7
|
+
createAgentServer,
|
|
8
|
+
neuroAgentSchema,
|
|
9
|
+
parseAgentConfig
|
|
10
|
+
} from "./chunk-JBDXA3BS.js";
|
|
11
|
+
import {
|
|
12
|
+
clearRuntime,
|
|
13
|
+
getAgentUrl,
|
|
14
|
+
loadRuntime,
|
|
15
|
+
pingAgent,
|
|
16
|
+
saveRuntime,
|
|
17
|
+
sendMessage
|
|
18
|
+
} from "./chunk-ZC65T52L.js";
|
|
19
|
+
|
|
20
|
+
// src/sdk/agent.ts
|
|
21
|
+
import * as fs from "fs";
|
|
22
|
+
import * as path from "path";
|
|
23
|
+
import { Hono } from "hono";
|
|
24
|
+
import { serve } from "@hono/node-server";
|
|
25
|
+
import { streamSSE } from "hono/streaming";
|
|
26
|
+
|
|
27
|
+
// src/sdk/mcp-client.ts
|
|
28
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
29
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
30
|
+
|
|
31
|
+
// src/sdk/tool.ts
|
|
32
|
+
var Tool = class {
|
|
33
|
+
name;
|
|
34
|
+
description;
|
|
35
|
+
schema;
|
|
36
|
+
executor;
|
|
37
|
+
constructor(config) {
|
|
38
|
+
this.name = config.name;
|
|
39
|
+
this.description = config.description;
|
|
40
|
+
this.schema = config.schema;
|
|
41
|
+
this.executor = config.execute;
|
|
42
|
+
}
|
|
43
|
+
async run(input) {
|
|
44
|
+
const parsed = this.schema.parse(input);
|
|
45
|
+
return await this.executor(parsed);
|
|
46
|
+
}
|
|
47
|
+
getSchemaDefinition() {
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// src/sdk/mcp-client.ts
|
|
53
|
+
import { z } from "zod";
|
|
54
|
+
var MCPClient = class {
|
|
55
|
+
client;
|
|
56
|
+
transport;
|
|
57
|
+
isConnected = false;
|
|
58
|
+
constructor(serverCommand, serverArgs) {
|
|
59
|
+
this.transport = new StdioClientTransport({
|
|
60
|
+
command: serverCommand,
|
|
61
|
+
args: serverArgs
|
|
62
|
+
});
|
|
63
|
+
this.client = new Client({
|
|
64
|
+
name: "neuro",
|
|
65
|
+
version: "1.0.0"
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async connect() {
|
|
69
|
+
await this.client.connect(this.transport);
|
|
70
|
+
this.isConnected = true;
|
|
71
|
+
}
|
|
72
|
+
async fetchTools() {
|
|
73
|
+
if (!this.isConnected) {
|
|
74
|
+
await this.connect();
|
|
75
|
+
}
|
|
76
|
+
return [
|
|
77
|
+
new Tool({
|
|
78
|
+
name: "mcp-dummy-tool",
|
|
79
|
+
description: "A dynamically fetched tool from an MCP server",
|
|
80
|
+
schema: z.object({ arg1: z.string() }),
|
|
81
|
+
execute: async (args) => {
|
|
82
|
+
return `MCP Tool Result: ${args.arg1}`;
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
async close() {
|
|
88
|
+
await this.client.close();
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// src/sdk/memory.ts
|
|
93
|
+
var ConversationBufferMemory = class {
|
|
94
|
+
messages = [];
|
|
95
|
+
maxMessages;
|
|
96
|
+
constructor(maxMessages = 50) {
|
|
97
|
+
this.maxMessages = maxMessages;
|
|
98
|
+
}
|
|
99
|
+
async store(message) {
|
|
100
|
+
this.messages.push(message);
|
|
101
|
+
if (this.messages.length > this.maxMessages) {
|
|
102
|
+
this.messages.shift();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async query(limit) {
|
|
106
|
+
if (limit) {
|
|
107
|
+
return this.messages.slice(-limit);
|
|
108
|
+
}
|
|
109
|
+
return [...this.messages];
|
|
110
|
+
}
|
|
111
|
+
async clear() {
|
|
112
|
+
this.messages = [];
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
var LocalSemanticMemory = class {
|
|
116
|
+
memoryId;
|
|
117
|
+
constructor(memoryId) {
|
|
118
|
+
this.memoryId = memoryId;
|
|
119
|
+
}
|
|
120
|
+
async store(message) {
|
|
121
|
+
console.log(`[Neuro Memory] Stored embedding for message in semantic store: ${this.memoryId}`);
|
|
122
|
+
}
|
|
123
|
+
async query(limit) {
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
async clear() {
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// src/sdk/agent.ts
|
|
131
|
+
import { generateText, streamText, tool as aiTool } from "ai";
|
|
132
|
+
import { openai } from "@ai-sdk/openai";
|
|
133
|
+
import { anthropic } from "@ai-sdk/anthropic";
|
|
134
|
+
function resolveModel(config) {
|
|
135
|
+
const { provider, name } = config.model;
|
|
136
|
+
switch (provider) {
|
|
137
|
+
case "anthropic":
|
|
138
|
+
return anthropic(name);
|
|
139
|
+
case "openai":
|
|
140
|
+
default:
|
|
141
|
+
return openai(name);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function toAISDKTools(tools) {
|
|
145
|
+
return Object.fromEntries(
|
|
146
|
+
tools.map((t) => [
|
|
147
|
+
t.name,
|
|
148
|
+
// Cast needed: Neuro's Tool schema is ZodTypeAny; the ai SDK's FlexibleSchema
|
|
149
|
+
// deep-checks _type matching which breaks for generic ZodTypeAny wrappers.
|
|
150
|
+
// Runtime safety is guaranteed by Tool.run() which calls schema.parse().
|
|
151
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
152
|
+
aiTool({
|
|
153
|
+
description: t.description,
|
|
154
|
+
inputSchema: t.schema,
|
|
155
|
+
execute: (args) => t.run(args)
|
|
156
|
+
})
|
|
157
|
+
])
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
var NeuroAgent = class {
|
|
161
|
+
agentConfig;
|
|
162
|
+
tools;
|
|
163
|
+
hooks;
|
|
164
|
+
onMessageHandler;
|
|
165
|
+
systemPromptSource;
|
|
166
|
+
memoryStore;
|
|
167
|
+
tasks = /* @__PURE__ */ new Map();
|
|
168
|
+
app;
|
|
169
|
+
agentCard;
|
|
170
|
+
constructor(cfg) {
|
|
171
|
+
this.tools = cfg.tools ?? [];
|
|
172
|
+
this.hooks = cfg.hooks ?? [];
|
|
173
|
+
this.onMessageHandler = cfg.onMessage;
|
|
174
|
+
this.systemPromptSource = cfg.systemPrompt;
|
|
175
|
+
const rawYaml = fs.readFileSync(path.resolve(process.cwd(), cfg.config), "utf-8");
|
|
176
|
+
this.agentConfig = parseAgentConfig(rawYaml);
|
|
177
|
+
const policyHash = this.hooks.length > 0 ? this.hooks[0].getPolicyHash() : void 0;
|
|
178
|
+
this.agentCard = compileToAgentCard(this.agentConfig, policyHash);
|
|
179
|
+
this.memoryStore = this.agentConfig.memory?.type === "conversation-buffer" ? new ConversationBufferMemory(this.agentConfig.memory.max_messages) : new ConversationBufferMemory();
|
|
180
|
+
this.app = new Hono();
|
|
181
|
+
this.setupRoutes();
|
|
182
|
+
}
|
|
183
|
+
// ─── Routes ───────────────────────────────────────────────────────────────
|
|
184
|
+
setupRoutes() {
|
|
185
|
+
this.app.get("/.well-known/agent.json", (c) => c.json(this.agentCard));
|
|
186
|
+
this.app.get("/a2a/tasks/:taskId", (c) => {
|
|
187
|
+
const task = this.tasks.get(c.req.param("taskId"));
|
|
188
|
+
return task ? c.json(task) : c.json({ error: "Task not found" }, 404);
|
|
189
|
+
});
|
|
190
|
+
this.app.post("/a2a/tasks/:taskId/cancel", (c) => {
|
|
191
|
+
const task = this.tasks.get(c.req.param("taskId"));
|
|
192
|
+
if (!task) return c.json({ error: "Task not found" }, 404);
|
|
193
|
+
if (task.state === "WORKING" || task.state === "PENDING") {
|
|
194
|
+
task.state = "CANCELLED";
|
|
195
|
+
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
196
|
+
}
|
|
197
|
+
return c.json(task);
|
|
198
|
+
});
|
|
199
|
+
this.app.post("/a2a/message", async (c) => {
|
|
200
|
+
const body = await c.req.json();
|
|
201
|
+
const isStream = body.stream === true;
|
|
202
|
+
const inboundMessage = body.message;
|
|
203
|
+
const taskId = crypto.randomUUID();
|
|
204
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
205
|
+
const task = { taskId, state: "PENDING", createdAt: now, updatedAt: now };
|
|
206
|
+
this.tasks.set(taskId, task);
|
|
207
|
+
for (const hook of this.hooks) {
|
|
208
|
+
const result = await hook.checkIngress(inboundMessage.content);
|
|
209
|
+
if (!result.allowed) {
|
|
210
|
+
task.state = "FAILED";
|
|
211
|
+
task.error = result.reason;
|
|
212
|
+
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
213
|
+
return c.json({ taskId, error: "Security Policy Violation", reason: result.reason }, 403);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
await this.memoryStore.store(inboundMessage);
|
|
217
|
+
task.state = "WORKING";
|
|
218
|
+
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
219
|
+
if (isStream) {
|
|
220
|
+
return streamSSE(c, async (stream) => {
|
|
221
|
+
const combinedTools = await this.resolveTools();
|
|
222
|
+
const write = async (chunk) => {
|
|
223
|
+
let out = chunk;
|
|
224
|
+
for (const hook of this.hooks) {
|
|
225
|
+
const res = await hook.checkEgress(out);
|
|
226
|
+
if (res.allowed && res.modifiedPayload !== void 0) out = res.modifiedPayload;
|
|
227
|
+
}
|
|
228
|
+
await stream.writeSSE({ data: JSON.stringify({ delta: out, taskId }) });
|
|
229
|
+
};
|
|
230
|
+
try {
|
|
231
|
+
if (this.onMessageHandler) {
|
|
232
|
+
await this.onMessageHandler(inboundMessage, this.buildContext(combinedTools, write));
|
|
233
|
+
} else {
|
|
234
|
+
await this.defaultStreamHandler(inboundMessage, combinedTools, write);
|
|
235
|
+
}
|
|
236
|
+
task.state = "COMPLETED";
|
|
237
|
+
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
238
|
+
await stream.writeSSE({ data: JSON.stringify({ done: true, taskId }) });
|
|
239
|
+
} catch (err) {
|
|
240
|
+
task.state = "FAILED";
|
|
241
|
+
task.error = err instanceof Error ? err.message : String(err);
|
|
242
|
+
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
243
|
+
await stream.writeSSE({ data: JSON.stringify({ error: task.error, taskId }) });
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
try {
|
|
248
|
+
const combinedTools = await this.resolveTools();
|
|
249
|
+
const history = await this.memoryStore.query();
|
|
250
|
+
const model = resolveModel(this.agentConfig);
|
|
251
|
+
const sdkTools = combinedTools.length > 0 ? toAISDKTools(combinedTools) : void 0;
|
|
252
|
+
const result = await generateText({
|
|
253
|
+
model,
|
|
254
|
+
system: this.resolveSystemPrompt(),
|
|
255
|
+
messages: history.map((m) => ({ role: m.role, content: m.content })),
|
|
256
|
+
tools: sdkTools,
|
|
257
|
+
maxSteps: 5
|
|
258
|
+
});
|
|
259
|
+
let out = result.text;
|
|
260
|
+
for (const hook of this.hooks) {
|
|
261
|
+
const res = await hook.checkEgress(out);
|
|
262
|
+
if (res.allowed && res.modifiedPayload !== void 0) out = res.modifiedPayload;
|
|
263
|
+
}
|
|
264
|
+
await this.memoryStore.store({ role: "assistant", content: out });
|
|
265
|
+
task.state = "COMPLETED";
|
|
266
|
+
task.result = out;
|
|
267
|
+
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
268
|
+
return c.json({ taskId, state: "COMPLETED", result: out });
|
|
269
|
+
} catch (err) {
|
|
270
|
+
task.state = "FAILED";
|
|
271
|
+
task.error = err instanceof Error ? err.message : String(err);
|
|
272
|
+
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
273
|
+
return c.json({ taskId, state: "FAILED", error: task.error }, 500);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────
|
|
278
|
+
resolveSystemPrompt(metadata) {
|
|
279
|
+
if (!this.systemPromptSource) {
|
|
280
|
+
return `You are ${this.agentCard.name}. ${this.agentCard.description}`;
|
|
281
|
+
}
|
|
282
|
+
return typeof this.systemPromptSource === "function" ? this.systemPromptSource({ metadata }) : this.systemPromptSource;
|
|
283
|
+
}
|
|
284
|
+
async resolveTools() {
|
|
285
|
+
const dynamic = [];
|
|
286
|
+
for (const t of this.agentCard.tools ?? []) {
|
|
287
|
+
if (t.mcp_server) {
|
|
288
|
+
try {
|
|
289
|
+
const client = new MCPClient(t.mcp_server, []);
|
|
290
|
+
const fetched = await client.fetchTools();
|
|
291
|
+
dynamic.push(...fetched);
|
|
292
|
+
} catch {
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return [...this.tools, ...dynamic];
|
|
297
|
+
}
|
|
298
|
+
buildContext(combinedTools, write) {
|
|
299
|
+
const self = this;
|
|
300
|
+
return {
|
|
301
|
+
llm: {
|
|
302
|
+
async *execute(msg, cfg) {
|
|
303
|
+
const model = resolveModel(self.agentConfig);
|
|
304
|
+
const sdkTools = (cfg.tools ?? []).length > 0 ? toAISDKTools(cfg.tools) : void 0;
|
|
305
|
+
const result = await streamText({
|
|
306
|
+
model,
|
|
307
|
+
system: self.resolveSystemPrompt(),
|
|
308
|
+
prompt: msg.content,
|
|
309
|
+
tools: sdkTools,
|
|
310
|
+
maxSteps: 5
|
|
311
|
+
});
|
|
312
|
+
for await (const chunk of result.textStream) {
|
|
313
|
+
yield chunk;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
tools: combinedTools,
|
|
318
|
+
stream: { write },
|
|
319
|
+
delegate: self.delegate.bind(self),
|
|
320
|
+
memory: self.memoryStore
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
async defaultStreamHandler(msg, tools, write) {
|
|
324
|
+
const model = resolveModel(this.agentConfig);
|
|
325
|
+
const history = await this.memoryStore.query();
|
|
326
|
+
const sdkTools = tools.length > 0 ? toAISDKTools(tools) : void 0;
|
|
327
|
+
const result = await streamText({
|
|
328
|
+
model,
|
|
329
|
+
system: this.resolveSystemPrompt(),
|
|
330
|
+
messages: history.map((m) => ({ role: m.role, content: m.content })),
|
|
331
|
+
tools: sdkTools,
|
|
332
|
+
maxSteps: 5
|
|
333
|
+
});
|
|
334
|
+
let fullReply = "";
|
|
335
|
+
for await (const chunk of result.textStream) {
|
|
336
|
+
fullReply += chunk;
|
|
337
|
+
await write(chunk);
|
|
338
|
+
}
|
|
339
|
+
await this.memoryStore.store({ role: "assistant", content: fullReply });
|
|
340
|
+
}
|
|
341
|
+
async delegate(subAgentName, task) {
|
|
342
|
+
const subAgent = this.agentCard.sub_agents?.find((sa) => sa.name === subAgentName);
|
|
343
|
+
if (!subAgent) throw new Error(`Sub-agent "${subAgentName}" not declared in neuro.agent.yaml`);
|
|
344
|
+
const response = await fetch(`${subAgent.endpoint}/a2a/message`, {
|
|
345
|
+
method: "POST",
|
|
346
|
+
headers: { "Content-Type": "application/json" },
|
|
347
|
+
body: JSON.stringify({ stream: false, message: { role: "user", content: task } })
|
|
348
|
+
});
|
|
349
|
+
if (!response.ok) throw new Error(`Delegation to "${subAgentName}" failed: ${response.statusText}`);
|
|
350
|
+
const data = await response.json();
|
|
351
|
+
return data.result ?? JSON.stringify(data);
|
|
352
|
+
}
|
|
353
|
+
// ─── Public ───────────────────────────────────────────────────────────────
|
|
354
|
+
start(port = 8080) {
|
|
355
|
+
console.log(`[Neuro] ${this.agentCard.name} v${this.agentCard.version} \u2192 http://localhost:${port}`);
|
|
356
|
+
console.log(`[Neuro] AgentCard \u2192 http://localhost:${port}/.well-known/agent.json`);
|
|
357
|
+
return serve({ fetch: this.app.fetch, port });
|
|
358
|
+
}
|
|
359
|
+
getApp() {
|
|
360
|
+
return this.app;
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
function createAgent(config) {
|
|
364
|
+
return new NeuroAgent(config);
|
|
365
|
+
}
|
|
366
|
+
export {
|
|
367
|
+
ConversationBufferMemory,
|
|
368
|
+
LocalSemanticMemory,
|
|
369
|
+
MCPClient,
|
|
370
|
+
NeuroAgent,
|
|
371
|
+
PraesidiaHooks,
|
|
372
|
+
Tool,
|
|
373
|
+
clearRuntime,
|
|
374
|
+
compileToAgentCard,
|
|
375
|
+
createAgent,
|
|
376
|
+
createAgentServer,
|
|
377
|
+
getAgentUrl,
|
|
378
|
+
loadRuntime,
|
|
379
|
+
neuroAgentSchema,
|
|
380
|
+
parseAgentConfig,
|
|
381
|
+
parsePolicy,
|
|
382
|
+
pingAgent,
|
|
383
|
+
saveRuntime,
|
|
384
|
+
sendMessage
|
|
385
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|