agent-trajectories 0.6.0 → 0.6.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.
@@ -1,8 +1,157 @@
1
1
  // src/sdk/client.ts
2
2
  import { spawn } from "child_process";
3
- import { existsSync as existsSync2, readFileSync } from "fs";
3
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
4
4
  import { createRequire } from "module";
5
- import { dirname as dirname2, resolve as resolvePath } from "path";
5
+ import { dirname as dirname3, resolve as resolvePath } from "path";
6
+
7
+ // src/core/project-id.ts
8
+ import { execFileSync } from "child_process";
9
+ import { existsSync, readFileSync } from "fs";
10
+ import { dirname, join, resolve } from "path";
11
+ function resolveProjectId(explicitProjectId, options = {}) {
12
+ return readString(explicitProjectId) ?? readString((options.env ?? process.env).TRAJECTORIES_PROJECT) ?? resolveDefaultProjectId(options.cwd);
13
+ }
14
+ function resolveDefaultProjectId(cwd = process.cwd()) {
15
+ const packageJson = readNearestPackageJson(cwd);
16
+ return resolvePackageRepositoryId(packageJson) ?? resolveGitRemoteProjectId(cwd) ?? resolvePackageName(packageJson);
17
+ }
18
+ function normalizeRepositoryId(value) {
19
+ const raw = readString(value);
20
+ if (!raw) {
21
+ return void 0;
22
+ }
23
+ const withoutGitPrefix = raw.replace(/^git\+/, "");
24
+ const shorthand = withoutGitPrefix.match(
25
+ /^(?:github|gitlab|bitbucket):([^/]+\/[^/]+(?:\/[^/]+)*)$/
26
+ );
27
+ if (shorthand) {
28
+ return cleanRepositoryPath(shorthand[1]);
29
+ }
30
+ const ownerRepo = withoutGitPrefix.match(
31
+ /^([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)$/
32
+ );
33
+ if (ownerRepo) {
34
+ return cleanRepositoryPath(ownerRepo[1]);
35
+ }
36
+ const sshUrlScpLike = withoutGitPrefix.match(
37
+ /^ssh:\/\/[^@/\s]+@[^:/\s]+:(.+)$/i
38
+ );
39
+ if (sshUrlScpLike) {
40
+ return cleanRepositoryPath(sshUrlScpLike[1]);
41
+ }
42
+ const scpLike = withoutGitPrefix.match(/^[^@/\s]+@[^:/\s]+:(.+)$/);
43
+ if (scpLike) {
44
+ return cleanRepositoryPath(scpLike[1]);
45
+ }
46
+ let parsed;
47
+ try {
48
+ parsed = new URL(withoutGitPrefix);
49
+ } catch {
50
+ return void 0;
51
+ }
52
+ if (!["https:", "http:", "ssh:", "git:"].includes(parsed.protocol)) {
53
+ return void 0;
54
+ }
55
+ return cleanRepositoryPath(parsed.pathname);
56
+ }
57
+ function resolvePackageRepositoryId(packageJson) {
58
+ if (!packageJson) {
59
+ return void 0;
60
+ }
61
+ const repository = packageJson.repository;
62
+ if (typeof repository === "string") {
63
+ return normalizeRepositoryId(repository);
64
+ }
65
+ if (isRepositoryObject(repository) && typeof repository.url === "string") {
66
+ const projectId = normalizeRepositoryId(repository.url);
67
+ const directory = cleanRepositoryDirectory(repository.directory);
68
+ return directory && projectId ? `${projectId}//${directory}` : projectId;
69
+ }
70
+ return void 0;
71
+ }
72
+ function resolvePackageName(packageJson) {
73
+ return readString(packageJson?.name);
74
+ }
75
+ function resolveGitRemoteProjectId(cwd) {
76
+ for (const remote of ["upstream", "origin"]) {
77
+ const remoteUrl = getGitRemoteUrl(cwd, remote);
78
+ if (!remoteUrl) {
79
+ continue;
80
+ }
81
+ const projectId = normalizeRepositoryId(remoteUrl);
82
+ if (projectId) {
83
+ return projectId;
84
+ }
85
+ }
86
+ return void 0;
87
+ }
88
+ function getGitRemoteUrl(cwd, remote) {
89
+ try {
90
+ return readString(
91
+ execFileSync("git", ["config", "--get", `remote.${remote}.url`], {
92
+ cwd,
93
+ encoding: "utf-8",
94
+ stdio: ["ignore", "pipe", "ignore"]
95
+ })
96
+ );
97
+ } catch {
98
+ return void 0;
99
+ }
100
+ }
101
+ function readNearestPackageJson(cwd) {
102
+ let current = resolve(cwd);
103
+ while (true) {
104
+ const candidate = join(current, "package.json");
105
+ if (existsSync(candidate)) {
106
+ try {
107
+ const parsed = JSON.parse(readFileSync(candidate, "utf-8"));
108
+ if (isPackageJson(parsed)) {
109
+ return parsed;
110
+ }
111
+ } catch {
112
+ }
113
+ }
114
+ const parent = dirname(current);
115
+ if (parent === current) {
116
+ return void 0;
117
+ }
118
+ current = parent;
119
+ }
120
+ }
121
+ function cleanRepositoryPath(path) {
122
+ const withoutQuery = path.split(/[?#]/, 1)[0] ?? "";
123
+ const withoutSlashes = withoutQuery.replace(/^\/+|\/+$/g, "");
124
+ const withoutGitSuffix = withoutSlashes.replace(/\.git$/i, "");
125
+ const parts = withoutGitSuffix.split("/").map((part) => part.trim()).filter(Boolean);
126
+ if (parts.length < 2) {
127
+ return void 0;
128
+ }
129
+ return parts.join("/");
130
+ }
131
+ function cleanRepositoryDirectory(directory) {
132
+ const raw = readString(directory);
133
+ if (!raw || raw.startsWith("/") || raw.startsWith("\\") || /^[A-Za-z]:[\\/]/.test(raw)) {
134
+ return void 0;
135
+ }
136
+ const parts = raw.split(/[\\/]/).map((part) => part.trim()).filter(Boolean);
137
+ if (parts.length === 0 || parts.some((part) => part === "." || part === "..")) {
138
+ return void 0;
139
+ }
140
+ return parts.join("/");
141
+ }
142
+ function readString(value) {
143
+ if (typeof value !== "string") {
144
+ return void 0;
145
+ }
146
+ const trimmed = value.trim();
147
+ return trimmed.length > 0 ? trimmed : void 0;
148
+ }
149
+ function isPackageJson(value) {
150
+ return value !== null && typeof value === "object" && !Array.isArray(value);
151
+ }
152
+ function isRepositoryObject(value) {
153
+ return value !== null && typeof value === "object" && !Array.isArray(value);
154
+ }
6
155
 
7
156
  // src/core/id.ts
8
157
  import { webcrypto } from "crypto";
@@ -269,7 +418,7 @@ function createTrajectory(input) {
269
418
  chapters: [],
270
419
  commits: [],
271
420
  filesChanged: [],
272
- projectId: input.projectId ?? process.cwd(),
421
+ projectId: input.projectId,
273
422
  tags: input.tags ?? []
274
423
  };
275
424
  }
@@ -666,7 +815,7 @@ function formatTime(isoString) {
666
815
  }
667
816
 
668
817
  // src/storage/file.ts
669
- import { existsSync } from "fs";
818
+ import { existsSync as existsSync2 } from "fs";
670
819
  import {
671
820
  mkdir,
672
821
  readFile,
@@ -677,27 +826,27 @@ import {
677
826
  } from "fs/promises";
678
827
  import {
679
828
  basename,
680
- dirname,
829
+ dirname as dirname2,
681
830
  isAbsolute,
682
- join,
831
+ join as join2,
683
832
  relative,
684
- resolve
833
+ resolve as resolve2
685
834
  } from "path";
686
835
  var TRAJECTORY_FILE = "trajectory.json";
687
836
  var SUMMARY_FILE = "summary.md";
688
837
  var COMPACTION_FILE = "compaction.json";
689
838
  var LEGACY_COMPACTION_SUFFIX = ".compaction.json";
690
- var DEFAULT_TRAJECTORY_DATA_DIR = join(
839
+ var DEFAULT_TRAJECTORY_DATA_DIR = join2(
691
840
  ".agentworkforce",
692
841
  "trajectories"
693
842
  );
694
843
  var LEGACY_TRAJECTORY_DATA_DIR = ".trajectories";
695
844
  function getDefaultTrajectoryDataDir(baseDir = process.cwd()) {
696
- return join(baseDir, DEFAULT_TRAJECTORY_DATA_DIR);
845
+ return join2(baseDir, DEFAULT_TRAJECTORY_DATA_DIR);
697
846
  }
698
847
  function expandPath(path) {
699
848
  if (path.startsWith("~")) {
700
- return join(process.env.HOME ?? "", path.slice(1));
849
+ return join2(process.env.HOME ?? "", path.slice(1));
701
850
  }
702
851
  return path;
703
852
  }
@@ -731,8 +880,8 @@ var FileStorage = class {
731
880
  this.trajectoriesDir = getDefaultTrajectoryDataDir(this.baseDir);
732
881
  this.shouldMigrateLegacyDefault = true;
733
882
  }
734
- this.activeDir = join(this.trajectoriesDir, "active");
735
- this.completedDir = join(this.trajectoriesDir, "completed");
883
+ this.activeDir = join2(this.trajectoriesDir, "active");
884
+ this.completedDir = join2(this.trajectoriesDir, "completed");
736
885
  }
737
886
  /**
738
887
  * Initialize storage directories
@@ -743,18 +892,18 @@ var FileStorage = class {
743
892
  await mkdir(this.activeDir, { recursive: true });
744
893
  await mkdir(this.completedDir, { recursive: true });
745
894
  await this.migrateLegacyIndexCompactionMarkers();
746
- await rm(join(this.trajectoriesDir, "index.json"), { force: true });
895
+ await rm(join2(this.trajectoriesDir, "index.json"), { force: true });
747
896
  await this.reconcileIndex();
748
897
  }
749
898
  async migrateLegacyDefaultDir() {
750
899
  if (!this.shouldMigrateLegacyDefault) {
751
900
  return;
752
901
  }
753
- const legacyDir = join(this.baseDir, LEGACY_TRAJECTORY_DATA_DIR);
754
- if (!existsSync(legacyDir) || existsSync(this.trajectoriesDir)) {
902
+ const legacyDir = join2(this.baseDir, LEGACY_TRAJECTORY_DATA_DIR);
903
+ if (!existsSync2(legacyDir) || existsSync2(this.trajectoriesDir)) {
755
904
  return;
756
905
  }
757
- await mkdir(dirname(this.trajectoriesDir), { recursive: true });
906
+ await mkdir(dirname2(this.trajectoriesDir), { recursive: true });
758
907
  await rename(legacyDir, this.trajectoriesDir);
759
908
  }
760
909
  /**
@@ -837,7 +986,7 @@ var FileStorage = class {
837
986
  */
838
987
  async quarantineInvalid() {
839
988
  const summary = await this.reconcileIndex();
840
- const targetDir = join(this.trajectoriesDir, "invalid");
989
+ const targetDir = join2(this.trajectoriesDir, "invalid");
841
990
  const candidates = summary.failures.filter((f) => f.reason !== "io_error");
842
991
  if (candidates.length === 0) {
843
992
  return { moved: [], targetDir };
@@ -847,7 +996,7 @@ var FileStorage = class {
847
996
  for (const failure of candidates) {
848
997
  const dest = await this.resolveQuarantineDest(failure.path, targetDir);
849
998
  try {
850
- await mkdir(dirname(dest), { recursive: true });
999
+ await mkdir(dirname2(dest), { recursive: true });
851
1000
  await rename(failure.path, dest);
852
1001
  moved.push(failure);
853
1002
  } catch (error) {
@@ -873,13 +1022,13 @@ var FileStorage = class {
873
1022
  async resolveQuarantineDest(sourcePath, targetDir) {
874
1023
  const rel = relative(this.trajectoriesDir, sourcePath);
875
1024
  const safeRel = rel && !rel.startsWith("..") && !isAbsolute(rel) ? rel : basename(sourcePath);
876
- let dest = join(targetDir, safeRel);
877
- if (!existsSync(dest)) return dest;
1025
+ let dest = join2(targetDir, safeRel);
1026
+ if (!existsSync2(dest)) return dest;
878
1027
  const ext = safeRel.endsWith(".json") ? ".json" : "";
879
1028
  const stem = ext ? safeRel.slice(0, -ext.length) : safeRel;
880
1029
  for (let i = 1; i < 1e3; i += 1) {
881
- dest = join(targetDir, `${stem}.${i}${ext}`);
882
- if (!existsSync(dest)) return dest;
1030
+ dest = join2(targetDir, `${stem}.${i}${ext}`);
1031
+ if (!existsSync2(dest)) return dest;
883
1032
  }
884
1033
  return dest;
885
1034
  }
@@ -896,7 +1045,7 @@ var FileStorage = class {
896
1045
  throw error;
897
1046
  }
898
1047
  for (const entry of entries) {
899
- const entryPath = join(dir, entry.name);
1048
+ const entryPath = join2(dir, entry.name);
900
1049
  if (entry.isDirectory()) {
901
1050
  await this.walkJsonFilesInto(entryPath, out);
902
1051
  } else if (entry.isFile() && isTrajectoryJsonFile(entry.name)) {
@@ -927,20 +1076,20 @@ var FileStorage = class {
927
1076
  let trajectoryDir;
928
1077
  if (isCompleted) {
929
1078
  const date = new Date(trajectory2.completedAt ?? trajectory2.startedAt);
930
- const monthDir = join(
1079
+ const monthDir = join2(
931
1080
  this.completedDir,
932
1081
  `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`
933
1082
  );
934
- trajectoryDir = join(monthDir, trajectory2.id);
1083
+ trajectoryDir = join2(monthDir, trajectory2.id);
935
1084
  } else {
936
- trajectoryDir = join(this.activeDir, trajectory2.id);
1085
+ trajectoryDir = join2(this.activeDir, trajectory2.id);
937
1086
  }
938
- const filePath = join(trajectoryDir, TRAJECTORY_FILE);
1087
+ const filePath = join2(trajectoryDir, TRAJECTORY_FILE);
939
1088
  await this.removeTrajectoryFiles(existingPaths, filePath);
940
1089
  await mkdir(trajectoryDir, { recursive: true });
941
1090
  if (isCompleted) {
942
1091
  const markdown = exportToMarkdown(trajectory2);
943
- await writeFile(join(trajectoryDir, SUMMARY_FILE), markdown, "utf-8");
1092
+ await writeFile(join2(trajectoryDir, SUMMARY_FILE), markdown, "utf-8");
944
1093
  }
945
1094
  await writeFile(filePath, JSON.stringify(trajectory2, null, 2), "utf-8");
946
1095
  }
@@ -949,7 +1098,7 @@ var FileStorage = class {
949
1098
  */
950
1099
  async get(id) {
951
1100
  for (const filePath of this.getActiveCandidatePaths(id)) {
952
- if (!existsSync(filePath)) continue;
1101
+ if (!existsSync2(filePath)) continue;
953
1102
  const trajectory2 = await this.readTrajectoryOrNull(filePath);
954
1103
  if (trajectory2?.id === id) {
955
1104
  return trajectory2;
@@ -1154,9 +1303,9 @@ var FileStorage = class {
1154
1303
  return [];
1155
1304
  }
1156
1305
  return [
1157
- join(this.activeDir, id, TRAJECTORY_FILE),
1306
+ join2(this.activeDir, id, TRAJECTORY_FILE),
1158
1307
  // Legacy layout from v0.5.x and earlier.
1159
- join(this.activeDir, `${id}.json`)
1308
+ join2(this.activeDir, `${id}.json`)
1160
1309
  ];
1161
1310
  }
1162
1311
  async loadAllTrajectories() {
@@ -1210,7 +1359,7 @@ var FileStorage = class {
1210
1359
  }
1211
1360
  getTrajectoryIdFromPath(filePath) {
1212
1361
  if (basename(filePath) === TRAJECTORY_FILE) {
1213
- const id = basename(dirname(filePath));
1362
+ const id = basename(dirname2(filePath));
1214
1363
  return isSafeTrajectoryId(id) ? id : void 0;
1215
1364
  }
1216
1365
  const name = basename(filePath);
@@ -1231,10 +1380,10 @@ var FileStorage = class {
1231
1380
  }
1232
1381
  async removeTrajectoryFile(filePath, summary) {
1233
1382
  if (basename(filePath) === TRAJECTORY_FILE) {
1234
- const trajectoryDir = dirname(filePath);
1383
+ const trajectoryDir = dirname2(filePath);
1235
1384
  await this.countDirectoryTrajectoryFiles(trajectoryDir, summary);
1236
1385
  await this.removeFileIfExists(
1237
- join(dirname(trajectoryDir), `${basename(trajectoryDir)}.trace.json`),
1386
+ join2(dirname2(trajectoryDir), `${basename(trajectoryDir)}.trace.json`),
1238
1387
  "trace",
1239
1388
  summary
1240
1389
  );
@@ -1260,40 +1409,40 @@ var FileStorage = class {
1260
1409
  }
1261
1410
  async countDirectoryTrajectoryFiles(trajectoryDir, summary) {
1262
1411
  await this.countFileIfExists(
1263
- join(trajectoryDir, TRAJECTORY_FILE),
1412
+ join2(trajectoryDir, TRAJECTORY_FILE),
1264
1413
  "json",
1265
1414
  summary
1266
1415
  );
1267
1416
  await this.countFileIfExists(
1268
- join(trajectoryDir, SUMMARY_FILE),
1417
+ join2(trajectoryDir, SUMMARY_FILE),
1269
1418
  "markdown",
1270
1419
  summary
1271
1420
  );
1272
1421
  await this.countFileIfExists(
1273
- join(trajectoryDir, `${basename(trajectoryDir)}.trace.json`),
1422
+ join2(trajectoryDir, `${basename(trajectoryDir)}.trace.json`),
1274
1423
  "trace",
1275
1424
  summary
1276
1425
  );
1277
1426
  await this.countFileIfExists(
1278
- join(trajectoryDir, "trace.json"),
1427
+ join2(trajectoryDir, "trace.json"),
1279
1428
  "trace",
1280
1429
  summary
1281
1430
  );
1282
1431
  await this.countFileIfExists(
1283
- join(trajectoryDir, COMPACTION_FILE),
1432
+ join2(trajectoryDir, COMPACTION_FILE),
1284
1433
  "compaction",
1285
1434
  summary
1286
1435
  );
1287
1436
  }
1288
1437
  async removeFileIfExists(path, kind, summary) {
1289
- if (!existsSync(path)) {
1438
+ if (!existsSync2(path)) {
1290
1439
  return;
1291
1440
  }
1292
1441
  await rm(path, { force: true });
1293
1442
  this.incrementDeleteSummary(kind, summary);
1294
1443
  }
1295
1444
  async countFileIfExists(path, kind, summary) {
1296
- if (existsSync(path)) {
1445
+ if (existsSync2(path)) {
1297
1446
  this.incrementDeleteSummary(kind, summary);
1298
1447
  }
1299
1448
  }
@@ -1334,21 +1483,21 @@ var FileStorage = class {
1334
1483
  }
1335
1484
  getCompactionMarkerPath(filePath, id) {
1336
1485
  if (basename(filePath) === TRAJECTORY_FILE) {
1337
- return join(dirname(filePath), COMPACTION_FILE);
1486
+ return join2(dirname2(filePath), COMPACTION_FILE);
1338
1487
  }
1339
- return join(dirname(filePath), `${id}${LEGACY_COMPACTION_SUFFIX}`);
1488
+ return join2(dirname2(filePath), `${id}${LEGACY_COMPACTION_SUFFIX}`);
1340
1489
  }
1341
1490
  getTrajectoryIdFromCompactionMarkerPath(markerPath) {
1342
1491
  if (basename(markerPath) === COMPACTION_FILE) {
1343
- const id = basename(dirname(markerPath));
1492
+ const id = basename(dirname2(markerPath));
1344
1493
  return id.startsWith("traj_") ? id : void 0;
1345
1494
  }
1346
1495
  const markerName = basename(markerPath);
1347
1496
  return markerName.endsWith(LEGACY_COMPACTION_SUFFIX) ? markerName.slice(0, -LEGACY_COMPACTION_SUFFIX.length) : void 0;
1348
1497
  }
1349
1498
  async migrateLegacyIndexCompactionMarkers() {
1350
- const indexPath = join(this.trajectoriesDir, "index.json");
1351
- if (!existsSync(indexPath)) {
1499
+ const indexPath = join2(this.trajectoriesDir, "index.json");
1500
+ if (!existsSync2(indexPath)) {
1352
1501
  return;
1353
1502
  }
1354
1503
  let parsed;
@@ -1374,7 +1523,7 @@ var FileStorage = class {
1374
1523
  if (typeof compactedInto !== "string") {
1375
1524
  return;
1376
1525
  }
1377
- const paths = typeof path === "string" && existsSync(path) && this.isPathInsideTrajectoriesDir(path) ? [path] : await this.findTrajectoryFilePaths(id);
1526
+ const paths = typeof path === "string" && existsSync2(path) && this.isPathInsideTrajectoriesDir(path) ? [path] : await this.findTrajectoryFilePaths(id);
1378
1527
  if (paths.length === 0) return;
1379
1528
  const marker = {
1380
1529
  trajectoryId: id,
@@ -1394,7 +1543,7 @@ var FileStorage = class {
1394
1543
  );
1395
1544
  }
1396
1545
  isPathInsideTrajectoriesDir(path) {
1397
- const rel = relative(resolve(this.trajectoriesDir), resolve(path));
1546
+ const rel = relative(resolve2(this.trajectoriesDir), resolve2(path));
1398
1547
  return Boolean(rel && !rel.startsWith("..") && !isAbsolute(rel));
1399
1548
  }
1400
1549
  async walkFilesInto(dir, out, predicate) {
@@ -1406,7 +1555,7 @@ var FileStorage = class {
1406
1555
  throw error;
1407
1556
  }
1408
1557
  for (const entry of entries) {
1409
- const entryPath = join(dir, entry.name);
1558
+ const entryPath = join2(dir, entry.name);
1410
1559
  if (entry.isDirectory()) {
1411
1560
  await this.walkFilesInto(entryPath, out, predicate);
1412
1561
  } else if (entry.isFile() && predicate(entry.name)) {
@@ -1539,12 +1688,12 @@ function resolveTrajectoryCliInvocation() {
1539
1688
  try {
1540
1689
  const packageJsonPath = require2.resolve("agent-trajectories/package.json");
1541
1690
  const pkg = JSON.parse(
1542
- readFileSync(packageJsonPath, "utf-8")
1691
+ readFileSync2(packageJsonPath, "utf-8")
1543
1692
  );
1544
1693
  const binEntry = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.trail ?? (pkg.name ? pkg.bin?.[pkg.name] : void 0);
1545
1694
  if (binEntry) {
1546
- const cliPath = resolvePath(dirname2(packageJsonPath), binEntry);
1547
- if (existsSync2(cliPath)) {
1695
+ const cliPath = resolvePath(dirname3(packageJsonPath), binEntry);
1696
+ if (existsSync3(cliPath)) {
1548
1697
  return { command: process.execPath, args: [cliPath] };
1549
1698
  }
1550
1699
  }
@@ -1589,7 +1738,7 @@ async function compactWorkflow(workflowId, options) {
1589
1738
  if (options?.discardSources) {
1590
1739
  args.push("--discard-sources");
1591
1740
  }
1592
- return new Promise((resolve2, reject) => {
1741
+ return new Promise((resolve3, reject) => {
1593
1742
  const child = spawn(cli.command, args, {
1594
1743
  cwd: options?.cwd,
1595
1744
  stdio: ["ignore", "pipe", "pipe"]
@@ -1617,7 +1766,7 @@ async function compactWorkflow(workflowId, options) {
1617
1766
  }
1618
1767
  try {
1619
1768
  const stdout = Buffer.concat(stdoutChunks).toString("utf-8");
1620
- resolve2(parseCompactWorkflowOutput(stdout));
1769
+ resolve3(parseCompactWorkflowOutput(stdout));
1621
1770
  } catch (error) {
1622
1771
  reject(
1623
1772
  error instanceof Error ? error : new Error("compactWorkflow failed: unable to parse CLI output")
@@ -1844,7 +1993,7 @@ var TrajectoryClient = class {
1844
1993
  constructor(options = {}) {
1845
1994
  this.storage = options.storage ?? new FileStorage(options.dataDir);
1846
1995
  this.defaultAgent = options.defaultAgent ?? process.env.TRAJECTORIES_AGENT;
1847
- this.projectId = options.projectId ?? process.env.TRAJECTORIES_PROJECT;
1996
+ this.projectId = resolveProjectId(options.projectId);
1848
1997
  this.autoSave = options.autoSave ?? true;
1849
1998
  this.autoCompact = normalizeAutoCompactOptions(options.autoCompact);
1850
1999
  this.autoCompactCwd = options.storage ? void 0 : options.dataDir;
@@ -2286,7 +2435,7 @@ function trajectory(title) {
2286
2435
 
2287
2436
  // src/core/trailers.ts
2288
2437
  import { execSync as execSync2 } from "child_process";
2289
- import { readFileSync as readFileSync2 } from "fs";
2438
+ import { readFileSync as readFileSync3 } from "fs";
2290
2439
 
2291
2440
  // src/core/trace.ts
2292
2441
  import { execSync } from "child_process";
@@ -2445,4 +2594,4 @@ export {
2445
2594
  getCommitsBetween,
2446
2595
  getFilesChangedBetween
2447
2596
  };
2448
- //# sourceMappingURL=chunk-ENWKFNUD.js.map
2597
+ //# sourceMappingURL=chunk-BVTUGGYW.js.map