agent-relay 4.0.31 → 4.0.32
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/bin/agent-relay-broker-darwin-arm64 +0 -0
- package/bin/agent-relay-broker-darwin-x64 +0 -0
- package/bin/agent-relay-broker-linux-arm64 +0 -0
- package/bin/agent-relay-broker-linux-x64 +0 -0
- package/dist/index.cjs +210 -75
- package/node_modules/@agent-relay/cloud/package.json +2 -2
- package/node_modules/@agent-relay/config/package.json +1 -1
- package/node_modules/@agent-relay/hooks/package.json +4 -4
- package/node_modules/@agent-relay/sdk/README.md +3 -0
- package/node_modules/@agent-relay/sdk/dist/relay.d.ts +16 -0
- package/node_modules/@agent-relay/sdk/dist/relay.d.ts.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/relay.js +83 -0
- package/node_modules/@agent-relay/sdk/dist/relay.js.map +1 -1
- package/node_modules/@agent-relay/sdk/package.json +2 -2
- package/node_modules/@agent-relay/telemetry/package.json +1 -1
- package/node_modules/@agent-relay/trajectory/package.json +2 -2
- package/node_modules/@agent-relay/user-directory/package.json +2 -2
- package/node_modules/@agent-relay/utils/package.json +2 -2
- package/node_modules/agent-trajectories/dist/{chunk-2XT3DOJC.js → chunk-27AQPWHK.js} +136 -72
- package/node_modules/agent-trajectories/dist/chunk-27AQPWHK.js.map +1 -0
- package/node_modules/agent-trajectories/dist/cli/index.js +135 -71
- package/node_modules/agent-trajectories/dist/cli/index.js.map +1 -1
- package/node_modules/agent-trajectories/dist/{index-thTh5iI8.d.ts → index-C7XhwsoN.d.ts} +24 -0
- package/node_modules/agent-trajectories/dist/index.d.ts +2 -2
- package/node_modules/agent-trajectories/dist/index.js +1 -1
- package/node_modules/agent-trajectories/dist/sdk/index.d.ts +1 -1
- package/node_modules/agent-trajectories/dist/sdk/index.js +1 -1
- package/node_modules/agent-trajectories/package.json +1 -1
- package/package.json +9 -9
- package/packages/cloud/package.json +2 -2
- package/packages/config/package.json +1 -1
- package/packages/hooks/package.json +4 -4
- package/packages/sdk/README.md +3 -0
- package/packages/sdk/dist/relay.d.ts +16 -0
- package/packages/sdk/dist/relay.d.ts.map +1 -1
- package/packages/sdk/dist/relay.js +83 -0
- package/packages/sdk/dist/relay.js.map +1 -1
- package/packages/sdk/package.json +2 -2
- package/packages/telemetry/package.json +1 -1
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/package.json +2 -2
- package/node_modules/agent-trajectories/dist/chunk-2XT3DOJC.js.map +0 -1
|
@@ -404,8 +404,16 @@ function abandonTrajectory(trajectory, reason) {
|
|
|
404
404
|
}
|
|
405
405
|
|
|
406
406
|
// src/storage/file.ts
|
|
407
|
+
import { randomUUID } from "crypto";
|
|
407
408
|
import { existsSync } from "fs";
|
|
408
|
-
import {
|
|
409
|
+
import {
|
|
410
|
+
mkdir,
|
|
411
|
+
readFile,
|
|
412
|
+
readdir,
|
|
413
|
+
rename,
|
|
414
|
+
unlink,
|
|
415
|
+
writeFile
|
|
416
|
+
} from "fs/promises";
|
|
409
417
|
import { join } from "path";
|
|
410
418
|
|
|
411
419
|
// src/export/markdown.ts
|
|
@@ -586,6 +594,16 @@ function getSearchPaths() {
|
|
|
586
594
|
}
|
|
587
595
|
return [join(process.cwd(), ".trajectories")];
|
|
588
596
|
}
|
|
597
|
+
var indexLocks = /* @__PURE__ */ new Map();
|
|
598
|
+
function withIndexLock(path2, task) {
|
|
599
|
+
const prev = indexLocks.get(path2) ?? Promise.resolve();
|
|
600
|
+
const next = prev.then(task, task);
|
|
601
|
+
indexLocks.set(
|
|
602
|
+
path2,
|
|
603
|
+
next.catch(() => void 0)
|
|
604
|
+
);
|
|
605
|
+
return next;
|
|
606
|
+
}
|
|
589
607
|
var FileStorage = class {
|
|
590
608
|
baseDir;
|
|
591
609
|
trajectoriesDir;
|
|
@@ -612,10 +630,10 @@ var FileStorage = class {
|
|
|
612
630
|
await mkdir(this.activeDir, { recursive: true });
|
|
613
631
|
await mkdir(this.completedDir, { recursive: true });
|
|
614
632
|
if (!existsSync(this.indexPath)) {
|
|
615
|
-
await this.
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
633
|
+
await withIndexLock(this.indexPath, async () => {
|
|
634
|
+
if (!existsSync(this.indexPath)) {
|
|
635
|
+
await this.saveIndex(this.emptyIndex());
|
|
636
|
+
}
|
|
619
637
|
});
|
|
620
638
|
}
|
|
621
639
|
await this.reconcileIndex();
|
|
@@ -643,49 +661,51 @@ var FileStorage = class {
|
|
|
643
661
|
skippedSchemaViolation: 0,
|
|
644
662
|
skippedIoError: 0
|
|
645
663
|
};
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
664
|
+
await withIndexLock(this.indexPath, async () => {
|
|
665
|
+
const index = await this.loadIndex();
|
|
666
|
+
const before = Object.keys(index.trajectories).length;
|
|
667
|
+
const discovered = [];
|
|
668
|
+
try {
|
|
669
|
+
const activeFiles = await readdir(this.activeDir);
|
|
670
|
+
for (const file of activeFiles) {
|
|
671
|
+
if (!file.endsWith(".json")) continue;
|
|
672
|
+
discovered.push(join(this.activeDir, file));
|
|
673
|
+
}
|
|
674
|
+
} catch (error) {
|
|
675
|
+
if (error.code !== "ENOENT") throw error;
|
|
654
676
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
summary.skippedIoError += 1;
|
|
677
|
+
await this.walkJsonFilesInto(this.completedDir, discovered);
|
|
678
|
+
for (const filePath of discovered) {
|
|
679
|
+
summary.scanned += 1;
|
|
680
|
+
const result = await this.readTrajectoryFile(filePath);
|
|
681
|
+
if (!result.ok) {
|
|
682
|
+
if (result.reason === "malformed_json") {
|
|
683
|
+
summary.skippedMalformedJson += 1;
|
|
684
|
+
} else if (result.reason === "schema_violation") {
|
|
685
|
+
summary.skippedSchemaViolation += 1;
|
|
686
|
+
} else {
|
|
687
|
+
summary.skippedIoError += 1;
|
|
688
|
+
}
|
|
689
|
+
continue;
|
|
669
690
|
}
|
|
670
|
-
|
|
691
|
+
const trajectory = result.trajectory;
|
|
692
|
+
if (index.trajectories[trajectory.id]) {
|
|
693
|
+
summary.alreadyIndexed += 1;
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
index.trajectories[trajectory.id] = {
|
|
697
|
+
title: trajectory.task.title,
|
|
698
|
+
status: trajectory.status,
|
|
699
|
+
startedAt: trajectory.startedAt,
|
|
700
|
+
completedAt: trajectory.completedAt,
|
|
701
|
+
path: filePath
|
|
702
|
+
};
|
|
703
|
+
summary.added += 1;
|
|
671
704
|
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
summary.alreadyIndexed += 1;
|
|
675
|
-
continue;
|
|
705
|
+
if (Object.keys(index.trajectories).length !== before) {
|
|
706
|
+
await this.saveIndex(index);
|
|
676
707
|
}
|
|
677
|
-
|
|
678
|
-
title: trajectory.task.title,
|
|
679
|
-
status: trajectory.status,
|
|
680
|
-
startedAt: trajectory.startedAt,
|
|
681
|
-
completedAt: trajectory.completedAt,
|
|
682
|
-
path: filePath
|
|
683
|
-
};
|
|
684
|
-
summary.added += 1;
|
|
685
|
-
}
|
|
686
|
-
if (Object.keys(index.trajectories).length !== before) {
|
|
687
|
-
await this.saveIndex(index);
|
|
688
|
-
}
|
|
708
|
+
});
|
|
689
709
|
const hadSkips = summary.skippedMalformedJson + summary.skippedSchemaViolation + summary.skippedIoError > 0;
|
|
690
710
|
if (summary.added > 0 || hadSkips) {
|
|
691
711
|
const parts = [`reconciled ${summary.added}/${summary.scanned}`];
|
|
@@ -888,17 +908,19 @@ var FileStorage = class {
|
|
|
888
908
|
if (existsSync(activePath)) {
|
|
889
909
|
await unlink(activePath);
|
|
890
910
|
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
911
|
+
await withIndexLock(this.indexPath, async () => {
|
|
912
|
+
const index = await this.loadIndex();
|
|
913
|
+
const entry = index.trajectories[id];
|
|
914
|
+
if (entry?.path && existsSync(entry.path)) {
|
|
915
|
+
await unlink(entry.path);
|
|
916
|
+
const mdPath = entry.path.replace(".json", ".md");
|
|
917
|
+
if (existsSync(mdPath)) {
|
|
918
|
+
await unlink(mdPath);
|
|
919
|
+
}
|
|
898
920
|
}
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
921
|
+
delete index.trajectories[id];
|
|
922
|
+
await this.saveIndex(index);
|
|
923
|
+
});
|
|
902
924
|
}
|
|
903
925
|
/**
|
|
904
926
|
* Search trajectories by text
|
|
@@ -976,10 +998,23 @@ var FileStorage = class {
|
|
|
976
998
|
const result = await this.readTrajectoryFile(path2);
|
|
977
999
|
return result.ok ? result.trajectory : null;
|
|
978
1000
|
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Read and parse the on-disk index.
|
|
1003
|
+
*
|
|
1004
|
+
* Tolerances (belt-and-braces against the read/write race):
|
|
1005
|
+
* - ENOENT: first-run, return an empty index silently.
|
|
1006
|
+
* - Empty file: a concurrent writer truncated index.json in "w" mode
|
|
1007
|
+
* right before we read. Return an empty index silently — this is
|
|
1008
|
+
* not a real corruption, just an interleaving the mutex + atomic
|
|
1009
|
+
* rename should already prevent. Logging here would be noise.
|
|
1010
|
+
* - Non-empty but malformed JSON: genuinely corrupted on disk (hand
|
|
1011
|
+
* edit, disk error, etc). Log it and return an empty index so the
|
|
1012
|
+
* caller can recover, but keep the log so the problem is visible.
|
|
1013
|
+
*/
|
|
979
1014
|
async loadIndex() {
|
|
1015
|
+
let content;
|
|
980
1016
|
try {
|
|
981
|
-
|
|
982
|
-
return JSON.parse(content);
|
|
1017
|
+
content = await readFile(this.indexPath, "utf-8");
|
|
983
1018
|
} catch (error) {
|
|
984
1019
|
if (error.code !== "ENOENT") {
|
|
985
1020
|
console.error(
|
|
@@ -987,27 +1022,56 @@ var FileStorage = class {
|
|
|
987
1022
|
error
|
|
988
1023
|
);
|
|
989
1024
|
}
|
|
990
|
-
return
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1025
|
+
return this.emptyIndex();
|
|
1026
|
+
}
|
|
1027
|
+
if (content.length === 0) {
|
|
1028
|
+
return this.emptyIndex();
|
|
1029
|
+
}
|
|
1030
|
+
try {
|
|
1031
|
+
return JSON.parse(content);
|
|
1032
|
+
} catch (error) {
|
|
1033
|
+
console.error(
|
|
1034
|
+
"Error loading trajectory index, using empty index:",
|
|
1035
|
+
error
|
|
1036
|
+
);
|
|
1037
|
+
return this.emptyIndex();
|
|
995
1038
|
}
|
|
996
1039
|
}
|
|
1040
|
+
emptyIndex() {
|
|
1041
|
+
return {
|
|
1042
|
+
version: 1,
|
|
1043
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1044
|
+
trajectories: {}
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
/**
|
|
1048
|
+
* Atomic write: stage into a process-unique temp path in the same directory
|
|
1049
|
+
* and then rename over the live file. `rename` is atomic on POSIX, so
|
|
1050
|
+
* concurrent readers in any process either see the old complete file or
|
|
1051
|
+
* the new complete file — never a half-written / zero-byte state.
|
|
1052
|
+
*
|
|
1053
|
+
* Callers MUST hold `withIndexLock(this.indexPath, ...)` so the in-process
|
|
1054
|
+
* read-modify-write cycle stays serialized; the unique temp name also keeps
|
|
1055
|
+
* parallel writers in other processes from colliding on a shared tmp path.
|
|
1056
|
+
*/
|
|
997
1057
|
async saveIndex(index) {
|
|
998
1058
|
index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
999
|
-
|
|
1059
|
+
const tmpPath = `${this.indexPath}.${process.pid}.${randomUUID()}.tmp`;
|
|
1060
|
+
await writeFile(tmpPath, JSON.stringify(index, null, 2), "utf-8");
|
|
1061
|
+
await rename(tmpPath, this.indexPath);
|
|
1000
1062
|
}
|
|
1001
1063
|
async updateIndex(trajectory, filePath) {
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1064
|
+
await withIndexLock(this.indexPath, async () => {
|
|
1065
|
+
const index = await this.loadIndex();
|
|
1066
|
+
index.trajectories[trajectory.id] = {
|
|
1067
|
+
title: trajectory.task.title,
|
|
1068
|
+
status: trajectory.status,
|
|
1069
|
+
startedAt: trajectory.startedAt,
|
|
1070
|
+
completedAt: trajectory.completedAt,
|
|
1071
|
+
path: filePath
|
|
1072
|
+
};
|
|
1073
|
+
await this.saveIndex(index);
|
|
1074
|
+
});
|
|
1011
1075
|
}
|
|
1012
1076
|
};
|
|
1013
1077
|
|