agentflow-core 0.4.0 → 0.5.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.
@@ -209,6 +209,7 @@ function getTraceTree(trace) {
209
209
  // src/live.ts
210
210
  import { existsSync, readdirSync, readFileSync, statSync, watch } from "fs";
211
211
  import { basename, join, resolve } from "path";
212
+ import { execSync } from "child_process";
212
213
  var C = {
213
214
  reset: "\x1B[0m",
214
215
  bold: "\x1B[1m",
@@ -276,11 +277,29 @@ Examples:
276
277
  `.trim()
277
278
  );
278
279
  }
280
+ var dirMtimeCache = /* @__PURE__ */ new Map();
281
+ var dirFileCache = /* @__PURE__ */ new Map();
279
282
  function scanFiles(dirs, recursive) {
280
283
  const results = [];
281
284
  const seen = /* @__PURE__ */ new Set();
282
285
  function scanDir(d, topLevel) {
283
286
  try {
287
+ const dirStat = statSync(d);
288
+ const dirMtime = dirStat.mtime.getTime();
289
+ const cachedMtime = dirMtimeCache.get(d);
290
+ if (cachedMtime === dirMtime) {
291
+ const cached = dirFileCache.get(d);
292
+ if (cached) {
293
+ for (const f of cached) {
294
+ if (!seen.has(f.path)) {
295
+ seen.add(f.path);
296
+ results.push(f);
297
+ }
298
+ }
299
+ return;
300
+ }
301
+ }
302
+ const dirResults = [];
284
303
  for (const f of readdirSync(d)) {
285
304
  if (f.startsWith(".")) continue;
286
305
  const fp = join(d, f);
@@ -298,12 +317,18 @@ function scanFiles(dirs, recursive) {
298
317
  if (!stat.isFile()) continue;
299
318
  if (f.endsWith(".json")) {
300
319
  seen.add(fp);
301
- results.push({ filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".json" });
320
+ const entry = { filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".json" };
321
+ results.push(entry);
322
+ dirResults.push(entry);
302
323
  } else if (f.endsWith(".jsonl")) {
303
324
  seen.add(fp);
304
- results.push({ filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".jsonl" });
325
+ const entry = { filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".jsonl" };
326
+ results.push(entry);
327
+ dirResults.push(entry);
305
328
  }
306
329
  }
330
+ dirMtimeCache.set(d, dirMtime);
331
+ dirFileCache.set(d, dirResults);
307
332
  } catch {
308
333
  }
309
334
  }
@@ -456,11 +481,22 @@ function processJsonFile(file) {
456
481
  const status2 = findStatus(w);
457
482
  const ts2 = findTimestamp(w) || findTimestamp(obj) || file.mtime;
458
483
  const pid = w.pid;
459
- const detail2 = pid ? `pid: ${pid}` : extractDetail(w);
484
+ let validatedStatus = status2;
485
+ let pidAlive = true;
486
+ if (pid && (status2 === "running" || status2 === "ok")) {
487
+ try {
488
+ execSync(`kill -0 ${pid} 2>/dev/null`, { stdio: "ignore" });
489
+ } catch {
490
+ pidAlive = false;
491
+ validatedStatus = "error";
492
+ }
493
+ }
494
+ const pidLabel = pid ? pidAlive ? `pid: ${pid}` : `pid: ${pid} (dead)` : "";
495
+ const detail2 = pidLabel || extractDetail(w);
460
496
  records.push({
461
497
  id: name,
462
498
  source: "workers",
463
- status: status2,
499
+ status: validatedStatus,
464
500
  lastActive: ts2,
465
501
  detail: detail2,
466
502
  file: file.filename
@@ -636,23 +672,57 @@ var prevFileCount = 0;
636
672
  var newExecCount = 0;
637
673
  var sessionStart = Date.now();
638
674
  var firstRender = true;
675
+ var fileCache = /* @__PURE__ */ new Map();
676
+ function getRecordsCached(f) {
677
+ const cached = fileCache.get(f.path);
678
+ if (cached && cached.mtime === f.mtime) return cached;
679
+ const records = f.ext === ".jsonl" ? processJsonlFile(f) : processJsonFile(f);
680
+ const traces = records.filter((r) => r.traceData).map((r) => r.traceData);
681
+ const entry = { mtime: f.mtime, records, traces };
682
+ fileCache.set(f.path, entry);
683
+ return entry;
684
+ }
639
685
  function render(config) {
640
686
  const files = scanFiles(config.dirs, config.recursive);
641
687
  if (files.length > prevFileCount && prevFileCount > 0) {
642
688
  newExecCount += files.length - prevFileCount;
643
689
  }
644
690
  prevFileCount = files.length;
691
+ const currentPaths = new Set(files.map((f) => f.path));
692
+ for (const key of fileCache.keys()) {
693
+ if (!currentPaths.has(key)) fileCache.delete(key);
694
+ }
645
695
  const allRecords = [];
646
696
  const allTraces = [];
647
697
  for (const f of files.slice(0, 300)) {
648
- const records = f.ext === ".jsonl" ? processJsonlFile(f) : processJsonFile(f);
649
- for (const r of records) {
650
- allRecords.push(r);
651
- if (r.traceData) allTraces.push(r.traceData);
698
+ const { records, traces } = getRecordsCached(f);
699
+ for (const r of records) allRecords.push(r);
700
+ for (const t of traces) allTraces.push(t);
701
+ }
702
+ const deduped = [];
703
+ const seenAgents = /* @__PURE__ */ new Map();
704
+ for (const r of allRecords) {
705
+ if (r.source === "jobs" || r.source === "workers" || r.source === "state") {
706
+ deduped.push(r);
707
+ continue;
708
+ }
709
+ const key = `${r.source}:${r.id}`;
710
+ const existing = seenAgents.get(key);
711
+ if (!existing || r.lastActive > existing.lastActive) {
712
+ seenAgents.set(key, r);
652
713
  }
653
714
  }
715
+ for (const r of seenAgents.values()) deduped.push(r);
716
+ const STALE_THRESHOLD_MS = 2 * 60 * 60 * 1e3;
717
+ const now = Date.now();
718
+ const filtered = deduped.filter((r) => {
719
+ if (r.source === "jobs" || r.source === "workers") return true;
720
+ if (r.status === "running") return true;
721
+ return now - r.lastActive < STALE_THRESHOLD_MS;
722
+ });
723
+ const activeRecords = filtered;
654
724
  const byFile = /* @__PURE__ */ new Map();
655
- for (const r of allRecords) {
725
+ for (const r of activeRecords) {
656
726
  const arr = byFile.get(r.file) ?? [];
657
727
  arr.push(r);
658
728
  byFile.set(r.file, arr);
@@ -699,15 +769,14 @@ function render(config) {
699
769
  }
700
770
  }
701
771
  groups.sort((a, b) => b.lastTs - a.lastTs);
702
- const totExec = allRecords.length;
703
- const totFail = allRecords.filter((r) => r.status === "error").length;
704
- const totRunning = allRecords.filter((r) => r.status === "running").length;
705
- const uniqueAgents = new Set(allRecords.map((r) => r.id)).size;
772
+ const totExec = activeRecords.length;
773
+ const totFail = activeRecords.filter((r) => r.status === "error").length;
774
+ const totRunning = activeRecords.filter((r) => r.status === "running").length;
775
+ const uniqueAgents = new Set(activeRecords.map((r) => r.id)).size;
706
776
  const sysRate = totExec > 0 ? ((totExec - totFail) / totExec * 100).toFixed(1) : "100.0";
707
- const now = Date.now();
708
777
  const buckets = new Array(12).fill(0);
709
778
  const failBuckets = new Array(12).fill(0);
710
- for (const r of allRecords) {
779
+ for (const r of activeRecords) {
711
780
  const age = now - r.lastActive;
712
781
  if (age > 36e5 || age < 0) continue;
713
782
  const idx = 11 - Math.floor(age / 3e5);
@@ -881,7 +950,7 @@ function render(config) {
881
950
  }
882
951
  }
883
952
  }
884
- const recentRecords = allRecords.filter((r) => r.lastActive > 0).sort((a, b) => b.lastActive - a.lastActive).slice(0, 6);
953
+ const recentRecords = activeRecords.filter((r) => r.lastActive > 0).sort((a, b) => b.lastActive - a.lastActive).slice(0, 6);
885
954
  if (recentRecords.length > 0) {
886
955
  writeLine(L, "");
887
956
  writeLine(L, ` ${C.bold}${C.under}Recent Activity${C.reset}`);
package/dist/cli.cjs CHANGED
@@ -98,6 +98,7 @@ var import_path3 = require("path");
98
98
  // src/live.ts
99
99
  var import_node_fs = require("fs");
100
100
  var import_node_path = require("path");
101
+ var import_node_child_process = require("child_process");
101
102
 
102
103
  // src/graph-query.ts
103
104
  function getChildren(graph, nodeId) {
@@ -311,11 +312,29 @@ Examples:
311
312
  `.trim()
312
313
  );
313
314
  }
315
+ var dirMtimeCache = /* @__PURE__ */ new Map();
316
+ var dirFileCache = /* @__PURE__ */ new Map();
314
317
  function scanFiles(dirs, recursive) {
315
318
  const results = [];
316
319
  const seen = /* @__PURE__ */ new Set();
317
320
  function scanDir(d, topLevel) {
318
321
  try {
322
+ const dirStat = (0, import_node_fs.statSync)(d);
323
+ const dirMtime = dirStat.mtime.getTime();
324
+ const cachedMtime = dirMtimeCache.get(d);
325
+ if (cachedMtime === dirMtime) {
326
+ const cached = dirFileCache.get(d);
327
+ if (cached) {
328
+ for (const f of cached) {
329
+ if (!seen.has(f.path)) {
330
+ seen.add(f.path);
331
+ results.push(f);
332
+ }
333
+ }
334
+ return;
335
+ }
336
+ }
337
+ const dirResults = [];
319
338
  for (const f of (0, import_node_fs.readdirSync)(d)) {
320
339
  if (f.startsWith(".")) continue;
321
340
  const fp = (0, import_node_path.join)(d, f);
@@ -333,12 +352,18 @@ function scanFiles(dirs, recursive) {
333
352
  if (!stat.isFile()) continue;
334
353
  if (f.endsWith(".json")) {
335
354
  seen.add(fp);
336
- results.push({ filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".json" });
355
+ const entry = { filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".json" };
356
+ results.push(entry);
357
+ dirResults.push(entry);
337
358
  } else if (f.endsWith(".jsonl")) {
338
359
  seen.add(fp);
339
- results.push({ filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".jsonl" });
360
+ const entry = { filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".jsonl" };
361
+ results.push(entry);
362
+ dirResults.push(entry);
340
363
  }
341
364
  }
365
+ dirMtimeCache.set(d, dirMtime);
366
+ dirFileCache.set(d, dirResults);
342
367
  } catch {
343
368
  }
344
369
  }
@@ -491,11 +516,22 @@ function processJsonFile(file) {
491
516
  const status2 = findStatus(w);
492
517
  const ts2 = findTimestamp(w) || findTimestamp(obj) || file.mtime;
493
518
  const pid = w.pid;
494
- const detail2 = pid ? `pid: ${pid}` : extractDetail(w);
519
+ let validatedStatus = status2;
520
+ let pidAlive = true;
521
+ if (pid && (status2 === "running" || status2 === "ok")) {
522
+ try {
523
+ (0, import_node_child_process.execSync)(`kill -0 ${pid} 2>/dev/null`, { stdio: "ignore" });
524
+ } catch {
525
+ pidAlive = false;
526
+ validatedStatus = "error";
527
+ }
528
+ }
529
+ const pidLabel = pid ? pidAlive ? `pid: ${pid}` : `pid: ${pid} (dead)` : "";
530
+ const detail2 = pidLabel || extractDetail(w);
495
531
  records.push({
496
532
  id: name,
497
533
  source: "workers",
498
- status: status2,
534
+ status: validatedStatus,
499
535
  lastActive: ts2,
500
536
  detail: detail2,
501
537
  file: file.filename
@@ -671,23 +707,57 @@ var prevFileCount = 0;
671
707
  var newExecCount = 0;
672
708
  var sessionStart = Date.now();
673
709
  var firstRender = true;
710
+ var fileCache = /* @__PURE__ */ new Map();
711
+ function getRecordsCached(f) {
712
+ const cached = fileCache.get(f.path);
713
+ if (cached && cached.mtime === f.mtime) return cached;
714
+ const records = f.ext === ".jsonl" ? processJsonlFile(f) : processJsonFile(f);
715
+ const traces = records.filter((r) => r.traceData).map((r) => r.traceData);
716
+ const entry = { mtime: f.mtime, records, traces };
717
+ fileCache.set(f.path, entry);
718
+ return entry;
719
+ }
674
720
  function render(config) {
675
721
  const files = scanFiles(config.dirs, config.recursive);
676
722
  if (files.length > prevFileCount && prevFileCount > 0) {
677
723
  newExecCount += files.length - prevFileCount;
678
724
  }
679
725
  prevFileCount = files.length;
726
+ const currentPaths = new Set(files.map((f) => f.path));
727
+ for (const key of fileCache.keys()) {
728
+ if (!currentPaths.has(key)) fileCache.delete(key);
729
+ }
680
730
  const allRecords = [];
681
731
  const allTraces = [];
682
732
  for (const f of files.slice(0, 300)) {
683
- const records = f.ext === ".jsonl" ? processJsonlFile(f) : processJsonFile(f);
684
- for (const r of records) {
685
- allRecords.push(r);
686
- if (r.traceData) allTraces.push(r.traceData);
733
+ const { records, traces } = getRecordsCached(f);
734
+ for (const r of records) allRecords.push(r);
735
+ for (const t of traces) allTraces.push(t);
736
+ }
737
+ const deduped = [];
738
+ const seenAgents = /* @__PURE__ */ new Map();
739
+ for (const r of allRecords) {
740
+ if (r.source === "jobs" || r.source === "workers" || r.source === "state") {
741
+ deduped.push(r);
742
+ continue;
743
+ }
744
+ const key = `${r.source}:${r.id}`;
745
+ const existing = seenAgents.get(key);
746
+ if (!existing || r.lastActive > existing.lastActive) {
747
+ seenAgents.set(key, r);
687
748
  }
688
749
  }
750
+ for (const r of seenAgents.values()) deduped.push(r);
751
+ const STALE_THRESHOLD_MS = 2 * 60 * 60 * 1e3;
752
+ const now = Date.now();
753
+ const filtered = deduped.filter((r) => {
754
+ if (r.source === "jobs" || r.source === "workers") return true;
755
+ if (r.status === "running") return true;
756
+ return now - r.lastActive < STALE_THRESHOLD_MS;
757
+ });
758
+ const activeRecords = filtered;
689
759
  const byFile = /* @__PURE__ */ new Map();
690
- for (const r of allRecords) {
760
+ for (const r of activeRecords) {
691
761
  const arr = byFile.get(r.file) ?? [];
692
762
  arr.push(r);
693
763
  byFile.set(r.file, arr);
@@ -734,15 +804,14 @@ function render(config) {
734
804
  }
735
805
  }
736
806
  groups.sort((a, b) => b.lastTs - a.lastTs);
737
- const totExec = allRecords.length;
738
- const totFail = allRecords.filter((r) => r.status === "error").length;
739
- const totRunning = allRecords.filter((r) => r.status === "running").length;
740
- const uniqueAgents = new Set(allRecords.map((r) => r.id)).size;
807
+ const totExec = activeRecords.length;
808
+ const totFail = activeRecords.filter((r) => r.status === "error").length;
809
+ const totRunning = activeRecords.filter((r) => r.status === "running").length;
810
+ const uniqueAgents = new Set(activeRecords.map((r) => r.id)).size;
741
811
  const sysRate = totExec > 0 ? ((totExec - totFail) / totExec * 100).toFixed(1) : "100.0";
742
- const now = Date.now();
743
812
  const buckets = new Array(12).fill(0);
744
813
  const failBuckets = new Array(12).fill(0);
745
- for (const r of allRecords) {
814
+ for (const r of activeRecords) {
746
815
  const age = now - r.lastActive;
747
816
  if (age > 36e5 || age < 0) continue;
748
817
  const idx = 11 - Math.floor(age / 3e5);
@@ -916,7 +985,7 @@ function render(config) {
916
985
  }
917
986
  }
918
987
  }
919
- const recentRecords = allRecords.filter((r) => r.lastActive > 0).sort((a, b) => b.lastActive - a.lastActive).slice(0, 6);
988
+ const recentRecords = activeRecords.filter((r) => r.lastActive > 0).sort((a, b) => b.lastActive - a.lastActive).slice(0, 6);
920
989
  if (recentRecords.length > 0) {
921
990
  writeLine(L, "");
922
991
  writeLine(L, ` ${C.bold}${C.under}Recent Activity${C.reset}`);
@@ -982,7 +1051,7 @@ function startLive(argv) {
982
1051
  }
983
1052
 
984
1053
  // src/runner.ts
985
- var import_node_child_process = require("child_process");
1054
+ var import_node_child_process2 = require("child_process");
986
1055
  var import_node_fs2 = require("fs");
987
1056
  var import_node_path2 = require("path");
988
1057
 
@@ -1286,7 +1355,7 @@ async function runTraced(config) {
1286
1355
  const execArgs = command.slice(1);
1287
1356
  process.env.AGENTFLOW_TRACE_ID = traceId;
1288
1357
  process.env.AGENTFLOW_PARENT_SPAN_ID = spanId;
1289
- const result = (0, import_node_child_process.spawnSync)(execCmd, execArgs, { stdio: "inherit" });
1358
+ const result = (0, import_node_child_process2.spawnSync)(execCmd, execArgs, { stdio: "inherit" });
1290
1359
  delete process.env.AGENTFLOW_TRACE_ID;
1291
1360
  delete process.env.AGENTFLOW_PARENT_SPAN_ID;
1292
1361
  const exitCode = result.status ?? 1;
@@ -1792,7 +1861,7 @@ var import_node_os = require("os");
1792
1861
  var import_node_path3 = require("path");
1793
1862
 
1794
1863
  // src/watch-alerts.ts
1795
- var import_node_child_process2 = require("child_process");
1864
+ var import_node_child_process3 = require("child_process");
1796
1865
  var import_node_http = require("http");
1797
1866
  var import_node_https = require("https");
1798
1867
  function formatAlertMessage(payload) {
@@ -1902,7 +1971,7 @@ function sendCommand(payload, cmd) {
1902
1971
  AGENTFLOW_ALERT_FILE: payload.file,
1903
1972
  AGENTFLOW_ALERT_TIMESTAMP: String(payload.timestamp)
1904
1973
  };
1905
- (0, import_node_child_process2.exec)(cmd, { env, timeout: 3e4 }, (err) => {
1974
+ (0, import_node_child_process3.exec)(cmd, { env, timeout: 3e4 }, (err) => {
1906
1975
  if (err) reject(err);
1907
1976
  else resolve6();
1908
1977
  });
package/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  startWatch,
7
7
  toAsciiTree,
8
8
  toTimeline
9
- } from "./chunk-DHCTDCDI.js";
9
+ } from "./chunk-LPTCBS77.js";
10
10
  import "./chunk-DY7YHFIB.js";
11
11
 
12
12
  // src/cli.ts
package/dist/index.cjs CHANGED
@@ -638,6 +638,7 @@ function withGuards(builder, config) {
638
638
  // src/live.ts
639
639
  var import_node_fs = require("fs");
640
640
  var import_node_path = require("path");
641
+ var import_node_child_process = require("child_process");
641
642
 
642
643
  // src/loader.ts
643
644
  function toNodesMap(raw) {
@@ -759,11 +760,29 @@ Examples:
759
760
  `.trim()
760
761
  );
761
762
  }
763
+ var dirMtimeCache = /* @__PURE__ */ new Map();
764
+ var dirFileCache = /* @__PURE__ */ new Map();
762
765
  function scanFiles(dirs, recursive) {
763
766
  const results = [];
764
767
  const seen = /* @__PURE__ */ new Set();
765
768
  function scanDir(d, topLevel) {
766
769
  try {
770
+ const dirStat = (0, import_node_fs.statSync)(d);
771
+ const dirMtime = dirStat.mtime.getTime();
772
+ const cachedMtime = dirMtimeCache.get(d);
773
+ if (cachedMtime === dirMtime) {
774
+ const cached = dirFileCache.get(d);
775
+ if (cached) {
776
+ for (const f of cached) {
777
+ if (!seen.has(f.path)) {
778
+ seen.add(f.path);
779
+ results.push(f);
780
+ }
781
+ }
782
+ return;
783
+ }
784
+ }
785
+ const dirResults = [];
767
786
  for (const f of (0, import_node_fs.readdirSync)(d)) {
768
787
  if (f.startsWith(".")) continue;
769
788
  const fp = (0, import_node_path.join)(d, f);
@@ -781,12 +800,18 @@ function scanFiles(dirs, recursive) {
781
800
  if (!stat.isFile()) continue;
782
801
  if (f.endsWith(".json")) {
783
802
  seen.add(fp);
784
- results.push({ filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".json" });
803
+ const entry = { filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".json" };
804
+ results.push(entry);
805
+ dirResults.push(entry);
785
806
  } else if (f.endsWith(".jsonl")) {
786
807
  seen.add(fp);
787
- results.push({ filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".jsonl" });
808
+ const entry = { filename: f, path: fp, mtime: stat.mtime.getTime(), ext: ".jsonl" };
809
+ results.push(entry);
810
+ dirResults.push(entry);
788
811
  }
789
812
  }
813
+ dirMtimeCache.set(d, dirMtime);
814
+ dirFileCache.set(d, dirResults);
790
815
  } catch {
791
816
  }
792
817
  }
@@ -939,11 +964,22 @@ function processJsonFile(file) {
939
964
  const status2 = findStatus(w);
940
965
  const ts2 = findTimestamp(w) || findTimestamp(obj) || file.mtime;
941
966
  const pid = w.pid;
942
- const detail2 = pid ? `pid: ${pid}` : extractDetail(w);
967
+ let validatedStatus = status2;
968
+ let pidAlive = true;
969
+ if (pid && (status2 === "running" || status2 === "ok")) {
970
+ try {
971
+ (0, import_node_child_process.execSync)(`kill -0 ${pid} 2>/dev/null`, { stdio: "ignore" });
972
+ } catch {
973
+ pidAlive = false;
974
+ validatedStatus = "error";
975
+ }
976
+ }
977
+ const pidLabel = pid ? pidAlive ? `pid: ${pid}` : `pid: ${pid} (dead)` : "";
978
+ const detail2 = pidLabel || extractDetail(w);
943
979
  records.push({
944
980
  id: name,
945
981
  source: "workers",
946
- status: status2,
982
+ status: validatedStatus,
947
983
  lastActive: ts2,
948
984
  detail: detail2,
949
985
  file: file.filename
@@ -1119,23 +1155,57 @@ var prevFileCount = 0;
1119
1155
  var newExecCount = 0;
1120
1156
  var sessionStart = Date.now();
1121
1157
  var firstRender = true;
1158
+ var fileCache = /* @__PURE__ */ new Map();
1159
+ function getRecordsCached(f) {
1160
+ const cached = fileCache.get(f.path);
1161
+ if (cached && cached.mtime === f.mtime) return cached;
1162
+ const records = f.ext === ".jsonl" ? processJsonlFile(f) : processJsonFile(f);
1163
+ const traces = records.filter((r) => r.traceData).map((r) => r.traceData);
1164
+ const entry = { mtime: f.mtime, records, traces };
1165
+ fileCache.set(f.path, entry);
1166
+ return entry;
1167
+ }
1122
1168
  function render(config) {
1123
1169
  const files = scanFiles(config.dirs, config.recursive);
1124
1170
  if (files.length > prevFileCount && prevFileCount > 0) {
1125
1171
  newExecCount += files.length - prevFileCount;
1126
1172
  }
1127
1173
  prevFileCount = files.length;
1174
+ const currentPaths = new Set(files.map((f) => f.path));
1175
+ for (const key of fileCache.keys()) {
1176
+ if (!currentPaths.has(key)) fileCache.delete(key);
1177
+ }
1128
1178
  const allRecords = [];
1129
1179
  const allTraces = [];
1130
1180
  for (const f of files.slice(0, 300)) {
1131
- const records = f.ext === ".jsonl" ? processJsonlFile(f) : processJsonFile(f);
1132
- for (const r of records) {
1133
- allRecords.push(r);
1134
- if (r.traceData) allTraces.push(r.traceData);
1181
+ const { records, traces } = getRecordsCached(f);
1182
+ for (const r of records) allRecords.push(r);
1183
+ for (const t of traces) allTraces.push(t);
1184
+ }
1185
+ const deduped = [];
1186
+ const seenAgents = /* @__PURE__ */ new Map();
1187
+ for (const r of allRecords) {
1188
+ if (r.source === "jobs" || r.source === "workers" || r.source === "state") {
1189
+ deduped.push(r);
1190
+ continue;
1191
+ }
1192
+ const key = `${r.source}:${r.id}`;
1193
+ const existing = seenAgents.get(key);
1194
+ if (!existing || r.lastActive > existing.lastActive) {
1195
+ seenAgents.set(key, r);
1135
1196
  }
1136
1197
  }
1198
+ for (const r of seenAgents.values()) deduped.push(r);
1199
+ const STALE_THRESHOLD_MS = 2 * 60 * 60 * 1e3;
1200
+ const now = Date.now();
1201
+ const filtered = deduped.filter((r) => {
1202
+ if (r.source === "jobs" || r.source === "workers") return true;
1203
+ if (r.status === "running") return true;
1204
+ return now - r.lastActive < STALE_THRESHOLD_MS;
1205
+ });
1206
+ const activeRecords = filtered;
1137
1207
  const byFile = /* @__PURE__ */ new Map();
1138
- for (const r of allRecords) {
1208
+ for (const r of activeRecords) {
1139
1209
  const arr = byFile.get(r.file) ?? [];
1140
1210
  arr.push(r);
1141
1211
  byFile.set(r.file, arr);
@@ -1182,15 +1252,14 @@ function render(config) {
1182
1252
  }
1183
1253
  }
1184
1254
  groups.sort((a, b) => b.lastTs - a.lastTs);
1185
- const totExec = allRecords.length;
1186
- const totFail = allRecords.filter((r) => r.status === "error").length;
1187
- const totRunning = allRecords.filter((r) => r.status === "running").length;
1188
- const uniqueAgents = new Set(allRecords.map((r) => r.id)).size;
1255
+ const totExec = activeRecords.length;
1256
+ const totFail = activeRecords.filter((r) => r.status === "error").length;
1257
+ const totRunning = activeRecords.filter((r) => r.status === "running").length;
1258
+ const uniqueAgents = new Set(activeRecords.map((r) => r.id)).size;
1189
1259
  const sysRate = totExec > 0 ? ((totExec - totFail) / totExec * 100).toFixed(1) : "100.0";
1190
- const now = Date.now();
1191
1260
  const buckets = new Array(12).fill(0);
1192
1261
  const failBuckets = new Array(12).fill(0);
1193
- for (const r of allRecords) {
1262
+ for (const r of activeRecords) {
1194
1263
  const age = now - r.lastActive;
1195
1264
  if (age > 36e5 || age < 0) continue;
1196
1265
  const idx = 11 - Math.floor(age / 3e5);
@@ -1364,7 +1433,7 @@ function render(config) {
1364
1433
  }
1365
1434
  }
1366
1435
  }
1367
- const recentRecords = allRecords.filter((r) => r.lastActive > 0).sort((a, b) => b.lastActive - a.lastActive).slice(0, 6);
1436
+ const recentRecords = activeRecords.filter((r) => r.lastActive > 0).sort((a, b) => b.lastActive - a.lastActive).slice(0, 6);
1368
1437
  if (recentRecords.length > 0) {
1369
1438
  writeLine(L, "");
1370
1439
  writeLine(L, ` ${C.bold}${C.under}Recent Activity${C.reset}`);
@@ -1430,7 +1499,7 @@ function startLive(argv) {
1430
1499
  }
1431
1500
 
1432
1501
  // src/runner.ts
1433
- var import_node_child_process = require("child_process");
1502
+ var import_node_child_process2 = require("child_process");
1434
1503
  var import_node_fs2 = require("fs");
1435
1504
  var import_node_path2 = require("path");
1436
1505
  function globToRegex(pattern) {
@@ -1505,7 +1574,7 @@ async function runTraced(config) {
1505
1574
  const execArgs = command.slice(1);
1506
1575
  process.env.AGENTFLOW_TRACE_ID = traceId;
1507
1576
  process.env.AGENTFLOW_PARENT_SPAN_ID = spanId;
1508
- const result = (0, import_node_child_process.spawnSync)(execCmd, execArgs, { stdio: "inherit" });
1577
+ const result = (0, import_node_child_process2.spawnSync)(execCmd, execArgs, { stdio: "inherit" });
1509
1578
  delete process.env.AGENTFLOW_TRACE_ID;
1510
1579
  delete process.env.AGENTFLOW_PARENT_SPAN_ID;
1511
1580
  const exitCode = result.status ?? 1;
@@ -1818,7 +1887,7 @@ var import_node_os = require("os");
1818
1887
  var import_node_path3 = require("path");
1819
1888
 
1820
1889
  // src/watch-alerts.ts
1821
- var import_node_child_process2 = require("child_process");
1890
+ var import_node_child_process3 = require("child_process");
1822
1891
  var import_node_http = require("http");
1823
1892
  var import_node_https = require("https");
1824
1893
  function formatAlertMessage(payload) {
@@ -1928,7 +1997,7 @@ function sendCommand(payload, cmd) {
1928
1997
  AGENTFLOW_ALERT_FILE: payload.file,
1929
1998
  AGENTFLOW_ALERT_TIMESTAMP: String(payload.timestamp)
1930
1999
  };
1931
- (0, import_node_child_process2.exec)(cmd, { env, timeout: 3e4 }, (err) => {
2000
+ (0, import_node_child_process3.exec)(cmd, { env, timeout: 3e4 }, (err) => {
1932
2001
  if (err) reject(err);
1933
2002
  else resolve4();
1934
2003
  });
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  stitchTrace,
21
21
  toAsciiTree,
22
22
  toTimeline
23
- } from "./chunk-DHCTDCDI.js";
23
+ } from "./chunk-LPTCBS77.js";
24
24
  import {
25
25
  graphToJson,
26
26
  loadGraph
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentflow-core",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "Monitor any AI agent system. Auto-detects failures, sends alerts. Zero config, zero dependencies.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",