@hilbras/omninode 0.10.0 → 2.0.0-alpha.1

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
@@ -130,6 +130,7 @@ var agentConfigSchema = z.object({
130
130
  env: z.record(z.string()).optional(),
131
131
  input_mode: z.enum(["stdin", "arg", "protocol"]).default("stdin"),
132
132
  timeout_ms: z.number().int().positive().optional(),
133
+ inherit_env: z.boolean().optional(),
133
134
  metadata: z.record(z.unknown()).optional()
134
135
  }).strict();
135
136
  var roleConfigSchema = z.object({
@@ -303,6 +304,7 @@ function toAgentConfig(raw) {
303
304
  env: raw.env,
304
305
  inputMode: raw.input_mode,
305
306
  timeoutMs: raw.timeout_ms,
307
+ inheritEnv: raw.inherit_env,
306
308
  metadata: raw.metadata
307
309
  };
308
310
  }
@@ -664,6 +666,17 @@ var ModelRegistry = class {
664
666
  import { spawn } from "child_process";
665
667
  var DEFAULT_TIMEOUT_MS2 = 6e5;
666
668
  var KILL_GRACE_MS = 5e3;
669
+ function buildChildEnv(config) {
670
+ const configured = config.env ?? {};
671
+ if (config.inheritEnv !== false) {
672
+ return { ...process.env, ...configured };
673
+ }
674
+ return {
675
+ PATH: process.env.PATH ?? "",
676
+ HOME: process.env.HOME ?? "",
677
+ ...configured
678
+ };
679
+ }
667
680
  function composeTaskText(input) {
668
681
  const sections = [`# Objective
669
682
  ${input.objective}`];
@@ -730,7 +743,7 @@ var ProcessAgent = class {
730
743
  [...this.config.args ?? [], ...mode === "arg" ? [composeTaskText(input)] : []],
731
744
  {
732
745
  cwd: this.config.cwd,
733
- env: { ...process.env, ...this.config.env },
746
+ env: buildChildEnv(this.config),
734
747
  stdio: ["pipe", "pipe", "pipe"]
735
748
  }
736
749
  );
@@ -974,40 +987,54 @@ function buildRoleRegistry(config) {
974
987
  return registry;
975
988
  }
976
989
 
977
- // src/tasks/store.ts
990
+ // src/persistence/json-file-store.ts
978
991
  import { mkdir, readFile, rename, writeFile } from "fs/promises";
979
992
  import { dirname } from "path";
980
- var FileTaskStore = class {
993
+ var JsonFileStore = class {
981
994
  filePath;
982
- /**
983
- * Read-modify-write operations are serialized: parallel pipeline tasks
984
- * would otherwise race on the JSON file (last writer wins).
985
- */
995
+ schemaVersion;
996
+ onCorrupt;
997
+ log;
986
998
  queue = Promise.resolve();
987
- constructor(directory = `${process.cwd()}/.omninode`) {
988
- this.filePath = `${directory}/tasks.json`;
999
+ constructor(options) {
1000
+ this.filePath = `${options.directory ?? `${process.cwd()}/.omninode`}/${options.fileName}`;
1001
+ this.schemaVersion = options.schemaVersion ?? 1;
1002
+ this.onCorrupt = options.onCorrupt;
1003
+ this.log = logger.child({ component: "persistence", file: options.fileName });
989
1004
  }
990
- async save(task) {
991
- return this.synchronized(async () => {
992
- const tasks = await this.load();
993
- const index = tasks.findIndex((t) => t.id === task.id);
994
- if (index >= 0) tasks[index] = task;
995
- else tasks.push(task);
996
- await this.write(tasks);
997
- });
1005
+ get path() {
1006
+ return this.filePath;
998
1007
  }
999
- async get(id) {
1000
- return this.synchronized(async () => {
1001
- const tasks = await this.load();
1002
- return tasks.find((t) => t.id === id);
1003
- });
1008
+ /** All items, or [] when missing/corrupt. */
1009
+ async readAll() {
1010
+ let raw;
1011
+ try {
1012
+ raw = await readFile(this.filePath, "utf8");
1013
+ } catch {
1014
+ return [];
1015
+ }
1016
+ try {
1017
+ const parsed = JSON.parse(raw);
1018
+ if (Array.isArray(parsed)) return parsed;
1019
+ if (parsed !== null && typeof parsed === "object" && Array.isArray(parsed.items)) {
1020
+ return parsed.items;
1021
+ }
1022
+ throw new Error("persisted file has an unrecognized shape");
1023
+ } catch (error) {
1024
+ this.log.warn(`Treating ${this.filePath} as empty \u2014 it could not be parsed.`);
1025
+ this.onCorrupt?.(this.filePath, error);
1026
+ return [];
1027
+ }
1004
1028
  }
1005
- async list(filter = {}) {
1006
- return this.synchronized(async () => {
1007
- const tasks = await this.load();
1008
- return tasks.filter((task) => filter.status ? task.status === filter.status : true).filter((task) => filter.project ? task.project === filter.project : true).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
1009
- });
1029
+ async writeAll(items) {
1030
+ const document = { schemaVersion: this.schemaVersion, items };
1031
+ const tmp = `${this.filePath}.tmp`;
1032
+ await mkdir(dirname(this.filePath), { recursive: true });
1033
+ await writeFile(tmp, `${JSON.stringify(document, null, 2)}
1034
+ `, "utf8");
1035
+ await rename(tmp, this.filePath);
1010
1036
  }
1037
+ /** Serializes an async operation against this file. */
1011
1038
  synchronized(operation) {
1012
1039
  const result = this.queue.then(operation, operation);
1013
1040
  this.queue = result.then(
@@ -1016,21 +1043,37 @@ var FileTaskStore = class {
1016
1043
  );
1017
1044
  return result;
1018
1045
  }
1019
- async load() {
1020
- try {
1021
- const raw = await readFile(this.filePath, "utf8");
1022
- const parsed = JSON.parse(raw);
1023
- return Array.isArray(parsed) ? parsed : [];
1024
- } catch {
1025
- return [];
1026
- }
1046
+ /** Read-modify-write helper: applies `mutate` to the current items. */
1047
+ async mutate(mutate) {
1048
+ return this.synchronized(async () => {
1049
+ const items = await this.readAll();
1050
+ const [next, result] = await mutate(items);
1051
+ await this.writeAll(next);
1052
+ return result;
1053
+ });
1027
1054
  }
1028
- async write(tasks) {
1029
- const tmp = `${this.filePath}.tmp`;
1030
- await mkdir(dirname(this.filePath), { recursive: true });
1031
- await writeFile(tmp, `${JSON.stringify(tasks, null, 2)}
1032
- `, "utf8");
1033
- await rename(tmp, this.filePath);
1055
+ };
1056
+
1057
+ // src/tasks/store.ts
1058
+ var FileTaskStore = class extends JsonFileStore {
1059
+ constructor(directory) {
1060
+ super({ ...directory !== void 0 ? { directory } : {}, fileName: "tasks.json" });
1061
+ }
1062
+ save(task) {
1063
+ return this.mutate((tasks) => {
1064
+ const index = tasks.findIndex((t) => t.id === task.id);
1065
+ if (index >= 0) tasks[index] = task;
1066
+ else tasks.push(task);
1067
+ return [tasks, void 0];
1068
+ });
1069
+ }
1070
+ async get(id) {
1071
+ const tasks = await this.readAll();
1072
+ return tasks.find((t) => t.id === id);
1073
+ }
1074
+ async list(filter = {}) {
1075
+ const tasks = await this.readAll();
1076
+ return tasks.filter((task) => filter.status ? task.status === filter.status : true).filter((task) => filter.project ? task.project === filter.project : true).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
1034
1077
  }
1035
1078
  };
1036
1079
 
@@ -1085,6 +1128,7 @@ var TaskEngine = class {
1085
1128
  };
1086
1129
  await this.options.store.save(task);
1087
1130
  this.log.info(`Created task ${task.id}.`);
1131
+ await this.audit("task.created", task.id, { objective: task.objective, project: task.project });
1088
1132
  return task;
1089
1133
  }
1090
1134
  async queue(id) {
@@ -1176,6 +1220,7 @@ var TaskEngine = class {
1176
1220
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1177
1221
  };
1178
1222
  await this.options.store.save(reset);
1223
+ await this.audit("task.retry", id, { from: task.status });
1179
1224
  this.log.info(`Task ${id} reset for retry.`);
1180
1225
  return reset;
1181
1226
  }
@@ -1204,16 +1249,66 @@ var TaskEngine = class {
1204
1249
  ...extra.result !== void 0 ? { result: { ...task.result, ...extra.result } } : {}
1205
1250
  };
1206
1251
  await this.options.store.save(updated);
1252
+ await this.audit(`task.${to === "queued" ? "queued" : to}`, updated.id, {
1253
+ from: task.status
1254
+ });
1207
1255
  return updated;
1208
1256
  }
1257
+ async audit(action, id, detail) {
1258
+ await this.options.audit?.record({
1259
+ at: (/* @__PURE__ */ new Date()).toISOString(),
1260
+ action,
1261
+ id,
1262
+ ...detail !== void 0 ? { detail } : {}
1263
+ });
1264
+ }
1209
1265
  };
1210
1266
  function generateTaskId() {
1211
1267
  return `task-${Date.now().toString(36)}-${randomBytes(3).toString("hex")}`;
1212
1268
  }
1213
1269
 
1214
- // src/memory/local.ts
1215
- import { mkdir as mkdir2, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "fs/promises";
1270
+ // src/audit/index.ts
1271
+ import { appendFile, mkdir as mkdir2 } from "fs/promises";
1216
1272
  import { dirname as dirname2 } from "path";
1273
+ var FileAuditLog = class {
1274
+ filePath;
1275
+ log;
1276
+ constructor(directory = `${process.cwd()}/.omninode`, log = logger) {
1277
+ this.filePath = `${directory}/audit.jsonl`;
1278
+ this.log = log.child({ component: "audit" });
1279
+ }
1280
+ async record(event) {
1281
+ try {
1282
+ await mkdir2(dirname2(this.filePath), { recursive: true });
1283
+ await appendFile(this.filePath, `${JSON.stringify(event)}
1284
+ `, "utf8");
1285
+ } catch (error) {
1286
+ this.log.warn(
1287
+ `Audit write failed: ${error instanceof Error ? error.message : String(error)}`
1288
+ );
1289
+ }
1290
+ }
1291
+ /** Recent events, oldest first; used by the CLI. */
1292
+ async recent(limit = 20) {
1293
+ const { readFile: readFile2 } = await import("fs/promises");
1294
+ let raw;
1295
+ try {
1296
+ raw = await readFile2(this.filePath, "utf8");
1297
+ } catch {
1298
+ return [];
1299
+ }
1300
+ const events = raw.split("\n").filter((line) => line.length > 0).map((line) => {
1301
+ try {
1302
+ return JSON.parse(line);
1303
+ } catch {
1304
+ return void 0;
1305
+ }
1306
+ }).filter((event) => event !== void 0);
1307
+ return events.slice(-limit);
1308
+ }
1309
+ };
1310
+
1311
+ // src/memory/local.ts
1217
1312
  var STOPWORDS = /* @__PURE__ */ new Set([
1218
1313
  "the",
1219
1314
  "and",
@@ -1270,29 +1365,24 @@ function relevanceScore(queryKeywords, entry) {
1270
1365
  }
1271
1366
  return overlap * 2 + tagMatches * 1.5;
1272
1367
  }
1273
- var LocalMemoryProvider = class {
1368
+ var LocalMemoryProvider = class extends JsonFileStore {
1274
1369
  name = "local";
1275
- filePath;
1276
- queue = Promise.resolve();
1277
- constructor(directory = `${process.cwd()}/.omninode`) {
1278
- this.filePath = `${directory}/memory.json`;
1370
+ constructor(directory) {
1371
+ super({ ...directory !== void 0 ? { directory } : {}, fileName: "memory.json" });
1279
1372
  }
1280
1373
  async query(query) {
1281
- return this.synchronized(async () => {
1282
- const entries = await this.load();
1283
- const filtered = entries.filter((entry) => query.scope ? entry.scope === query.scope : true).filter(
1284
- (entry) => query.tags && query.tags.length > 0 ? query.tags.every((tag) => (entry.tags ?? []).includes(tag)) : true
1285
- );
1286
- if (!query.text || query.text.trim().length === 0) {
1287
- return filtered.sort((a, b) => (b.updatedAt ?? b.createdAt ?? "").localeCompare(a.updatedAt ?? a.createdAt ?? "")).slice(0, query.limit ?? 10);
1288
- }
1289
- const queryKeywords = keywords(query.text);
1290
- 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);
1291
- });
1374
+ const entries = await this.readAll();
1375
+ const filtered = entries.filter((entry) => query.scope ? entry.scope === query.scope : true).filter(
1376
+ (entry) => query.tags && query.tags.length > 0 ? query.tags.every((tag) => (entry.tags ?? []).includes(tag)) : true
1377
+ );
1378
+ if (!query.text || query.text.trim().length === 0) {
1379
+ return filtered.sort((a, b) => (b.updatedAt ?? b.createdAt ?? "").localeCompare(a.updatedAt ?? a.createdAt ?? "")).slice(0, query.limit ?? 10);
1380
+ }
1381
+ const queryKeywords = keywords(query.text);
1382
+ 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);
1292
1383
  }
1293
- async write(entry) {
1294
- return this.synchronized(async () => {
1295
- const entries = await this.load();
1384
+ write(entry) {
1385
+ return this.mutate((entries) => {
1296
1386
  const index = entries.findIndex((e) => e.key === entry.key);
1297
1387
  const stamped = {
1298
1388
  ...entry,
@@ -1301,35 +1391,11 @@ var LocalMemoryProvider = class {
1301
1391
  };
1302
1392
  if (index >= 0) entries[index] = stamped;
1303
1393
  else entries.push(stamped);
1304
- await this.saveAll(entries);
1394
+ return [entries, void 0];
1305
1395
  });
1306
1396
  }
1307
1397
  async count() {
1308
- return (await this.load()).length;
1309
- }
1310
- synchronized(operation) {
1311
- const result = this.queue.then(operation, operation);
1312
- this.queue = result.then(
1313
- () => void 0,
1314
- () => void 0
1315
- );
1316
- return result;
1317
- }
1318
- async load() {
1319
- try {
1320
- const raw = await readFile2(this.filePath, "utf8");
1321
- const parsed = JSON.parse(raw);
1322
- return Array.isArray(parsed) ? parsed : [];
1323
- } catch {
1324
- return [];
1325
- }
1326
- }
1327
- async saveAll(entries) {
1328
- const tmp = `${this.filePath}.tmp`;
1329
- await mkdir2(dirname2(this.filePath), { recursive: true });
1330
- await writeFile2(tmp, `${JSON.stringify(entries, null, 2)}
1331
- `, "utf8");
1332
- await rename2(tmp, this.filePath);
1398
+ return (await this.readAll()).length;
1333
1399
  }
1334
1400
  };
1335
1401
 
@@ -1511,6 +1577,7 @@ function createTaskEngine(config, log = logger, directory) {
1511
1577
  roles,
1512
1578
  store: new FileTaskStore(directory),
1513
1579
  ...config.project.memory !== void 0 ? { memory: new MemoryService(createMemoryProvider(config.project.memory), log) } : {},
1580
+ audit: new FileAuditLog(),
1514
1581
  log
1515
1582
  });
1516
1583
  }
@@ -1862,6 +1929,12 @@ var PipelineEngine = class {
1862
1929
  };
1863
1930
  await this.options.store.save(run);
1864
1931
  this.log.info(`Running pipeline ${def.id} as ${run.id}.`);
1932
+ await this.options.audit?.record({
1933
+ at: (/* @__PURE__ */ new Date()).toISOString(),
1934
+ action: "pipeline.run.started",
1935
+ id: run.id,
1936
+ detail: { pipeline: def.id, objective }
1937
+ });
1865
1938
  const deps = this.effectiveDependencies(def);
1866
1939
  const outcomes = /* @__PURE__ */ new Map();
1867
1940
  const pending = new Set(def.steps.map((s) => s.id));
@@ -1952,6 +2025,12 @@ ${contribution}`);
1952
2025
  };
1953
2026
  await this.options.store.save(finished);
1954
2027
  this.log.info(`Pipeline ${def.id} finished: ${finalStatus}.`);
2028
+ await this.options.audit?.record({
2029
+ at: (/* @__PURE__ */ new Date()).toISOString(),
2030
+ action: "pipeline.run.finished",
2031
+ id: run.id,
2032
+ detail: { pipeline: def.id, status: finalStatus }
2033
+ });
1955
2034
  return finished;
1956
2035
  }
1957
2036
  async get(runId) {
@@ -2096,6 +2175,12 @@ ${contribution}`);
2096
2175
  };
2097
2176
  }
2098
2177
  await this.options.plans?.save(plan);
2178
+ await this.options.audit?.record({
2179
+ at: (/* @__PURE__ */ new Date()).toISOString(),
2180
+ action: "plan.generated",
2181
+ id: plan.id,
2182
+ detail: { generatedBy: plan.generatedBy, steps: plan.steps.length }
2183
+ });
2099
2184
  return {
2100
2185
  status: "completed",
2101
2186
  summary: plan.summary,
@@ -2153,114 +2238,44 @@ function createDefaultChatFn(providers) {
2153
2238
  }
2154
2239
 
2155
2240
  // src/pipelines/store.ts
2156
- import { mkdir as mkdir3, readFile as readFile3, rename as rename3, writeFile as writeFile3 } from "fs/promises";
2157
- import { dirname as dirname3 } from "path";
2158
- var FilePipelineRunStore = class {
2159
- filePath;
2160
- queue = Promise.resolve();
2161
- constructor(directory = `${process.cwd()}/.omninode`) {
2162
- this.filePath = `${directory}/pipelines.json`;
2241
+ var FilePipelineRunStore = class extends JsonFileStore {
2242
+ constructor(directory) {
2243
+ super({ ...directory !== void 0 ? { directory } : {}, fileName: "pipelines.json" });
2163
2244
  }
2164
- async save(run) {
2165
- return this.synchronized(async () => {
2166
- const runs = await this.load();
2245
+ save(run) {
2246
+ return this.mutate((runs) => {
2167
2247
  const index = runs.findIndex((r) => r.id === run.id);
2168
2248
  if (index >= 0) runs[index] = run;
2169
2249
  else runs.push(run);
2170
- await this.write(runs);
2250
+ return [runs, void 0];
2171
2251
  });
2172
2252
  }
2173
2253
  async get(id) {
2174
- return this.synchronized(async () => {
2175
- const runs = await this.load();
2176
- return runs.find((r) => r.id === id);
2177
- });
2254
+ return (await this.readAll()).find((r) => r.id === id);
2178
2255
  }
2179
2256
  async list(filter = {}) {
2180
- return this.synchronized(async () => {
2181
- const runs = await this.load();
2182
- return runs.filter((run) => filter.pipelineId ? run.pipelineId === filter.pipelineId : true).filter((run) => filter.status ? run.status === filter.status : true).sort((a, b) => (a.startedAt ?? "").localeCompare(b.startedAt ?? ""));
2183
- });
2184
- }
2185
- synchronized(operation) {
2186
- const result = this.queue.then(operation, operation);
2187
- this.queue = result.then(
2188
- () => void 0,
2189
- () => void 0
2190
- );
2191
- return result;
2192
- }
2193
- async load() {
2194
- try {
2195
- const raw = await readFile3(this.filePath, "utf8");
2196
- const parsed = JSON.parse(raw);
2197
- return Array.isArray(parsed) ? parsed : [];
2198
- } catch {
2199
- return [];
2200
- }
2201
- }
2202
- async write(runs) {
2203
- const tmp = `${this.filePath}.tmp`;
2204
- await mkdir3(dirname3(this.filePath), { recursive: true });
2205
- await writeFile3(tmp, `${JSON.stringify(runs, null, 2)}
2206
- `, "utf8");
2207
- await rename3(tmp, this.filePath);
2257
+ return (await this.readAll()).filter((run) => filter.pipelineId ? run.pipelineId === filter.pipelineId : true).filter((run) => filter.status ? run.status === filter.status : true).sort((a, b) => (a.startedAt ?? "").localeCompare(b.startedAt ?? ""));
2208
2258
  }
2209
2259
  };
2210
2260
 
2211
2261
  // src/planner/store.ts
2212
- import { mkdir as mkdir4, readFile as readFile4, rename as rename4, writeFile as writeFile4 } from "fs/promises";
2213
- import { dirname as dirname4 } from "path";
2214
- var FilePlanStore = class {
2215
- filePath;
2216
- queue = Promise.resolve();
2217
- constructor(directory = `${process.cwd()}/.omninode`) {
2218
- this.filePath = `${directory}/plans.json`;
2262
+ var FilePlanStore = class extends JsonFileStore {
2263
+ constructor(directory) {
2264
+ super({ ...directory !== void 0 ? { directory } : {}, fileName: "plans.json" });
2219
2265
  }
2220
- async save(plan) {
2221
- return this.synchronized(async () => {
2222
- const plans = await this.load();
2266
+ save(plan) {
2267
+ return this.mutate((plans) => {
2223
2268
  const index = plans.findIndex((p) => p.id === plan.id);
2224
2269
  if (index >= 0) plans[index] = plan;
2225
2270
  else plans.push(plan);
2226
- await this.write(plans);
2271
+ return [plans, void 0];
2227
2272
  });
2228
2273
  }
2229
2274
  async get(id) {
2230
- return this.synchronized(async () => {
2231
- const plans = await this.load();
2232
- return plans.find((p) => p.id === id);
2233
- });
2275
+ return (await this.readAll()).find((p) => p.id === id);
2234
2276
  }
2235
2277
  async list(filter = {}) {
2236
- return this.synchronized(async () => {
2237
- const plans = await this.load();
2238
- 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));
2239
- });
2240
- }
2241
- synchronized(operation) {
2242
- const result = this.queue.then(operation, operation);
2243
- this.queue = result.then(
2244
- () => void 0,
2245
- () => void 0
2246
- );
2247
- return result;
2248
- }
2249
- async load() {
2250
- try {
2251
- const raw = await readFile4(this.filePath, "utf8");
2252
- const parsed = JSON.parse(raw);
2253
- return Array.isArray(parsed) ? parsed : [];
2254
- } catch {
2255
- return [];
2256
- }
2257
- }
2258
- async write(plans) {
2259
- const tmp = `${this.filePath}.tmp`;
2260
- await mkdir4(dirname4(this.filePath), { recursive: true });
2261
- await writeFile4(tmp, `${JSON.stringify(plans, null, 2)}
2262
- `, "utf8");
2263
- await rename4(tmp, this.filePath);
2278
+ return (await this.readAll()).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));
2264
2279
  }
2265
2280
  };
2266
2281
 
@@ -2496,84 +2511,39 @@ var ReportService = class {
2496
2511
  };
2497
2512
 
2498
2513
  // src/reports/store.ts
2499
- import { mkdir as mkdir5, readFile as readFile5, rename as rename5, writeFile as writeFile5 } from "fs/promises";
2500
- import { dirname as dirname5 } from "path";
2501
- var FileReportStore = class {
2502
- filePath;
2503
- queue = Promise.resolve();
2504
- constructor(directory = `${process.cwd()}/.omninode`) {
2505
- this.filePath = `${directory}/reports.json`;
2514
+ var FileReportStore = class extends JsonFileStore {
2515
+ constructor(directory) {
2516
+ super({ ...directory !== void 0 ? { directory } : {}, fileName: "reports.json" });
2506
2517
  }
2507
2518
  async saveReports(reports) {
2508
- return this.synchronized(async () => {
2509
- const entries = await this.load();
2519
+ await this.mutate((entries) => {
2510
2520
  for (const report of reports) {
2511
- const index = entries.findIndex(
2512
- (e) => e.kind === "report" && e.report.id === report.id
2513
- );
2521
+ const index = entries.findIndex((e) => e.kind === "report" && e.report.id === report.id);
2514
2522
  const entry = { kind: "report", report };
2515
2523
  if (index >= 0) entries[index] = entry;
2516
2524
  else entries.push(entry);
2517
2525
  }
2518
- await this.write(entries);
2526
+ return [entries, void 0];
2519
2527
  });
2520
2528
  }
2521
2529
  async listReports(filter = {}) {
2522
- return this.synchronized(async () => {
2523
- const entries = await this.load();
2524
- return entries.filter((e) => e.kind === "report").map((e) => e.report).filter((report) => filter.taskId ? report.taskId === filter.taskId : true).filter((report) => filter.agent ? report.agent === filter.agent : true).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
2525
- });
2530
+ return (await this.readAll()).filter((e) => e.kind === "report").map((e) => e.report).filter((report) => filter.taskId ? report.taskId === filter.taskId : true).filter((report) => filter.agent ? report.agent === filter.agent : true).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
2526
2531
  }
2527
2532
  async saveCombined(combined) {
2528
- return this.synchronized(async () => {
2529
- const entries = await this.load();
2530
- const index = entries.findIndex(
2531
- (e) => e.kind === "combined" && e.combined.id === combined.id
2532
- );
2533
+ await this.mutate((entries) => {
2534
+ const index = entries.findIndex((e) => e.kind === "combined" && e.combined.id === combined.id);
2533
2535
  const entry = { kind: "combined", combined };
2534
2536
  if (index >= 0) entries[index] = entry;
2535
2537
  else entries.push(entry);
2536
- await this.write(entries);
2538
+ return [entries, void 0];
2537
2539
  });
2538
2540
  }
2539
2541
  async getCombined(id) {
2540
- return this.synchronized(async () => {
2541
- const entries = await this.load();
2542
- const found = entries.find((e) => e.kind === "combined" && e.combined.id === id);
2543
- return found?.kind === "combined" ? found.combined : void 0;
2544
- });
2542
+ const found = (await this.readAll()).find((e) => e.kind === "combined" && e.combined.id === id);
2543
+ return found?.kind === "combined" ? found.combined : void 0;
2545
2544
  }
2546
2545
  async listCombined(filter = {}) {
2547
- return this.synchronized(async () => {
2548
- const entries = await this.load();
2549
- return entries.filter((e) => e.kind === "combined").map((e) => e.combined).filter(
2550
- (combined) => filter.pipelineRunId ? combined.pipelineRunId === filter.pipelineRunId : true
2551
- ).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
2552
- });
2553
- }
2554
- synchronized(operation) {
2555
- const result = this.queue.then(operation, operation);
2556
- this.queue = result.then(
2557
- () => void 0,
2558
- () => void 0
2559
- );
2560
- return result;
2561
- }
2562
- async load() {
2563
- try {
2564
- const raw = await readFile5(this.filePath, "utf8");
2565
- const parsed = JSON.parse(raw);
2566
- return Array.isArray(parsed) ? parsed : [];
2567
- } catch {
2568
- return [];
2569
- }
2570
- }
2571
- async write(entries) {
2572
- const tmp = `${this.filePath}.tmp`;
2573
- await mkdir5(dirname5(this.filePath), { recursive: true });
2574
- await writeFile5(tmp, `${JSON.stringify(entries, null, 2)}
2575
- `, "utf8");
2576
- await rename5(tmp, this.filePath);
2546
+ return (await this.readAll()).filter((e) => e.kind === "combined").map((e) => e.combined).filter((combined) => filter.pipelineRunId ? combined.pipelineRunId === filter.pipelineRunId : true).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
2577
2547
  }
2578
2548
  };
2579
2549
 
@@ -2587,6 +2557,7 @@ function buildPipelineEngine(config, log = logger) {
2587
2557
  roles,
2588
2558
  store: new FileTaskStore(),
2589
2559
  ...config.project.memory !== void 0 ? { memory: new MemoryService(createMemoryProvider(config.project.memory), log) } : {},
2560
+ audit: new FileAuditLog(),
2590
2561
  log
2591
2562
  });
2592
2563
  return new PipelineEngine({
@@ -2599,24 +2570,48 @@ function buildPipelineEngine(config, log = logger) {
2599
2570
  ...config.project.planner !== void 0 ? { planner: buildPlanner(config.project.planner, chat, log) } : {},
2600
2571
  plans: new FilePlanStore(),
2601
2572
  reports: new ReportService(new FileReportStore(), log),
2573
+ audit: new FileAuditLog(),
2602
2574
  log
2603
2575
  });
2604
2576
  }
2605
2577
 
2578
+ // src/persistence/index.ts
2579
+ var ProjectStores = class {
2580
+ tasks;
2581
+ pipelineRuns;
2582
+ reports;
2583
+ plans;
2584
+ memory;
2585
+ audit;
2586
+ constructor(directory = `${process.cwd()}/.omninode`) {
2587
+ this.tasks = new FileTaskStore(directory);
2588
+ this.pipelineRuns = new FilePipelineRunStore(directory);
2589
+ this.reports = new FileReportStore(directory);
2590
+ this.plans = new FilePlanStore(directory);
2591
+ this.memory = new LocalMemoryProvider(directory);
2592
+ this.audit = new FileAuditLog(directory);
2593
+ }
2594
+ };
2595
+
2606
2596
  // src/version.ts
2607
- var OMNINODE_VERSION = "0.10.0";
2597
+ var OMNINODE_VERSION = "2.0.0-alpha.1";
2608
2598
  export {
2609
2599
  AgentError,
2610
2600
  AgentRegistry,
2611
2601
  ConfigError,
2612
2602
  ConsoleLogSink,
2603
+ FileAuditLog,
2613
2604
  FilePipelineRunStore,
2605
+ FilePlanStore,
2614
2606
  FileReportStore,
2615
2607
  FileTaskStore,
2608
+ HeuristicPlanner,
2609
+ JsonFileStore,
2616
2610
  LocalMemoryProvider,
2617
2611
  Logger,
2618
2612
  MemoryError,
2619
2613
  MemoryService,
2614
+ ModelPlanner,
2620
2615
  ModelRegistry,
2621
2616
  OMNINODE_VERSION,
2622
2617
  OPENAI_COMPATIBLE_TYPES,
@@ -2627,6 +2622,7 @@ export {
2627
2622
  PipelineError,
2628
2623
  PlannerError,
2629
2624
  ProcessAgent,
2625
+ ProjectStores,
2630
2626
  ProtocolError,
2631
2627
  ProviderError,
2632
2628
  RememberaMemoryProvider,
@@ -2639,7 +2635,10 @@ export {
2639
2635
  appConfigSchema,
2640
2636
  authHeaders,
2641
2637
  buildAgentRegistry,
2638
+ buildChildEnv,
2642
2639
  buildPipelineEngine,
2640
+ buildPlanner,
2641
+ buildPlannerContext,
2643
2642
  buildRoleRegistry,
2644
2643
  composeTaskText,
2645
2644
  createAgent,
@@ -2655,6 +2654,7 @@ export {
2655
2654
  keywords,
2656
2655
  loadConfig,
2657
2656
  logger,
2657
+ parsePlanJson,
2658
2658
  pipelineConfigSchema,
2659
2659
  pipelineStepConfigSchema,
2660
2660
  plannerConfigSchema,