@hilbras/omninode 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -39,6 +39,11 @@ var ProtocolError = class extends OmniNodeError {
39
39
  super("PROTOCOL_VIOLATION", message, options);
40
40
  }
41
41
  };
42
+ var PlannerError = class extends OmniNodeError {
43
+ constructor(code, message, options = {}) {
44
+ super(code, message, options);
45
+ }
46
+ };
42
47
  var MemoryError = class extends OmniNodeError {
43
48
  constructor(message, options = {}) {
44
49
  super("MEMORY_UNAVAILABLE", message, options);
@@ -154,13 +159,23 @@ var pipelineConfigSchema = z.object({
154
159
  role: z.string().optional(),
155
160
  steps: z.array(pipelineStepConfigSchema).min(1)
156
161
  }).strict();
162
+ var plannerConfigSchema = z.object({
163
+ kind: z.enum(["model", "heuristic"]).default("model"),
164
+ model: z.string().optional(),
165
+ instruction: z.string().optional()
166
+ }).strict();
157
167
  var projectConfigSchema = z.object({
158
168
  name: z.string().min(1),
159
169
  providers: z.array(providerConfigSchema).default([]),
160
170
  agents: z.array(agentConfigSchema).default([]),
161
171
  roles: z.array(roleConfigSchema).default([]),
162
172
  pipelines: z.array(pipelineConfigSchema).default([]),
163
- memory: z.object({ provider: z.string().min(1) }).strict().optional()
173
+ planner: plannerConfigSchema.optional(),
174
+ memory: z.object({
175
+ provider: z.string().min(1).default("local"),
176
+ base_url: z.string().url().optional(),
177
+ api_key_env_var: z.string().min(1).optional()
178
+ }).strict().optional()
164
179
  }).strict();
165
180
  var appConfigSchema = z.object({
166
181
  project: projectConfigSchema,
@@ -230,7 +245,20 @@ function toAppConfig(yaml) {
230
245
  agents: yaml.project.agents.map(toAgentConfig),
231
246
  roles: yaml.project.roles.map(toRoleDefinition),
232
247
  pipelines: yaml.project.pipelines.map(toPipelineDefinition),
233
- ...yaml.project.memory !== void 0 ? { memory: yaml.project.memory } : {}
248
+ ...yaml.project.planner !== void 0 ? {
249
+ planner: {
250
+ kind: yaml.project.planner.kind,
251
+ model: yaml.project.planner.model,
252
+ instruction: yaml.project.planner.instruction
253
+ }
254
+ } : {},
255
+ ...yaml.project.memory !== void 0 ? {
256
+ memory: {
257
+ provider: yaml.project.memory.provider,
258
+ baseUrl: yaml.project.memory.base_url,
259
+ apiKeyEnvVar: yaml.project.memory.api_key_env_var
260
+ }
261
+ } : {}
234
262
  },
235
263
  ...yaml.logging !== void 0 ? { logging: yaml.logging } : {}
236
264
  };
@@ -1079,13 +1107,24 @@ var TaskEngine = class {
1079
1107
  const role = task.role ? this.options.roles.get(task.role) : void 0;
1080
1108
  const running = await this.transition(task, "running");
1081
1109
  this.log.info(`Running task ${task.id} on agent "${task.agent}".`);
1110
+ let background = running.context?.background;
1111
+ if (this.options.memory) {
1112
+ const memoryContext = await this.options.memory.gatherContext({
1113
+ objective: running.objective,
1114
+ ...running.role ? { role: running.role } : {},
1115
+ ...running.project ? { project: running.project } : {}
1116
+ });
1117
+ if (memoryContext.length > 0) {
1118
+ background = [memoryContext, background].filter((part) => part && part.length > 0).join("\n\n");
1119
+ }
1120
+ }
1082
1121
  let output;
1083
1122
  try {
1084
1123
  output = await agent.run({
1085
1124
  taskId: running.id,
1086
1125
  objective: running.objective,
1087
1126
  ...role ? { role } : {},
1088
- ...running.context?.background ? { context: running.context.background } : {}
1127
+ ...background !== void 0 ? { context: background } : {}
1089
1128
  });
1090
1129
  } catch (error) {
1091
1130
  const message = error instanceof Error ? error.message : String(error);
@@ -1103,8 +1142,12 @@ var TaskEngine = class {
1103
1142
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
1104
1143
  };
1105
1144
  const finalStatus = output.status === "completed" ? "completed" : "failed";
1145
+ const finished = await this.transition(running, finalStatus, { result });
1106
1146
  this.log.info(`Task ${task.id} finished: ${finalStatus}.`);
1107
- return this.transition(running, finalStatus, { result });
1147
+ if (this.options.memory) {
1148
+ await this.options.memory.recordTaskOutcome(finished);
1149
+ }
1150
+ return finished;
1108
1151
  }
1109
1152
  async cancel(id) {
1110
1153
  const task = await this.mustGet(id);
@@ -1149,18 +1192,536 @@ function generateTaskId() {
1149
1192
  return `task-${Date.now().toString(36)}-${randomBytes(3).toString("hex")}`;
1150
1193
  }
1151
1194
 
1195
+ // src/memory/local.ts
1196
+ import { mkdir as mkdir2, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "fs/promises";
1197
+ import { dirname as dirname2 } from "path";
1198
+ var STOPWORDS = /* @__PURE__ */ new Set([
1199
+ "the",
1200
+ "and",
1201
+ "for",
1202
+ "with",
1203
+ "this",
1204
+ "that",
1205
+ "from",
1206
+ "into",
1207
+ "was",
1208
+ "were",
1209
+ "have",
1210
+ "has",
1211
+ "had",
1212
+ "are",
1213
+ "but",
1214
+ "not",
1215
+ "you",
1216
+ "his",
1217
+ "her",
1218
+ "its",
1219
+ "their",
1220
+ "them",
1221
+ "then",
1222
+ "than",
1223
+ "when",
1224
+ "what",
1225
+ "which",
1226
+ "will",
1227
+ "would",
1228
+ "been",
1229
+ "were",
1230
+ "also",
1231
+ "because",
1232
+ "about",
1233
+ "after",
1234
+ "before"
1235
+ ]);
1236
+ function keywords(text) {
1237
+ return new Set(
1238
+ (text ?? "").toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((t) => t.length >= 4 && !STOPWORDS.has(t))
1239
+ );
1240
+ }
1241
+ function relevanceScore(queryKeywords, entry) {
1242
+ if (queryKeywords.size === 0) return 0;
1243
+ const entryKeywords = keywords(`${entry.content} ${entry.key}`);
1244
+ let overlap = 0;
1245
+ for (const keyword of queryKeywords) {
1246
+ if (entryKeywords.has(keyword)) overlap += 1;
1247
+ }
1248
+ let tagMatches = 0;
1249
+ for (const tag of entry.tags ?? []) {
1250
+ if (queryKeywords.has(tag.toLowerCase())) tagMatches += 1;
1251
+ }
1252
+ return overlap * 2 + tagMatches * 1.5;
1253
+ }
1254
+ var LocalMemoryProvider = class {
1255
+ name = "local";
1256
+ filePath;
1257
+ queue = Promise.resolve();
1258
+ constructor(directory = `${process.cwd()}/.omninode`) {
1259
+ this.filePath = `${directory}/memory.json`;
1260
+ }
1261
+ async query(query) {
1262
+ return this.synchronized(async () => {
1263
+ const entries = await this.load();
1264
+ const filtered = entries.filter((entry) => query.scope ? entry.scope === query.scope : true).filter(
1265
+ (entry) => query.tags && query.tags.length > 0 ? query.tags.every((tag) => (entry.tags ?? []).includes(tag)) : true
1266
+ );
1267
+ if (!query.text || query.text.trim().length === 0) {
1268
+ return filtered.sort((a, b) => (b.updatedAt ?? b.createdAt ?? "").localeCompare(a.updatedAt ?? a.createdAt ?? "")).slice(0, query.limit ?? 10);
1269
+ }
1270
+ const queryKeywords = keywords(query.text);
1271
+ return filtered.map((entry) => ({ entry, score: relevanceScore(queryKeywords, entry) })).filter(({ score }) => score > 0).sort((a, b) => b.score - a.score).slice(0, query.limit ?? 10).map(({ entry }) => entry);
1272
+ });
1273
+ }
1274
+ async write(entry) {
1275
+ return this.synchronized(async () => {
1276
+ const entries = await this.load();
1277
+ const index = entries.findIndex((e) => e.key === entry.key);
1278
+ const stamped = {
1279
+ ...entry,
1280
+ createdAt: entry.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1281
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1282
+ };
1283
+ if (index >= 0) entries[index] = stamped;
1284
+ else entries.push(stamped);
1285
+ await this.saveAll(entries);
1286
+ });
1287
+ }
1288
+ async count() {
1289
+ return (await this.load()).length;
1290
+ }
1291
+ synchronized(operation) {
1292
+ const result = this.queue.then(operation, operation);
1293
+ this.queue = result.then(
1294
+ () => void 0,
1295
+ () => void 0
1296
+ );
1297
+ return result;
1298
+ }
1299
+ async load() {
1300
+ try {
1301
+ const raw = await readFile2(this.filePath, "utf8");
1302
+ const parsed = JSON.parse(raw);
1303
+ return Array.isArray(parsed) ? parsed : [];
1304
+ } catch {
1305
+ return [];
1306
+ }
1307
+ }
1308
+ async saveAll(entries) {
1309
+ const tmp = `${this.filePath}.tmp`;
1310
+ await mkdir2(dirname2(this.filePath), { recursive: true });
1311
+ await writeFile2(tmp, `${JSON.stringify(entries, null, 2)}
1312
+ `, "utf8");
1313
+ await rename2(tmp, this.filePath);
1314
+ }
1315
+ };
1316
+
1317
+ // src/memory/remembera.ts
1318
+ var RememberaMemoryProvider = class {
1319
+ name = "remembera";
1320
+ baseUrl;
1321
+ apiKeyEnvVar;
1322
+ env;
1323
+ log;
1324
+ constructor(config, log = logger) {
1325
+ this.baseUrl = config.baseUrl.replace(/\/+$/, "");
1326
+ this.apiKeyEnvVar = config.apiKeyEnvVar;
1327
+ this.env = config.env ?? process.env;
1328
+ this.log = log.child({ component: "remembera" });
1329
+ }
1330
+ headers() {
1331
+ const key = this.apiKeyEnvVar ? resolveApiKey({ name: "remembera", apiKeyEnvVar: this.apiKeyEnvVar }, this.env) : void 0;
1332
+ return key ? { authorization: `Bearer ${key}` } : {};
1333
+ }
1334
+ async query(query) {
1335
+ const { status, body } = await this.request(`${this.baseUrl}/api/memory/query`, query);
1336
+ if (status !== 200) {
1337
+ throw new MemoryError(`Remembera query returned HTTP ${status}.`);
1338
+ }
1339
+ const entries = body.entries;
1340
+ if (!Array.isArray(entries)) {
1341
+ throw new MemoryError("Remembera query response had no entries array.");
1342
+ }
1343
+ return entries.map((entry) => this.toEntry(entry)).filter((entry) => entry !== void 0);
1344
+ }
1345
+ async write(entry) {
1346
+ const { status } = await this.request(`${this.baseUrl}/api/memory`, entry);
1347
+ if (status !== 200 && status !== 201) {
1348
+ throw new MemoryError(`Remembera write returned HTTP ${status}.`);
1349
+ }
1350
+ }
1351
+ async request(url, body) {
1352
+ const headers = this.headers();
1353
+ try {
1354
+ return await requestJson(url, {
1355
+ method: "POST",
1356
+ headers,
1357
+ body
1358
+ });
1359
+ } catch (error) {
1360
+ const message = error instanceof Error ? error.message : String(error);
1361
+ throw new MemoryError(`Remembera request to ${url} failed: ${message}`, { cause: error });
1362
+ }
1363
+ }
1364
+ toEntry(raw) {
1365
+ if (raw === null || typeof raw !== "object") {
1366
+ this.log.warn("Skipping malformed Remembera memory entry.");
1367
+ return void 0;
1368
+ }
1369
+ const entry = raw;
1370
+ if (typeof entry.key !== "string" || typeof entry.content !== "string") {
1371
+ this.log.warn("Skipping Remembera memory entry without key/content.");
1372
+ return void 0;
1373
+ }
1374
+ const scope = entry.scope;
1375
+ return {
1376
+ key: entry.key,
1377
+ content: entry.content,
1378
+ scope: scope === "project" || scope === "role" || scope === "task" || scope === "knowledge" ? scope : "knowledge",
1379
+ ...Array.isArray(entry.tags) ? { tags: entry.tags.filter((t) => typeof t === "string") } : {},
1380
+ ...typeof entry.createdAt === "string" ? { createdAt: entry.createdAt } : {},
1381
+ ...typeof entry.updatedAt === "string" ? { updatedAt: entry.updatedAt } : {}
1382
+ };
1383
+ }
1384
+ };
1385
+
1386
+ // src/memory/factory.ts
1387
+ function createMemoryProvider(config) {
1388
+ if (!config || config.provider === "local") {
1389
+ return new LocalMemoryProvider();
1390
+ }
1391
+ if (config.provider === "remembera") {
1392
+ if (!config.baseUrl) {
1393
+ throw new ConfigError(
1394
+ "CONFIG_INVALID",
1395
+ `Memory provider "remembera" needs a base_url in omninode.yaml.`
1396
+ );
1397
+ }
1398
+ return new RememberaMemoryProvider({
1399
+ baseUrl: config.baseUrl,
1400
+ apiKeyEnvVar: config.apiKeyEnvVar
1401
+ });
1402
+ }
1403
+ throw new ConfigError(
1404
+ "CONFIG_INVALID",
1405
+ `Unknown memory provider "${config.provider}". Available: local, remembera.`
1406
+ );
1407
+ }
1408
+
1409
+ // src/memory/service.ts
1410
+ var MAX_CONTEXT_CHARS = 4e3;
1411
+ var MemoryService = class {
1412
+ constructor(provider, log = logger) {
1413
+ this.provider = provider;
1414
+ this.log = log.child({ component: "memory", provider: provider.name });
1415
+ }
1416
+ provider;
1417
+ log;
1418
+ get providerRef() {
1419
+ return this.provider;
1420
+ }
1421
+ get providerName() {
1422
+ return this.provider.name;
1423
+ }
1424
+ /** Relevant context for a task objective; empty string when nothing matches. */
1425
+ async gatherContext(query) {
1426
+ try {
1427
+ const entries = await this.provider.query({
1428
+ text: query.objective,
1429
+ ...query.role ? { tags: [query.role] } : {},
1430
+ limit: 8
1431
+ });
1432
+ if (entries.length === 0) return "";
1433
+ const byScope = /* @__PURE__ */ new Map();
1434
+ for (const entry of entries) {
1435
+ byScope.set(entry.scope, [...byScope.get(entry.scope) ?? [], entry]);
1436
+ }
1437
+ const parts = [];
1438
+ for (const scope of ["project", "task", "role", "knowledge"]) {
1439
+ const scopeEntries = byScope.get(scope) ?? [];
1440
+ if (scopeEntries.length === 0) continue;
1441
+ const lines = scopeEntries.map((entry) => `- ${entry.content}`).join("\n");
1442
+ parts.push(`### Relevant memory (${scope})
1443
+ ${lines}`);
1444
+ }
1445
+ const context = parts.join("\n\n");
1446
+ return context.length > MAX_CONTEXT_CHARS ? `${context.slice(0, MAX_CONTEXT_CHARS)}\u2026` : context;
1447
+ } catch (error) {
1448
+ this.log.warn(
1449
+ `Memory query failed (continuing without memory): ${error instanceof Error ? error.message : String(error)}`
1450
+ );
1451
+ return "";
1452
+ }
1453
+ }
1454
+ /** Records a finished task's outcome — best-effort. */
1455
+ async recordTaskOutcome(task) {
1456
+ const outcome = task.result?.summary ?? task.result?.error ?? "(no output)";
1457
+ try {
1458
+ await this.provider.write({
1459
+ key: `task:${task.id}`,
1460
+ scope: "task",
1461
+ content: `Objective: ${task.objective}
1462
+ Status: ${task.status}
1463
+ Outcome: ${outcome}`,
1464
+ tags: ["task", ...task.agent ? [task.agent] : [], ...task.role ? [task.role] : []],
1465
+ createdAt: task.createdAt
1466
+ });
1467
+ const significant = (task.result?.reports ?? []).flatMap(
1468
+ (report) => report.findings.filter((finding) => finding.severity === "high" || finding.severity === "critical").map((finding) => ({ report, finding }))
1469
+ );
1470
+ for (const { report, finding } of significant) {
1471
+ await this.provider.write({
1472
+ key: `finding:${report.id}:${finding.id}`,
1473
+ scope: "project",
1474
+ content: `[${finding.severity}] ${finding.title} \u2014 ${finding.detail}`,
1475
+ tags: ["known-problem", report.agent]
1476
+ });
1477
+ }
1478
+ } catch (error) {
1479
+ this.log.warn(
1480
+ `Memory write failed (continuing): ${error instanceof Error ? error.message : String(error)}`
1481
+ );
1482
+ }
1483
+ }
1484
+ };
1485
+
1152
1486
  // src/tasks/engine-factory.ts
1153
1487
  function createTaskEngine(config, log = logger, directory) {
1488
+ const agents = buildAgentRegistry(config, log);
1489
+ const roles = buildRoleRegistry(config);
1154
1490
  return new TaskEngine({
1155
- agents: buildAgentRegistry(config, log),
1156
- roles: buildRoleRegistry(config),
1491
+ agents,
1492
+ roles,
1157
1493
  store: new FileTaskStore(directory),
1494
+ ...config.project.memory !== void 0 ? { memory: new MemoryService(createMemoryProvider(config.project.memory), log) } : {},
1158
1495
  log
1159
1496
  });
1160
1497
  }
1161
1498
 
1162
1499
  // src/pipelines/engine.ts
1500
+ import { randomBytes as randomBytes4 } from "crypto";
1501
+
1502
+ // src/planner/heuristic.ts
1163
1503
  import { randomBytes as randomBytes2 } from "crypto";
1504
+ var SEVERITY_RANK = {
1505
+ info: 0,
1506
+ low: 1,
1507
+ medium: 2,
1508
+ high: 3,
1509
+ critical: 4
1510
+ };
1511
+ var HeuristicPlanner = class {
1512
+ name = "heuristic";
1513
+ async plan(request) {
1514
+ const steps = [];
1515
+ const findings = request.reports.flatMap(
1516
+ (report) => report.findings.map((finding) => ({ finding, agent: report.agent }))
1517
+ );
1518
+ const recommendations = dedupe(request.reports.flatMap((r) => r.recommendations));
1519
+ for (const [index, text] of recommendations.entries()) {
1520
+ steps.push({
1521
+ id: `step-${steps.length + 1}`,
1522
+ title: truncate(text, 120),
1523
+ order: steps.length + 1,
1524
+ ...index > 0 ? { dependsOn: [`step-${steps.length}`] } : {}
1525
+ });
1526
+ }
1527
+ const significant = findings.filter(({ finding }) => finding.severity ? SEVERITY_RANK[finding.severity] >= SEVERITY_RANK.high : false).sort((a, b) => SEVERITY_RANK[b.finding.severity ?? "info"] - SEVERITY_RANK[a.finding.severity ?? "info"]);
1528
+ for (const { finding } of significant) {
1529
+ steps.push(remediationStep(finding, steps.length + 1));
1530
+ }
1531
+ if (steps.length === 0) {
1532
+ steps.push({
1533
+ id: "step-1",
1534
+ title: "Gather additional information",
1535
+ description: "The research reports contained no actionable recommendations or significant findings. Investigate the objective further before planning implementation work.",
1536
+ order: 1
1537
+ });
1538
+ }
1539
+ const risks = significant.filter(({ finding }) => finding.severity === "critical").map(({ finding }) => `Critical finding unaddressed until its step completes: ${finding.title}`);
1540
+ return {
1541
+ id: `plan-${Date.now().toString(36)}-${randomBytes2(3).toString("hex")}`,
1542
+ objective: request.objective,
1543
+ summary: `Derived from ${request.reports.length} report(s): ${recommendations.length} recommendation step(s), ${significant.length} remediation step(s).`,
1544
+ steps,
1545
+ ...risks.length > 0 ? { risks } : {},
1546
+ generatedBy: "heuristic",
1547
+ ...request.taskId !== void 0 ? { taskId: request.taskId } : {},
1548
+ ...request.pipelineRunId !== void 0 ? { pipelineRunId: request.pipelineRunId } : {},
1549
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1550
+ };
1551
+ }
1552
+ };
1553
+ function remediationStep(finding, order) {
1554
+ return {
1555
+ id: `step-${order}`,
1556
+ title: `Fix: ${truncate(finding.title, 100)}`,
1557
+ description: finding.detail,
1558
+ order,
1559
+ dependsOn: order > 1 ? [`step-${order - 1}`] : [],
1560
+ sourceFindings: [finding.id]
1561
+ };
1562
+ }
1563
+ function dedupe(texts) {
1564
+ return [...new Set(texts.map((t) => t.trim()).filter((t) => t.length > 0))];
1565
+ }
1566
+ function truncate(text, max) {
1567
+ return text.length > max ? `${text.slice(0, max - 1)}\u2026` : text;
1568
+ }
1569
+
1570
+ // src/planner/model.ts
1571
+ import { randomBytes as randomBytes3 } from "crypto";
1572
+ import { z as z2 } from "zod";
1573
+
1574
+ // src/planner/context.ts
1575
+ function buildPlannerContext(request) {
1576
+ const parts = [`# Objective
1577
+ ${request.objective}`];
1578
+ if (request.role) {
1579
+ const roleLines = [`# Role
1580
+ ${request.role.name} (${request.role.id})`];
1581
+ if (request.role.responsibilities.length > 0) {
1582
+ roleLines.push("Responsibilities:", ...request.role.responsibilities.map((r) => `- ${r}`));
1583
+ }
1584
+ if (request.role.expectedOutputs.length > 0) {
1585
+ roleLines.push("Expected outputs:", ...request.role.expectedOutputs.map((o) => `- ${o}`));
1586
+ }
1587
+ parts.push(roleLines.join("\n"));
1588
+ }
1589
+ if (request.reports.length > 0) {
1590
+ const agents = [...new Set(request.reports.map((r) => r.agent))].join(", ");
1591
+ const findingLines = request.reports.flatMap(
1592
+ (report) => report.findings.map(
1593
+ (finding) => `- [${finding.severity ?? "info"}] ${finding.title} (source: ${report.agent}, finding: ${finding.id})` + (finding.detail ? ` \u2014 ${finding.detail}` : "")
1594
+ )
1595
+ );
1596
+ parts.push(
1597
+ `# Findings (${request.reports.length} report(s) by ${agents})
1598
+ ` + (findingLines.length > 0 ? findingLines.join("\n") : "(no findings reported)")
1599
+ );
1600
+ const recommendationLines = request.reports.flatMap(
1601
+ (report) => report.recommendations.map((text) => `- ${text} (source: ${report.agent})`)
1602
+ );
1603
+ if (recommendationLines.length > 0) {
1604
+ parts.push(`# Recommendations
1605
+ ${recommendationLines.join("\n")}`);
1606
+ }
1607
+ const summaryLines = request.reports.map((report) => `- [${report.agent}] ${report.summary}`);
1608
+ parts.push(`# Report summaries
1609
+ ${summaryLines.join("\n")}`);
1610
+ }
1611
+ if (request.memory && request.memory.length > 0) {
1612
+ parts.push(
1613
+ `# Relevant memory
1614
+ ` + request.memory.map((entry) => `- [${entry.scope}] ${entry.content}`).join("\n")
1615
+ );
1616
+ }
1617
+ if (request.context && request.context.length > 0) {
1618
+ parts.push(`# Research context
1619
+ ${request.context}`);
1620
+ }
1621
+ return parts.join("\n\n");
1622
+ }
1623
+
1624
+ // src/planner/model.ts
1625
+ var planStepJsonSchema = z2.object({
1626
+ id: z2.string().optional(),
1627
+ title: z2.string().min(1),
1628
+ description: z2.string().optional(),
1629
+ order: z2.number().int().optional(),
1630
+ targets: z2.array(z2.string()).optional(),
1631
+ acceptance_criteria: z2.array(z2.string()).optional(),
1632
+ depends_on: z2.array(z2.string()).optional(),
1633
+ source_findings: z2.array(z2.string()).optional()
1634
+ }).strict();
1635
+ var planJsonSchema = z2.object({
1636
+ summary: z2.string().min(1),
1637
+ steps: z2.array(planStepJsonSchema).min(1),
1638
+ risks: z2.array(z2.string()).optional(),
1639
+ notes: z2.array(z2.string()).optional()
1640
+ }).strict();
1641
+ var ModelPlanner = class {
1642
+ constructor(options) {
1643
+ this.options = options;
1644
+ this.log = (options.log ?? logger).child({ component: "planner", model: options.model });
1645
+ }
1646
+ options;
1647
+ name = "model";
1648
+ log;
1649
+ async plan(request) {
1650
+ const context = buildPlannerContext(request);
1651
+ const messages = [
1652
+ {
1653
+ role: "system",
1654
+ content: 'You are the OmniNode planner. Given the objective and the combined research context, produce a final analysis and a prioritized, actionable implementation plan. Respond with JSON ONLY (no prose, no code fences) matching exactly this shape: {"summary": string, "steps": [{"id": string, "title": string, "description": string, "order": number, "targets": string[], "acceptance_criteria": string[], "source_findings": string[]}], "risks": string[], "notes": string[]}. ' + (this.options.instruction ?? "")
1655
+ },
1656
+ { role: "user", content: context }
1657
+ ];
1658
+ let responseText;
1659
+ try {
1660
+ responseText = await this.options.chat(this.options.model, messages);
1661
+ } catch (error) {
1662
+ return this.degrade(request, `planner model call failed: ${describe(error)}`);
1663
+ }
1664
+ const parsed = parsePlanJson(responseText);
1665
+ if (!parsed) {
1666
+ return this.degrade(request, "planner model output was not valid plan JSON");
1667
+ }
1668
+ const plan = {
1669
+ id: `plan-${Date.now().toString(36)}-${randomBytes3(3).toString("hex")}`,
1670
+ objective: request.objective,
1671
+ summary: parsed.summary,
1672
+ steps: parsed.steps,
1673
+ ...parsed.risks !== void 0 ? { risks: parsed.risks } : {},
1674
+ ...parsed.notes !== void 0 ? { notes: parsed.notes } : {},
1675
+ generatedBy: `model:${this.options.model}`,
1676
+ model: this.options.model,
1677
+ ...request.taskId !== void 0 ? { taskId: request.taskId } : {},
1678
+ ...request.pipelineRunId !== void 0 ? { pipelineRunId: request.pipelineRunId } : {},
1679
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1680
+ };
1681
+ return plan;
1682
+ }
1683
+ async degrade(request, reason) {
1684
+ if (this.options.fallback) {
1685
+ this.log.warn(`${reason} \u2014 using the heuristic fallback planner.`);
1686
+ return this.options.fallback.plan(request);
1687
+ }
1688
+ throw new PlannerError("PLANNER_FAILED", `Planner failed: ${reason}.`);
1689
+ }
1690
+ };
1691
+ function parsePlanJson(text) {
1692
+ const unfenced = text.replace(/```(?:json)?/gi, "");
1693
+ const start = unfenced.indexOf("{");
1694
+ const end = unfenced.lastIndexOf("}");
1695
+ if (start < 0 || end <= start) return void 0;
1696
+ try {
1697
+ const json = JSON.parse(unfenced.slice(start, end + 1));
1698
+ const parsed = planJsonSchema.safeParse(json);
1699
+ if (!parsed.success) return void 0;
1700
+ const steps = parsed.data.steps.map((step, index) => ({
1701
+ id: step.id ?? `step-${index + 1}`,
1702
+ title: step.title,
1703
+ description: step.description,
1704
+ order: step.order ?? index + 1,
1705
+ targets: step.targets,
1706
+ acceptanceCriteria: step.acceptance_criteria,
1707
+ dependsOn: step.depends_on,
1708
+ sourceFindings: step.source_findings
1709
+ }));
1710
+ return {
1711
+ summary: parsed.data.summary,
1712
+ steps: steps.sort((a, b) => a.order - b.order),
1713
+ risks: parsed.data.risks,
1714
+ notes: parsed.data.notes
1715
+ };
1716
+ } catch {
1717
+ return void 0;
1718
+ }
1719
+ }
1720
+ function describe(error) {
1721
+ return error instanceof Error ? error.message : String(error);
1722
+ }
1723
+
1724
+ // src/pipelines/engine.ts
1164
1725
  var PipelineEngine = class {
1165
1726
  constructor(options) {
1166
1727
  this.options = options;
@@ -1273,7 +1834,7 @@ var PipelineEngine = class {
1273
1834
  }
1274
1835
  const now = (/* @__PURE__ */ new Date()).toISOString();
1275
1836
  const run = {
1276
- id: `run-${Date.now().toString(36)}-${randomBytes2(3).toString("hex")}`,
1837
+ id: `run-${Date.now().toString(36)}-${randomBytes4(3).toString("hex")}`,
1277
1838
  pipelineId: def.id,
1278
1839
  status: "running",
1279
1840
  startedAt: now,
@@ -1301,7 +1862,9 @@ var PipelineEngine = class {
1301
1862
  const ctx = {
1302
1863
  objective,
1303
1864
  ...role ? { role } : {},
1304
- combinedContext: contextParts.join("\n\n")
1865
+ combinedContext: contextParts.join("\n\n"),
1866
+ reports: [...allReports],
1867
+ pipelineRunId: run.id
1305
1868
  };
1306
1869
  return this.executeStep(step, ctx);
1307
1870
  })
@@ -1320,6 +1883,7 @@ ${contribution}`);
1320
1883
  stepId: step.id,
1321
1884
  status: outcome.status === "skipped" ? "cancelled" : outcome.status === "failed" ? "failed" : "completed",
1322
1885
  ...outcome.taskIds.length > 0 ? { taskIds: outcome.taskIds } : {},
1886
+ ...outcome.planId !== void 0 ? { planId: outcome.planId } : {},
1323
1887
  ...outcome.error !== void 0 ? { error: outcome.error } : {}
1324
1888
  };
1325
1889
  run.stepRuns.push(stepRun);
@@ -1330,6 +1894,7 @@ ${contribution}`);
1330
1894
  }
1331
1895
  const failed = [...outcomes.values()].some((o) => o.status === "failed");
1332
1896
  const finalStatus = failed ? "failed" : "completed";
1897
+ const planId = [...outcomes.values()].find((o) => o.planId !== void 0)?.planId;
1333
1898
  let combinedReportId;
1334
1899
  if (this.options.reports) {
1335
1900
  const taskIds = [...outcomes.values()].flatMap((o) => o.taskIds);
@@ -1351,7 +1916,8 @@ ${contribution}`);
1351
1916
  status: finalStatus,
1352
1917
  finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
1353
1918
  ...run.reports !== void 0 && run.reports.length > 0 ? { reports: run.reports } : {},
1354
- ...combinedReportId !== void 0 ? { combinedReportId } : {}
1919
+ ...combinedReportId !== void 0 ? { combinedReportId } : {},
1920
+ ...planId !== void 0 ? { planId } : {}
1355
1921
  };
1356
1922
  await this.options.store.save(finished);
1357
1923
  this.log.info(`Pipeline ${def.id} finished: ${finalStatus}.`);
@@ -1461,33 +2027,34 @@ ${contribution}`);
1461
2027
  };
1462
2028
  }
1463
2029
  async runPlan(step, ctx) {
1464
- if (!step.model) {
1465
- return { status: "completed", summary: "(plan: no model configured \u2014 skipped)", taskIds: [], reports: [] };
1466
- }
1467
- const messages = [
1468
- {
1469
- role: "system",
1470
- content: "You are a planning assistant inside OmniNode. Given the objective and the combined research context, produce a prioritized, actionable plan."
1471
- },
1472
- {
1473
- role: "user",
1474
- content: `# Objective
1475
- ${ctx.objective}
1476
-
1477
- ${ctx.combinedContext}
1478
-
1479
- Produce the final plan.`
1480
- }
1481
- ];
1482
- try {
1483
- const planText = await this.getChat()(step.model, messages);
2030
+ const request = {
2031
+ objective: ctx.objective,
2032
+ reports: ctx.reports,
2033
+ ...ctx.combinedContext.length > 0 ? { context: ctx.combinedContext } : {},
2034
+ ...ctx.role ? { role: ctx.role } : {},
2035
+ ...ctx.pipelineRunId !== void 0 ? { pipelineRunId: ctx.pipelineRunId } : {}
2036
+ };
2037
+ let planner;
2038
+ if (step.model) {
2039
+ planner = new ModelPlanner({
2040
+ model: step.model,
2041
+ chat: this.getChat(),
2042
+ fallback: new HeuristicPlanner(),
2043
+ log: this.log
2044
+ });
2045
+ } else if (this.options.planner) {
2046
+ planner = this.options.planner;
2047
+ } else {
1484
2048
  return {
1485
2049
  status: "completed",
1486
- summary: planText.split("\n", 1)[0] ?? planText,
1487
- contextContribution: planText,
2050
+ summary: "(plan: no model or planner configured \u2014 skipped)",
1488
2051
  taskIds: [],
1489
2052
  reports: []
1490
2053
  };
2054
+ }
2055
+ let plan;
2056
+ try {
2057
+ plan = await planner.plan(request);
1491
2058
  } catch (error) {
1492
2059
  return {
1493
2060
  status: "failed",
@@ -1497,6 +2064,15 @@ Produce the final plan.`
1497
2064
  reports: []
1498
2065
  };
1499
2066
  }
2067
+ await this.options.plans?.save(plan);
2068
+ return {
2069
+ status: "completed",
2070
+ summary: plan.summary,
2071
+ contextContribution: formatPlan(plan),
2072
+ taskIds: [],
2073
+ planId: plan.id,
2074
+ reports: []
2075
+ };
1500
2076
  }
1501
2077
  getChat() {
1502
2078
  if (this.options.chat) return this.options.chat;
@@ -1506,6 +2082,21 @@ Produce the final plan.`
1506
2082
  );
1507
2083
  }
1508
2084
  };
2085
+ function formatPlan(plan) {
2086
+ const lines = [
2087
+ `# Implementation Plan (by ${plan.generatedBy})`,
2088
+ plan.summary,
2089
+ "Steps:",
2090
+ ...plan.steps.map((step) => {
2091
+ const targets = step.targets && step.targets.length > 0 ? ` [targets: ${step.targets.join(", ")}]` : "";
2092
+ return `${step.order}. ${step.title}${targets}`;
2093
+ })
2094
+ ];
2095
+ if (plan.risks && plan.risks.length > 0) {
2096
+ lines.push("Risks:", ...plan.risks.map((risk) => `- ${risk}`));
2097
+ }
2098
+ return lines.join("\n");
2099
+ }
1509
2100
  function createDefaultChatFn(providers) {
1510
2101
  return async (modelRef, messages) => {
1511
2102
  const separator = modelRef.indexOf(":");
@@ -1531,8 +2122,8 @@ function createDefaultChatFn(providers) {
1531
2122
  }
1532
2123
 
1533
2124
  // src/pipelines/store.ts
1534
- import { mkdir as mkdir2, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "fs/promises";
1535
- import { dirname as dirname2 } from "path";
2125
+ import { mkdir as mkdir3, readFile as readFile3, rename as rename3, writeFile as writeFile3 } from "fs/promises";
2126
+ import { dirname as dirname3 } from "path";
1536
2127
  var FilePipelineRunStore = class {
1537
2128
  filePath;
1538
2129
  queue = Promise.resolve();
@@ -1570,7 +2161,7 @@ var FilePipelineRunStore = class {
1570
2161
  }
1571
2162
  async load() {
1572
2163
  try {
1573
- const raw = await readFile2(this.filePath, "utf8");
2164
+ const raw = await readFile3(this.filePath, "utf8");
1574
2165
  const parsed = JSON.parse(raw);
1575
2166
  return Array.isArray(parsed) ? parsed : [];
1576
2167
  } catch {
@@ -1579,16 +2170,93 @@ var FilePipelineRunStore = class {
1579
2170
  }
1580
2171
  async write(runs) {
1581
2172
  const tmp = `${this.filePath}.tmp`;
1582
- await mkdir2(dirname2(this.filePath), { recursive: true });
1583
- await writeFile2(tmp, `${JSON.stringify(runs, null, 2)}
2173
+ await mkdir3(dirname3(this.filePath), { recursive: true });
2174
+ await writeFile3(tmp, `${JSON.stringify(runs, null, 2)}
1584
2175
  `, "utf8");
1585
- await rename2(tmp, this.filePath);
2176
+ await rename3(tmp, this.filePath);
2177
+ }
2178
+ };
2179
+
2180
+ // src/planner/store.ts
2181
+ import { mkdir as mkdir4, readFile as readFile4, rename as rename4, writeFile as writeFile4 } from "fs/promises";
2182
+ import { dirname as dirname4 } from "path";
2183
+ var FilePlanStore = class {
2184
+ filePath;
2185
+ queue = Promise.resolve();
2186
+ constructor(directory = `${process.cwd()}/.omninode`) {
2187
+ this.filePath = `${directory}/plans.json`;
2188
+ }
2189
+ async save(plan) {
2190
+ return this.synchronized(async () => {
2191
+ const plans = await this.load();
2192
+ const index = plans.findIndex((p) => p.id === plan.id);
2193
+ if (index >= 0) plans[index] = plan;
2194
+ else plans.push(plan);
2195
+ await this.write(plans);
2196
+ });
2197
+ }
2198
+ async get(id) {
2199
+ return this.synchronized(async () => {
2200
+ const plans = await this.load();
2201
+ return plans.find((p) => p.id === id);
2202
+ });
2203
+ }
2204
+ async list(filter = {}) {
2205
+ return this.synchronized(async () => {
2206
+ const plans = await this.load();
2207
+ return plans.filter((plan) => filter.pipelineRunId ? plan.pipelineRunId === filter.pipelineRunId : true).filter((plan) => filter.taskId ? plan.taskId === filter.taskId : true).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
2208
+ });
2209
+ }
2210
+ synchronized(operation) {
2211
+ const result = this.queue.then(operation, operation);
2212
+ this.queue = result.then(
2213
+ () => void 0,
2214
+ () => void 0
2215
+ );
2216
+ return result;
2217
+ }
2218
+ async load() {
2219
+ try {
2220
+ const raw = await readFile4(this.filePath, "utf8");
2221
+ const parsed = JSON.parse(raw);
2222
+ return Array.isArray(parsed) ? parsed : [];
2223
+ } catch {
2224
+ return [];
2225
+ }
2226
+ }
2227
+ async write(plans) {
2228
+ const tmp = `${this.filePath}.tmp`;
2229
+ await mkdir4(dirname4(this.filePath), { recursive: true });
2230
+ await writeFile4(tmp, `${JSON.stringify(plans, null, 2)}
2231
+ `, "utf8");
2232
+ await rename4(tmp, this.filePath);
1586
2233
  }
1587
2234
  };
1588
2235
 
2236
+ // src/planner/factory.ts
2237
+ function buildPlanner(plannerConfig, chat, log = logger) {
2238
+ if (!plannerConfig) return void 0;
2239
+ if (plannerConfig.kind === "heuristic") {
2240
+ return new HeuristicPlanner();
2241
+ }
2242
+ if (!plannerConfig.model) {
2243
+ throw new ConfigError(
2244
+ "CONFIG_INVALID",
2245
+ 'Planner kind "model" needs a model reference ("provider:model-id") in omninode.yaml.'
2246
+ );
2247
+ }
2248
+ return new ModelPlanner({
2249
+ model: plannerConfig.model,
2250
+ chat,
2251
+ ...plannerConfig.instruction !== void 0 ? { instruction: plannerConfig.instruction } : {},
2252
+ fallback: new HeuristicPlanner(),
2253
+ log
2254
+ });
2255
+ }
2256
+
1589
2257
  // src/reports/aggregate.ts
1590
- import { randomBytes as randomBytes3 } from "crypto";
1591
- var SEVERITY_RANK = {
2258
+ import { randomBytes as randomBytes5 } from "crypto";
2259
+ var SEVERITY_RANK2 = {
1592
2260
  info: 0,
1593
2261
  low: 1,
1594
2262
  medium: 2,
@@ -1611,8 +2279,8 @@ function aggregateReports(reports, options = {}) {
1611
2279
  const totalRawFindings = reports.reduce((sum, r) => sum + r.findings.length, 0);
1612
2280
  const summary = `${reports.length} report(s) from ${sources.length > 0 ? sources.join(", ") : "no agents"}: ${findingGroups.length} unique finding group(s) (${totalRawFindings} raw), ${recommendationGroups.length} recommendation group(s).`;
1613
2281
  return {
1614
- id: `combined-${Date.now().toString(36)}-${randomBytes3(3).toString("hex")}`,
1615
- title: options.objective !== void 0 ? truncate(options.objective, 120) : "Combined report",
2282
+ id: `combined-${Date.now().toString(36)}-${randomBytes5(3).toString("hex")}`,
2283
+ title: options.objective !== void 0 ? truncate2(options.objective, 120) : "Combined report",
1616
2284
  ...options.objective !== void 0 ? { objective: options.objective } : {},
1617
2285
  ...options.pipelineRunId !== void 0 ? { pipelineRunId: options.pipelineRunId } : {},
1618
2286
  taskIds: options.taskIds ?? [],
@@ -1625,7 +2293,7 @@ function aggregateReports(reports, options = {}) {
1625
2293
  };
1626
2294
  }
1627
2295
  function rank(finding) {
1628
- return finding.severity ? SEVERITY_RANK[finding.severity] : -1;
2296
+ return finding.severity ? SEVERITY_RANK2[finding.severity] : -1;
1629
2297
  }
1630
2298
  function mergeFinding(groups, finding, report, threshold) {
1631
2299
  const source = { agent: report.agent, reportId: report.id };
@@ -1635,7 +2303,7 @@ function mergeFinding(groups, finding, report, threshold) {
1635
2303
  group.details.push(finding.detail);
1636
2304
  group.sources.push(source);
1637
2305
  group.evidence.push(...finding.evidence ?? []);
1638
- if (finding.severity && (!group.severity || SEVERITY_RANK[finding.severity] > SEVERITY_RANK[group.severity])) {
2306
+ if (finding.severity && (!group.severity || SEVERITY_RANK2[finding.severity] > SEVERITY_RANK2[group.severity])) {
1639
2307
  group.severity = finding.severity;
1640
2308
  }
1641
2309
  return;
@@ -1675,7 +2343,7 @@ function tokens(text) {
1675
2343
  text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((t) => t.length >= 3)
1676
2344
  );
1677
2345
  }
1678
- function truncate(text, max) {
2346
+ function truncate2(text, max) {
1679
2347
  return text.length > max ? `${text.slice(0, max - 1)}\u2026` : text;
1680
2348
  }
1681
2349
 
@@ -1719,14 +2387,14 @@ function extractReportFromText(raw, meta) {
1719
2387
  const nonEmpty = trimmed.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
1720
2388
  const proseLine = nonEmpty.find((line) => !line.startsWith("#") && !BULLET.test(line)) ?? nonEmpty[0] ?? "";
1721
2389
  const cleaned = proseLine.replace(/^#{1,6}\s+/, "").replace(BULLET, "$1");
1722
- const summary = cleaned.length > 0 ? truncate2(cleaned, 200) : "(no output)";
2390
+ const summary = cleaned.length > 0 ? truncate3(cleaned, 200) : "(no output)";
1723
2391
  const sections = parseSections(trimmed);
1724
2392
  const findingTexts = collectBullets(sections, [/finding/, /issue/, /problem/, /vulnerab/]);
1725
2393
  const recommendationTexts = collectBullets(sections, [/recommend/, /suggestion/, /next step/, /action/]);
1726
2394
  const evidenceTexts = collectBullets(sections, [/evidence/, /proof/, /log/]);
1727
2395
  const findings = findingTexts.map((text, index) => ({
1728
2396
  id: `${meta.taskId}-f-${index + 1}`,
1729
- title: truncate2(text, 120),
2397
+ title: truncate3(text, 120),
1730
2398
  detail: text,
1731
2399
  severity: guessSeverity(text)
1732
2400
  }));
@@ -1737,12 +2405,12 @@ function extractReportFromText(raw, meta) {
1737
2405
  ...meta.model !== void 0 ? { model: meta.model } : {},
1738
2406
  summary,
1739
2407
  findings,
1740
- recommendations: recommendationTexts.map((t) => truncate2(t, 200)),
2408
+ recommendations: recommendationTexts.map((t) => truncate3(t, 200)),
1741
2409
  metadata: evidenceTexts.length > 0 ? { evidence: evidenceTexts } : void 0,
1742
2410
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
1743
2411
  };
1744
2412
  }
1745
- function truncate2(text, max) {
2413
+ function truncate3(text, max) {
1746
2414
  return text.length > max ? `${text.slice(0, max - 1)}\u2026` : text;
1747
2415
  }
1748
2416
 
@@ -1797,8 +2465,8 @@ var ReportService = class {
1797
2465
  };
1798
2466
 
1799
2467
  // src/reports/store.ts
1800
- import { mkdir as mkdir3, readFile as readFile3, rename as rename3, writeFile as writeFile3 } from "fs/promises";
1801
- import { dirname as dirname3 } from "path";
2468
+ import { mkdir as mkdir5, readFile as readFile5, rename as rename5, writeFile as writeFile5 } from "fs/promises";
2469
+ import { dirname as dirname5 } from "path";
1802
2470
  var FileReportStore = class {
1803
2471
  filePath;
1804
2472
  queue = Promise.resolve();
@@ -1862,7 +2530,7 @@ var FileReportStore = class {
1862
2530
  }
1863
2531
  async load() {
1864
2532
  try {
1865
- const raw = await readFile3(this.filePath, "utf8");
2533
+ const raw = await readFile5(this.filePath, "utf8");
1866
2534
  const parsed = JSON.parse(raw);
1867
2535
  return Array.isArray(parsed) ? parsed : [];
1868
2536
  } catch {
@@ -1871,10 +2539,10 @@ var FileReportStore = class {
1871
2539
  }
1872
2540
  async write(entries) {
1873
2541
  const tmp = `${this.filePath}.tmp`;
1874
- await mkdir3(dirname3(this.filePath), { recursive: true });
1875
- await writeFile3(tmp, `${JSON.stringify(entries, null, 2)}
2542
+ await mkdir5(dirname5(this.filePath), { recursive: true });
2543
+ await writeFile5(tmp, `${JSON.stringify(entries, null, 2)}
1876
2544
  `, "utf8");
1877
- await rename3(tmp, this.filePath);
2545
+ await rename5(tmp, this.filePath);
1878
2546
  }
1879
2547
  };
1880
2548
 
@@ -1882,10 +2550,12 @@ var FileReportStore = class {
1882
2550
  function buildPipelineEngine(config, log = logger) {
1883
2551
  const agents = buildAgentRegistry(config, log);
1884
2552
  const roles = buildRoleRegistry(config);
2553
+ const chat = createDefaultChatFn(config.project.providers);
1885
2554
  const tasks = new TaskEngine({
1886
2555
  agents,
1887
2556
  roles,
1888
2557
  store: new FileTaskStore(),
2558
+ ...config.project.memory !== void 0 ? { memory: new MemoryService(createMemoryProvider(config.project.memory), log) } : {},
1889
2559
  log
1890
2560
  });
1891
2561
  return new PipelineEngine({
@@ -1894,23 +2564,28 @@ function buildPipelineEngine(config, log = logger) {
1894
2564
  roles,
1895
2565
  providers: config.project.providers,
1896
2566
  store: new FilePipelineRunStore(),
1897
- chat: createDefaultChatFn(config.project.providers),
2567
+ chat,
2568
+ ...config.project.planner !== void 0 ? { planner: buildPlanner(config.project.planner, chat, log) } : {},
2569
+ plans: new FilePlanStore(),
1898
2570
  reports: new ReportService(new FileReportStore(), log),
1899
2571
  log
1900
2572
  });
1901
2573
  }
1902
2574
 
1903
2575
  // src/version.ts
1904
- var OMNINODE_VERSION = "0.7.0";
2576
+ var OMNINODE_VERSION = "0.9.0";
1905
2577
  export {
1906
2578
  AgentError,
1907
2579
  AgentRegistry,
1908
2580
  ConfigError,
1909
2581
  ConsoleLogSink,
1910
2582
  FilePipelineRunStore,
2583
+ FileReportStore,
1911
2584
  FileTaskStore,
2585
+ LocalMemoryProvider,
1912
2586
  Logger,
1913
2587
  MemoryError,
2588
+ MemoryService,
1914
2589
  ModelRegistry,
1915
2590
  OMNINODE_VERSION,
1916
2591
  OPENAI_COMPATIBLE_TYPES,
@@ -1919,13 +2594,17 @@ export {
1919
2594
  OpenAICompatibleProvider,
1920
2595
  PipelineEngine,
1921
2596
  PipelineError,
2597
+ PlannerError,
1922
2598
  ProcessAgent,
1923
2599
  ProtocolError,
1924
2600
  ProviderError,
2601
+ RememberaMemoryProvider,
2602
+ ReportService,
1925
2603
  RoleRegistry,
1926
2604
  TaskEngine,
1927
2605
  TaskError,
1928
2606
  agentConfigSchema,
2607
+ aggregateReports,
1929
2608
  appConfigSchema,
1930
2609
  authHeaders,
1931
2610
  buildAgentRegistry,
@@ -1934,21 +2613,26 @@ export {
1934
2613
  composeTaskText,
1935
2614
  createAgent,
1936
2615
  createDefaultChatFn,
2616
+ createMemoryProvider,
1937
2617
  createProvider,
1938
2618
  createTaskEngine,
1939
2619
  defaultProjectConfigYaml,
1940
2620
  expandEnvRefs,
2621
+ extractReportFromText,
1941
2622
  findConfigFile,
1942
2623
  isOmniNodeError,
2624
+ keywords,
1943
2625
  loadConfig,
1944
2626
  logger,
1945
2627
  pipelineConfigSchema,
1946
2628
  pipelineStepConfigSchema,
2629
+ plannerConfigSchema,
1947
2630
  projectConfigSchema,
1948
2631
  providerConfigSchema,
1949
2632
  registryKey,
1950
2633
  requestJson,
1951
2634
  resolveApiKey,
1952
- roleConfigSchema
2635
+ roleConfigSchema,
2636
+ similar
1953
2637
  };
1954
2638
  //# sourceMappingURL=index.js.map