@fml-inc/fml 0.8.13-rc.1 → 0.8.13-rc.2

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.
@@ -10,7 +10,7 @@ var __export = (target, all) => {
10
10
 
11
11
  // src/dirs.ts
12
12
  import os from "node:os";
13
- import path15 from "node:path";
13
+ import path16 from "node:path";
14
14
  function isPresent(value) {
15
15
  return value !== void 0 && value.trim().length > 0;
16
16
  }
@@ -31,7 +31,7 @@ function platformBaseDirectory(value, pathApi) {
31
31
  function resolveFmlDirectories(options = {}) {
32
32
  const platform = options.platform ?? process.platform;
33
33
  const env = options.env ?? process.env;
34
- const pathApi = platform === "win32" ? path15.win32 : path15.posix;
34
+ const pathApi = platform === "win32" ? path16.win32 : path16.posix;
35
35
  const homeDir = normalizeAbsolutePath(
36
36
  options.homeDir ?? os.homedir(),
37
37
  "home directory",
@@ -123,41 +123,41 @@ var init_dirs = __esm({
123
123
  FML_LOG_DIR = DIRECTORIES.logDir;
124
124
  FML_OBSERVER_DIR = DIRECTORIES.observerDir;
125
125
  FML_OBSERVER_PORT = observerPort();
126
- FML_OBSERVER_STATE_FILE = path15.join(
126
+ FML_OBSERVER_STATE_FILE = path16.join(
127
127
  FML_OBSERVER_DIR,
128
128
  "observer.pid.json"
129
129
  );
130
- FML_OBSERVER_LOCK_FILE = path15.join(
130
+ FML_OBSERVER_LOCK_FILE = path16.join(
131
131
  FML_OBSERVER_DIR,
132
132
  "observer.lock"
133
133
  );
134
- FML_OBSERVER_AUTH_TOKEN_FILE = path15.join(
134
+ FML_OBSERVER_AUTH_TOKEN_FILE = path16.join(
135
135
  FML_OBSERVER_DIR,
136
136
  "auth-token"
137
137
  );
138
- FML_OBSERVER_ENDPOINT_FILE = path15.join(
138
+ FML_OBSERVER_ENDPOINT_FILE = path16.join(
139
139
  FML_OBSERVER_DIR,
140
140
  "endpoint.json"
141
141
  );
142
- FML_OBSERVER_LOG_FILE = path15.join(FML_LOG_DIR, "observer.log");
143
- FML_UPDATE_LOG_FILE = path15.join(FML_LOG_DIR, "update.log");
144
- FML_INSTALLATION_ID_FILE = path15.join(
142
+ FML_OBSERVER_LOG_FILE = path16.join(FML_LOG_DIR, "observer.log");
143
+ FML_UPDATE_LOG_FILE = path16.join(FML_LOG_DIR, "update.log");
144
+ FML_INSTALLATION_ID_FILE = path16.join(
145
145
  FML_STATE_DIR,
146
146
  "installation_id"
147
147
  );
148
- FML_PANOPTICON_DB_FILE = path15.join(FML_STATE_DIR, "panopticon.db");
148
+ FML_PANOPTICON_DB_FILE = path16.join(FML_STATE_DIR, "panopticon.db");
149
149
  FML_PANOPTICON_DB_OBSERVER_STATE_FILE = `${FML_PANOPTICON_DB_FILE}.observer.pid.json`;
150
150
  FML_PANOPTICON_DB_OBSERVER_LOCK_FILE = `${FML_PANOPTICON_DB_FILE}.observer.lock`;
151
- FML_LEGACY_OBSERVER_DB_FILE = path15.join(
151
+ FML_LEGACY_OBSERVER_DB_FILE = path16.join(
152
152
  FML_OBSERVER_DIR,
153
153
  "observer.db"
154
154
  );
155
- FML_HOOK_INBOX_DIR = path15.join(FML_OBSERVER_DIR, "hook-inbox");
156
- FML_HOOK_CONTEXT_STATE_DIR = path15.join(
155
+ FML_HOOK_INBOX_DIR = path16.join(FML_OBSERVER_DIR, "hook-inbox");
156
+ FML_HOOK_CONTEXT_STATE_DIR = path16.join(
157
157
  FML_OBSERVER_DIR,
158
158
  "hook-context-state"
159
159
  );
160
- FML_HOOK_CAPTURE_DISABLED_FILE = path15.join(
160
+ FML_HOOK_CAPTURE_DISABLED_FILE = path16.join(
161
161
  FML_OBSERVER_DIR,
162
162
  "hook-capture.disabled"
163
163
  );
@@ -165,8 +165,8 @@ var init_dirs = __esm({
165
165
  });
166
166
 
167
167
  // src/config.ts
168
- import fs6 from "node:fs";
169
- import path16 from "node:path";
168
+ import fs7 from "node:fs";
169
+ import path17 from "node:path";
170
170
  function isHostedBackendUrl(url2) {
171
171
  let normalized;
172
172
  try {
@@ -195,7 +195,7 @@ function normalizeAppUrl(value) {
195
195
  function readEnvConfig() {
196
196
  try {
197
197
  const parsed = JSON.parse(
198
- fs6.readFileSync(ENV_CONFIG_PATH, "utf8")
198
+ fs7.readFileSync(ENV_CONFIG_PATH, "utf8")
199
199
  );
200
200
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
201
201
  throw new Error("invalid environment config");
@@ -256,7 +256,7 @@ function getActiveEnv() {
256
256
  return { name, convexUrl, appUrl };
257
257
  }
258
258
  function authStorePathFor(envName) {
259
- return path16.join(FML_DATA_DIR, `auth.${envName}.json`);
259
+ return path17.join(FML_DATA_DIR, `auth.${envName}.json`);
260
260
  }
261
261
  function getSiteUrlForConvexUrl(convexUrl) {
262
262
  const explicit = process.env.CONVEX_SITE_URL;
@@ -274,7 +274,7 @@ var init_config = __esm({
274
274
  LEGACY_HOSTED_BACKEND_URLS = [
275
275
  "https://trustworthy-chihuahua-382.convex.site"
276
276
  ];
277
- ENV_CONFIG_PATH = path16.join(FML_DATA_DIR, "env.json");
277
+ ENV_CONFIG_PATH = path17.join(FML_DATA_DIR, "env.json");
278
278
  ENV_NAME_RE = /^[A-Za-z0-9_-]+$/;
279
279
  CONVEX_URL = (process.env.FML_CONVEX_URL ?? getActiveEnv().convexUrl ?? DEFAULT_PROD_URL).replace(/\/$/, "");
280
280
  WORKOS_API_URL = "https://api.workos.com";
@@ -307,9 +307,9 @@ __export(telemetry_config_exports, {
307
307
  refreshCliTelemetryConfig: () => refreshCliTelemetryConfig,
308
308
  resolveCliSentryConfig: () => resolveCliSentryConfig
309
309
  });
310
- import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
311
- import fs7 from "node:fs";
312
- import path17 from "node:path";
310
+ import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
311
+ import fs8 from "node:fs";
312
+ import path18 from "node:path";
313
313
  function parseSentryConfig(value) {
314
314
  if (value === null) return null;
315
315
  if (!value || typeof value !== "object")
@@ -330,16 +330,16 @@ function parseSentryConfig(value) {
330
330
  }
331
331
  function cachePathFor(siteUrl) {
332
332
  const key = isHostedBackendUrl(siteUrl) ? DEFAULT_SYNC_URL : siteUrl;
333
- return path17.join(
333
+ return path18.join(
334
334
  FML_DATA_DIR,
335
335
  "telemetry-config",
336
- `${createHash3("sha256").update(key).digest("hex")}.json`
336
+ `${createHash4("sha256").update(key).digest("hex")}.json`
337
337
  );
338
338
  }
339
339
  function readCache(siteUrl) {
340
340
  try {
341
341
  const value = JSON.parse(
342
- fs7.readFileSync(cachePathFor(siteUrl), "utf8")
342
+ fs8.readFileSync(cachePathFor(siteUrl), "utf8")
343
343
  );
344
344
  if (!value || typeof value !== "object") return void 0;
345
345
  const entry = value;
@@ -356,15 +356,15 @@ function readCache(siteUrl) {
356
356
  }
357
357
  function writeCache(entry) {
358
358
  const cachePath = cachePathFor(entry.siteUrl);
359
- const temporaryPath = `${cachePath}.${randomUUID2()}.tmp`;
359
+ const temporaryPath = `${cachePath}.${randomUUID3()}.tmp`;
360
360
  try {
361
- fs7.mkdirSync(path17.dirname(cachePath), { recursive: true, mode: 448 });
362
- fs7.writeFileSync(temporaryPath, JSON.stringify(entry), { mode: 384 });
363
- fs7.renameSync(temporaryPath, cachePath);
361
+ fs8.mkdirSync(path18.dirname(cachePath), { recursive: true, mode: 448 });
362
+ fs8.writeFileSync(temporaryPath, JSON.stringify(entry), { mode: 384 });
363
+ fs8.renameSync(temporaryPath, cachePath);
364
364
  } catch {
365
365
  } finally {
366
366
  try {
367
- fs7.rmSync(temporaryPath, { force: true });
367
+ fs8.rmSync(temporaryPath, { force: true });
368
368
  } catch {
369
369
  }
370
370
  }
@@ -450,10 +450,10 @@ var init_telemetry_config = __esm({
450
450
  });
451
451
 
452
452
  // src/auth/token-store.ts
453
- import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
454
- import { execFileSync } from "node:child_process";
455
- import fs8 from "node:fs";
456
- import path18 from "node:path";
453
+ import { createHash as createHash5, randomUUID as randomUUID4 } from "node:crypto";
454
+ import { execFileSync as execFileSync2 } from "node:child_process";
455
+ import fs9 from "node:fs";
456
+ import path19 from "node:path";
457
457
  function getCurrentProcessIdentity() {
458
458
  let cache = processGlobals[PROCESS_IDENTITY_KEY];
459
459
  if (!cache) {
@@ -473,36 +473,36 @@ function resolveEnvName(envName) {
473
473
  return envName ?? getActiveEnv().name;
474
474
  }
475
475
  function ensureDir(envName) {
476
- const dir = path18.dirname(storePathFor(envName));
477
- fs8.mkdirSync(dir, { recursive: true, mode: 448 });
476
+ const dir = path19.dirname(storePathFor(envName));
477
+ fs9.mkdirSync(dir, { recursive: true, mode: 448 });
478
478
  try {
479
- fs8.chmodSync(dir, 448);
479
+ fs9.chmodSync(dir, 448);
480
480
  } catch {
481
481
  }
482
482
  }
483
483
  function writeSecretFile(filePath, data) {
484
- const temporaryFile = `${filePath}.${process.pid}.${randomUUID3()}.tmp`;
484
+ const temporaryFile = `${filePath}.${process.pid}.${randomUUID4()}.tmp`;
485
485
  let descriptor = null;
486
486
  try {
487
- descriptor = fs8.openSync(temporaryFile, "wx", 384);
488
- fs8.writeFileSync(descriptor, data, { encoding: "utf8" });
489
- fs8.fsyncSync(descriptor);
490
- fs8.closeSync(descriptor);
487
+ descriptor = fs9.openSync(temporaryFile, "wx", 384);
488
+ fs9.writeFileSync(descriptor, data, { encoding: "utf8" });
489
+ fs9.fsyncSync(descriptor);
490
+ fs9.closeSync(descriptor);
491
491
  descriptor = null;
492
- fs8.renameSync(temporaryFile, filePath);
492
+ fs9.renameSync(temporaryFile, filePath);
493
493
  try {
494
- fs8.chmodSync(filePath, 384);
494
+ fs9.chmodSync(filePath, 384);
495
495
  } catch {
496
496
  }
497
497
  } finally {
498
498
  if (descriptor !== null) {
499
499
  try {
500
- fs8.closeSync(descriptor);
500
+ fs9.closeSync(descriptor);
501
501
  } catch {
502
502
  }
503
503
  }
504
504
  try {
505
- fs8.unlinkSync(temporaryFile);
505
+ fs9.unlinkSync(temporaryFile);
506
506
  } catch {
507
507
  }
508
508
  }
@@ -510,7 +510,7 @@ function writeSecretFile(filePath, data) {
510
510
  function readTokens(envName) {
511
511
  let raw;
512
512
  try {
513
- raw = fs8.readFileSync(storePathFor(envName), "utf-8");
513
+ raw = fs9.readFileSync(storePathFor(envName), "utf-8");
514
514
  } catch {
515
515
  return null;
516
516
  }
@@ -534,7 +534,7 @@ function parseStoredAuthShape(parsed) {
534
534
  function readTokensForReconciliation(envName) {
535
535
  let raw;
536
536
  try {
537
- raw = fs8.readFileSync(storePathFor(envName), "utf8");
537
+ raw = fs9.readFileSync(storePathFor(envName), "utf8");
538
538
  } catch (error62) {
539
539
  if (error62.code === "ENOENT") return null;
540
540
  throw error62;
@@ -561,7 +561,7 @@ function writeTokensUnlocked(auth, envName) {
561
561
  writeSecretFile(storePathFor(envName), JSON.stringify(auth, null, 2));
562
562
  }
563
563
  function credentialHash(value) {
564
- return createHash4("sha256").update(value).digest("hex");
564
+ return createHash5("sha256").update(value).digest("hex");
565
565
  }
566
566
  function describeError(error62) {
567
567
  return error62 instanceof Error ? error62.message : String(error62);
@@ -639,7 +639,7 @@ function refreshLockPath(envName) {
639
639
  return `${storePathFor(envName)}.refresh.lock`;
640
640
  }
641
641
  function pendingOAuthRefreshPrefix(envName) {
642
- return `${path18.basename(storePathFor(envName))}.refresh-result.`;
642
+ return `${path19.basename(storePathFor(envName))}.refresh-result.`;
643
643
  }
644
644
  function parsePendingOAuthRefresh(raw) {
645
645
  try {
@@ -655,7 +655,7 @@ function parsePendingOAuthRefresh(raw) {
655
655
  }
656
656
  function stagePendingOAuthRefresh(previousRefreshToken, refreshed, envName) {
657
657
  ensureDir(envName);
658
- const resultPath = `${storePathFor(envName)}.refresh-result.${Date.now()}.${randomUUID3()}.json`;
658
+ const resultPath = `${storePathFor(envName)}.refresh-result.${Date.now()}.${randomUUID4()}.json`;
659
659
  const pending = {
660
660
  version: 1,
661
661
  previousRefreshTokenHash: credentialHash(previousRefreshToken),
@@ -668,7 +668,7 @@ function stagePendingOAuthRefresh(previousRefreshToken, refreshed, envName) {
668
668
  }
669
669
  function removePendingOAuthRefresh(resultPath) {
670
670
  try {
671
- fs8.unlinkSync(resultPath);
671
+ fs9.unlinkSync(resultPath);
672
672
  } catch (error62) {
673
673
  if (error62.code !== "ENOENT") {
674
674
  console.error(
@@ -679,11 +679,11 @@ function removePendingOAuthRefresh(resultPath) {
679
679
  }
680
680
  function reconcilePendingOAuthRefreshes(envName, unreadableStorePolicy = "preserve-staged") {
681
681
  const storePath = storePathFor(envName);
682
- const directory = path18.dirname(storePath);
682
+ const directory = path19.dirname(storePath);
683
683
  const prefix = pendingOAuthRefreshPrefix(envName);
684
684
  let resultPaths;
685
685
  try {
686
- resultPaths = fs8.readdirSync(directory).filter((entry) => entry.startsWith(prefix) && entry.endsWith(".json")).map((entry) => path18.join(directory, entry));
686
+ resultPaths = fs9.readdirSync(directory).filter((entry) => entry.startsWith(prefix) && entry.endsWith(".json")).map((entry) => path19.join(directory, entry));
687
687
  } catch (error62) {
688
688
  if (error62.code === "ENOENT") return;
689
689
  throw error62;
@@ -691,7 +691,7 @@ function reconcilePendingOAuthRefreshes(envName, unreadableStorePolicy = "preser
691
691
  const pendingResults = resultPaths.map((resultPath) => {
692
692
  let pending = null;
693
693
  try {
694
- pending = parsePendingOAuthRefresh(fs8.readFileSync(resultPath, "utf8"));
694
+ pending = parsePendingOAuthRefresh(fs9.readFileSync(resultPath, "utf8"));
695
695
  } catch (error62) {
696
696
  if (error62.code !== "ENOENT") throw error62;
697
697
  }
@@ -746,13 +746,13 @@ function parseRefreshLockOwner(raw) {
746
746
  function getProcessIdentity(pid) {
747
747
  try {
748
748
  if (process.platform === "linux") {
749
- const stat = fs8.readFileSync(`/proc/${pid}/stat`, "utf8");
749
+ const stat = fs9.readFileSync(`/proc/${pid}/stat`, "utf8");
750
750
  const fieldsAfterCommand = stat.slice(stat.lastIndexOf(")") + 2).split(" ");
751
751
  const startTimeTicks = fieldsAfterCommand[19];
752
752
  return startTimeTicks ? `linux:${startTimeTicks}` : null;
753
753
  }
754
754
  if (process.platform === "darwin") {
755
- const startedAt = execFileSync(
755
+ const startedAt = execFileSync2(
756
756
  "ps",
757
757
  ["-p", String(pid), "-o", "lstart="],
758
758
  {
@@ -764,7 +764,7 @@ function getProcessIdentity(pid) {
764
764
  return startedAt ? `darwin:${startedAt}` : null;
765
765
  }
766
766
  if (process.platform === "win32") {
767
- const startedAt = execFileSync(
767
+ const startedAt = execFileSync2(
768
768
  "powershell.exe",
769
769
  [
770
770
  "-NoProfile",
@@ -798,7 +798,7 @@ function parseRecoveryMarkerOwner(raw) {
798
798
  function snapshotLeaseFile(filePath) {
799
799
  let stat;
800
800
  try {
801
- stat = fs8.lstatSync(filePath);
801
+ stat = fs9.lstatSync(filePath);
802
802
  } catch (error62) {
803
803
  if (error62.code === "ENOENT") return null;
804
804
  throw error62;
@@ -807,7 +807,7 @@ function snapshotLeaseFile(filePath) {
807
807
  try {
808
808
  return {
809
809
  stat,
810
- raw: fs8.readFileSync(path18.join(filePath, "owner.json"), "utf8")
810
+ raw: fs9.readFileSync(path19.join(filePath, "owner.json"), "utf8")
811
811
  };
812
812
  } catch (error62) {
813
813
  if (error62.code === "ENOENT") {
@@ -817,7 +817,7 @@ function snapshotLeaseFile(filePath) {
817
817
  }
818
818
  }
819
819
  try {
820
- return { stat, raw: fs8.readFileSync(filePath, "utf8") };
820
+ return { stat, raw: fs9.readFileSync(filePath, "utf8") };
821
821
  } catch (error62) {
822
822
  if (error62.code === "ENOENT") return null;
823
823
  throw error62;
@@ -829,21 +829,21 @@ function snapshotsMatch(observed, current) {
829
829
  function restoreMovedLease(movedPath, originalPath) {
830
830
  let movedStat;
831
831
  try {
832
- movedStat = fs8.lstatSync(movedPath);
832
+ movedStat = fs9.lstatSync(movedPath);
833
833
  } catch (error62) {
834
834
  if (error62.code === "ENOENT") return;
835
835
  throw error62;
836
836
  }
837
837
  if (!movedStat.isFile()) return;
838
838
  try {
839
- fs8.linkSync(movedPath, originalPath);
839
+ fs9.linkSync(movedPath, originalPath);
840
840
  } catch (error62) {
841
841
  const code = error62.code;
842
842
  if (code === "EEXIST" || code === "ENOENT") return;
843
843
  throw error62;
844
844
  }
845
845
  try {
846
- fs8.unlinkSync(movedPath);
846
+ fs9.unlinkSync(movedPath);
847
847
  } catch (error62) {
848
848
  if (error62.code !== "ENOENT") throw error62;
849
849
  }
@@ -857,27 +857,27 @@ function pathMatchesClaim(filePath, claimedStat, ownerId, parseOwner) {
857
857
  function markClaimReleased(filePath, claimedStat, owner, parseOwner) {
858
858
  let descriptor;
859
859
  try {
860
- descriptor = fs8.openSync(filePath, "r+");
860
+ descriptor = fs9.openSync(filePath, "r+");
861
861
  } catch (error62) {
862
862
  if (error62.code === "ENOENT") return false;
863
863
  throw error62;
864
864
  }
865
865
  try {
866
- const currentStat = fs8.fstatSync(descriptor);
866
+ const currentStat = fs9.fstatSync(descriptor);
867
867
  if (currentStat.dev !== claimedStat.dev || currentStat.ino !== claimedStat.ino) {
868
868
  return false;
869
869
  }
870
- const currentOwner = parseOwner(fs8.readFileSync(descriptor, "utf8"));
870
+ const currentOwner = parseOwner(fs9.readFileSync(descriptor, "utf8"));
871
871
  if (currentOwner?.ownerId !== owner.ownerId) return false;
872
872
  const serialized = Buffer.from(
873
873
  `${JSON.stringify({ ...owner, releasedAtMs: Date.now() })}
874
874
  `,
875
875
  "utf8"
876
876
  );
877
- fs8.ftruncateSync(descriptor, 0);
877
+ fs9.ftruncateSync(descriptor, 0);
878
878
  let offset = 0;
879
879
  while (offset < serialized.length) {
880
- offset += fs8.writeSync(
880
+ offset += fs9.writeSync(
881
881
  descriptor,
882
882
  serialized,
883
883
  offset,
@@ -885,19 +885,19 @@ function markClaimReleased(filePath, claimedStat, owner, parseOwner) {
885
885
  offset
886
886
  );
887
887
  }
888
- fs8.fsyncSync(descriptor);
888
+ fs9.fsyncSync(descriptor);
889
889
  return true;
890
890
  } finally {
891
- fs8.closeSync(descriptor);
891
+ fs9.closeSync(descriptor);
892
892
  }
893
893
  }
894
894
  function releaseOwnedLease(filePath, claimedStat, owner, parseOwner, description) {
895
- const releasedPath = `${filePath}.released.${randomUUID3()}`;
895
+ const releasedPath = `${filePath}.released.${randomUUID4()}`;
896
896
  let moved = false;
897
897
  try {
898
898
  if (!markClaimReleased(filePath, claimedStat, owner, parseOwner)) return;
899
899
  try {
900
- fs8.renameSync(filePath, releasedPath);
900
+ fs9.renameSync(filePath, releasedPath);
901
901
  moved = true;
902
902
  } catch (error62) {
903
903
  if (error62.code === "ENOENT") return;
@@ -910,7 +910,7 @@ function releaseOwnedLease(filePath, claimedStat, owner, parseOwner, description
910
910
  moved = false;
911
911
  return;
912
912
  }
913
- fs8.rmSync(releasedPath, {
913
+ fs9.rmSync(releasedPath, {
914
914
  recursive: released.stat.isDirectory(),
915
915
  force: true
916
916
  });
@@ -984,9 +984,9 @@ function ownerPidWasReused(owner, lockMtimeMs) {
984
984
  return currentIdentity !== null && currentIdentity !== owner.processIdentity;
985
985
  }
986
986
  function writeClaimToDescriptor(descriptor, serializedOwner) {
987
- const claimedStat = fs8.fstatSync(descriptor);
988
- fs8.writeFileSync(descriptor, serializedOwner, { encoding: "utf8" });
989
- fs8.fsyncSync(descriptor);
987
+ const claimedStat = fs9.fstatSync(descriptor);
988
+ fs9.writeFileSync(descriptor, serializedOwner, { encoding: "utf8" });
989
+ fs9.fsyncSync(descriptor);
990
990
  return claimedStat;
991
991
  }
992
992
  function recoverAbandonedRecoveryMarker(recoveryPath) {
@@ -996,15 +996,15 @@ function recoverAbandonedRecoveryMarker(recoveryPath) {
996
996
  const ownerAlive = owner ? isProcessAlive(owner.pid) : false;
997
997
  const reclaimable = owner ? owner.releasedAtMs !== void 0 || !ownerAlive || ownerAlive && ownerPidWasReused(owner, observed.stat.mtimeMs) : Date.now() - observed.stat.mtimeMs > OAUTH_REFRESH_RECOVERY_STALE_MS;
998
998
  if (!reclaimable) return;
999
- const abandonedPath = `${recoveryPath}.stale.${randomUUID3()}`;
999
+ const abandonedPath = `${recoveryPath}.stale.${randomUUID4()}`;
1000
1000
  try {
1001
- fs8.renameSync(recoveryPath, abandonedPath);
1001
+ fs9.renameSync(recoveryPath, abandonedPath);
1002
1002
  const moved = snapshotLeaseFile(abandonedPath);
1003
1003
  if (!moved || !snapshotsMatch(observed, moved)) {
1004
1004
  restoreMovedLease(abandonedPath, recoveryPath);
1005
1005
  return;
1006
1006
  }
1007
- fs8.rmSync(abandonedPath, {
1007
+ fs9.rmSync(abandonedPath, {
1008
1008
  recursive: moved.stat.isDirectory(),
1009
1009
  force: true
1010
1010
  });
@@ -1013,7 +1013,7 @@ function recoverAbandonedRecoveryMarker(recoveryPath) {
1013
1013
  }
1014
1014
  }
1015
1015
  function acquireRecoveryMarker(recoveryPath) {
1016
- const ownerId = randomUUID3();
1016
+ const ownerId = randomUUID4();
1017
1017
  const owner = {
1018
1018
  version: 1,
1019
1019
  ownerId,
@@ -1024,7 +1024,7 @@ function acquireRecoveryMarker(recoveryPath) {
1024
1024
  };
1025
1025
  let descriptor;
1026
1026
  try {
1027
- descriptor = fs8.openSync(recoveryPath, "wx", 384);
1027
+ descriptor = fs9.openSync(recoveryPath, "wx", 384);
1028
1028
  } catch (error62) {
1029
1029
  if (error62.code === "EEXIST") return null;
1030
1030
  throw error62;
@@ -1037,7 +1037,7 @@ function acquireRecoveryMarker(recoveryPath) {
1037
1037
  `
1038
1038
  );
1039
1039
  } finally {
1040
- fs8.closeSync(descriptor);
1040
+ fs9.closeSync(descriptor);
1041
1041
  }
1042
1042
  const isOwned = () => pathMatchesClaim(
1043
1043
  recoveryPath,
@@ -1081,15 +1081,15 @@ function recoverStaleRefreshLock(lockPath) {
1081
1081
  }
1082
1082
  if (!reclaimable) return;
1083
1083
  if (!recovery.isOwned()) return;
1084
- const abandonedPath = `${lockPath}.stale.${randomUUID3()}`;
1084
+ const abandonedPath = `${lockPath}.stale.${randomUUID4()}`;
1085
1085
  try {
1086
- fs8.renameSync(lockPath, abandonedPath);
1086
+ fs9.renameSync(lockPath, abandonedPath);
1087
1087
  const moved = snapshotLeaseFile(abandonedPath);
1088
1088
  if (!moved || !snapshotsMatch(observed, moved)) {
1089
1089
  restoreMovedLease(abandonedPath, lockPath);
1090
1090
  return;
1091
1091
  }
1092
- fs8.rmSync(abandonedPath, {
1092
+ fs9.rmSync(abandonedPath, {
1093
1093
  recursive: moved.stat.isDirectory(),
1094
1094
  force: true
1095
1095
  });
@@ -1116,7 +1116,7 @@ async function acquireRefreshLock(envName, signal) {
1116
1116
  if (signal?.aborted) {
1117
1117
  throw signal.reason ?? new DOMException("The operation was aborted", "AbortError");
1118
1118
  }
1119
- if (fs8.existsSync(recoveryPath)) {
1119
+ if (fs9.existsSync(recoveryPath)) {
1120
1120
  try {
1121
1121
  recoverAbandonedRecoveryMarker(recoveryPath);
1122
1122
  } catch (recoveryError) {
@@ -1130,7 +1130,7 @@ async function acquireRefreshLock(envName, signal) {
1130
1130
  }
1131
1131
  let descriptor;
1132
1132
  try {
1133
- descriptor = fs8.openSync(lockPath, "wx", 384);
1133
+ descriptor = fs9.openSync(lockPath, "wx", 384);
1134
1134
  } catch (error62) {
1135
1135
  if (error62.code !== "EEXIST") {
1136
1136
  console.error(
@@ -1149,7 +1149,7 @@ async function acquireRefreshLock(envName, signal) {
1149
1149
  await sleep(OAUTH_REFRESH_LOCK_POLL_MS, signal);
1150
1150
  continue;
1151
1151
  }
1152
- const ownerId = randomUUID3();
1152
+ const ownerId = randomUUID4();
1153
1153
  const owner = {
1154
1154
  version: 1,
1155
1155
  ownerId,
@@ -1166,13 +1166,13 @@ async function acquireRefreshLock(envName, signal) {
1166
1166
  `
1167
1167
  );
1168
1168
  } catch (error62) {
1169
- fs8.closeSync(descriptor);
1169
+ fs9.closeSync(descriptor);
1170
1170
  console.error(
1171
1171
  `[fml] Auth: unable to initialize refresh lock: ${error62 instanceof Error ? error62.message : String(error62)}`
1172
1172
  );
1173
1173
  return null;
1174
1174
  }
1175
- fs8.closeSync(descriptor);
1175
+ fs9.closeSync(descriptor);
1176
1176
  const isOwned = () => pathMatchesClaim(lockPath, claimedStat, ownerId, parseRefreshLockOwner);
1177
1177
  const releaseClaim = () => releaseOwnedLease(
1178
1178
  lockPath,
@@ -1187,7 +1187,7 @@ async function acquireRefreshLock(envName, signal) {
1187
1187
  }
1188
1188
  let entryBlocked;
1189
1189
  try {
1190
- entryBlocked = fs8.existsSync(recoveryPath) || !isOwned();
1190
+ entryBlocked = fs9.existsSync(recoveryPath) || !isOwned();
1191
1191
  } catch (error62) {
1192
1192
  console.error(
1193
1193
  `[fml] Auth: unable to verify refresh lock ownership: ${describeError(error62)}`
@@ -1600,7 +1600,7 @@ var init_token_store = __esm({
1600
1600
  PROCESS_IDENTITY_KEY = /* @__PURE__ */ Symbol.for("@fml-inc/fml:auth-process-identity");
1601
1601
  processGlobals = globalThis;
1602
1602
  existingProcessInstanceId = processGlobals[PROCESS_INSTANCE_KEY];
1603
- CURRENT_PROCESS_INSTANCE_ID = typeof existingProcessInstanceId === "string" ? existingProcessInstanceId : randomUUID3();
1603
+ CURRENT_PROCESS_INSTANCE_ID = typeof existingProcessInstanceId === "string" ? existingProcessInstanceId : randomUUID4();
1604
1604
  processGlobals[PROCESS_INSTANCE_KEY] = CURRENT_PROCESS_INSTANCE_ID;
1605
1605
  CorruptStoredCredentialsError = class extends Error {
1606
1606
  };
@@ -1612,16 +1612,508 @@ var init_token_store = __esm({
1612
1612
  }
1613
1613
  });
1614
1614
 
1615
+ // ../../packages/agent-targets/src/git-state.ts
1616
+ import { execFile, execFileSync } from "node:child_process";
1617
+ import { createHash, randomUUID } from "node:crypto";
1618
+ import fs from "node:fs";
1619
+ import path from "node:path";
1620
+
1621
+ // ../../packages/shared/src/github-repository.ts
1622
+ var MAX_REPOSITORY_BYTES = 201;
1623
+ var UTF8_ENCODER = new TextEncoder();
1624
+ function utf8Bytes(value) {
1625
+ return UTF8_ENCODER.encode(value).byteLength;
1626
+ }
1627
+ function githubRepositoryPath(pathname) {
1628
+ const parts = pathname.replace(/^\/+|\/+$/g, "").split("/");
1629
+ if (parts.length !== 2) return null;
1630
+ const owner = parts[0];
1631
+ const repository = parts[1]?.replace(/\.git$/i, "");
1632
+ if (!owner || !repository) return null;
1633
+ const segmentPattern = /^[A-Za-z0-9_.-]+$/;
1634
+ if (!segmentPattern.test(owner) || !segmentPattern.test(repository)) {
1635
+ return null;
1636
+ }
1637
+ const canonical = `${owner.toLowerCase()}/${repository.toLowerCase()}`;
1638
+ if (canonical.startsWith("./") || canonical.startsWith("../") || repository === "." || repository === ".." || repository.toLowerCase().endsWith(".git")) {
1639
+ return null;
1640
+ }
1641
+ return utf8Bytes(canonical) <= MAX_REPOSITORY_BYTES ? canonical : null;
1642
+ }
1643
+ function canonicalGitHubRepository(remote) {
1644
+ const trimmed = remote.trim();
1645
+ if (!trimmed || trimmed.includes("\0")) return null;
1646
+ const scp = trimmed.match(/^git@github\.com:([^?#]+)$/i);
1647
+ if (scp?.[1]) return githubRepositoryPath(scp[1]);
1648
+ let url2;
1649
+ try {
1650
+ url2 = new URL(trimmed);
1651
+ } catch {
1652
+ return null;
1653
+ }
1654
+ if (url2.hostname.toLowerCase() !== "github.com") return null;
1655
+ if (url2.search || url2.hash || url2.password || url2.port) return null;
1656
+ if (url2.protocol === "https:" || url2.protocol === "http:") {
1657
+ if (url2.username) return null;
1658
+ } else if (url2.protocol === "ssh:") {
1659
+ if (url2.username && url2.username !== "git") return null;
1660
+ } else {
1661
+ return null;
1662
+ }
1663
+ return githubRepositoryPath(url2.pathname);
1664
+ }
1665
+
1666
+ // ../../packages/shared/src/git-state.ts
1667
+ function isRecord(value) {
1668
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1669
+ }
1670
+ function validGitText(value) {
1671
+ return typeof value === "string" && value.length > 0 && value.length <= 4096 && !new RegExp("\\p{Cc}", "u").test(value);
1672
+ }
1673
+ function parseGitState(value) {
1674
+ if (!isRecord(value)) return null;
1675
+ const {
1676
+ version: version2,
1677
+ observedAtMs,
1678
+ repository,
1679
+ worktreeRoot,
1680
+ gitDir,
1681
+ commonDir,
1682
+ headRef,
1683
+ branch,
1684
+ commitHash,
1685
+ detached,
1686
+ unborn,
1687
+ isLinkedWorktree
1688
+ } = value;
1689
+ if (version2 !== 1 || typeof observedAtMs !== "number" || !Number.isSafeInteger(observedAtMs) || observedAtMs < 0 || !(repository === null || typeof repository === "string" && repository.length <= 201 && canonicalGitHubRepository(`https://github.com/${repository}`) === repository) || !validGitText(worktreeRoot) || !validGitText(gitDir) || !validGitText(commonDir) || !(headRef === null || validGitText(headRef)) || !(branch === null || validGitText(branch)) || !(commitHash === null || typeof commitHash === "string" && /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(commitHash)) || typeof detached !== "boolean" || typeof unborn !== "boolean" || typeof isLinkedWorktree !== "boolean")
1690
+ return null;
1691
+ return {
1692
+ version: version2,
1693
+ observedAtMs,
1694
+ repository,
1695
+ worktreeRoot,
1696
+ gitDir,
1697
+ commonDir,
1698
+ headRef,
1699
+ branch,
1700
+ commitHash,
1701
+ detached,
1702
+ unborn,
1703
+ isLinkedWorktree
1704
+ };
1705
+ }
1706
+ var MAX_GIT_STATES_BYTES = 16 * 1024;
1707
+
1708
+ // ../../packages/agent-targets/src/git-state.ts
1709
+ function isRecord2(value) {
1710
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1711
+ }
1712
+ var GIT_STATE_CACHE_FILENAME = "git-state.cache.json";
1713
+ var GIT_STATE_PAYLOAD_KEY = "_fmlGitState";
1714
+ var DEFAULT_TTL_MS = 5e3;
1715
+ var DEFAULT_TIMEOUT_MS = 1e3;
1716
+ var DEFAULT_MAX_ENTRIES = 512;
1717
+ var MAX_OUTPUT_BYTES = 16 * 1024;
1718
+ var GIT_DISCOVERY_OVERRIDES = [
1719
+ "GIT_DIR",
1720
+ "GIT_COMMON_DIR",
1721
+ "GIT_WORK_TREE",
1722
+ "GIT_CEILING_DIRECTORIES",
1723
+ "GIT_DISCOVERY_ACROSS_FILESYSTEM"
1724
+ ];
1725
+ function* readState(cwd, knownHeadRef) {
1726
+ const layout = yield {
1727
+ args: [
1728
+ "rev-parse",
1729
+ "--show-toplevel",
1730
+ "--absolute-git-dir",
1731
+ "--git-common-dir",
1732
+ "--verify",
1733
+ "HEAD"
1734
+ ],
1735
+ // An unborn HEAD still emits the three validated checkout paths.
1736
+ allowedExit: 128,
1737
+ keepOutputOnAllowedExit: true
1738
+ };
1739
+ const [worktreePath, gitPath, commonPath, revision] = layout?.split("\n") ?? [];
1740
+ if (!worktreePath || !gitPath || !commonPath) return null;
1741
+ const worktreeRoot = path.resolve(cwd, worktreePath);
1742
+ const gitDir = path.resolve(cwd, gitPath);
1743
+ const commonDir = path.resolve(cwd, commonPath);
1744
+ const headRef = knownHeadRef === void 0 ? yield { args: ["symbolic-ref", "--quiet", "HEAD"], allowedExit: 1 } : knownHeadRef;
1745
+ const commitHash = revision ?? null;
1746
+ if (!headRef && !commitHash) return null;
1747
+ const remote = yield {
1748
+ args: ["remote", "get-url", "origin"],
1749
+ allowedExit: 2
1750
+ };
1751
+ return {
1752
+ version: 1,
1753
+ repository: remote ? canonicalGitHubRepository(remote) : null,
1754
+ worktreeRoot,
1755
+ gitDir,
1756
+ commonDir,
1757
+ headRef,
1758
+ branch: headRef?.startsWith("refs/heads/") ? headRef.slice(11) : null,
1759
+ commitHash: commitHash?.toLowerCase() ?? null,
1760
+ detached: headRef === null,
1761
+ unborn: headRef !== null && commitHash === null,
1762
+ isLinkedWorktree: gitDir !== commonDir
1763
+ };
1764
+ }
1765
+ function remaining(deadline) {
1766
+ const milliseconds = deadline - performance.now();
1767
+ if (milliseconds <= 0) throw new Error("Git lookup timed out");
1768
+ return Math.max(1, Math.ceil(milliseconds));
1769
+ }
1770
+ function runSync(cwd, command, deadline) {
1771
+ try {
1772
+ return execFileSync("git", ["-C", cwd, ...command.args], {
1773
+ encoding: "utf8",
1774
+ timeout: remaining(deadline),
1775
+ maxBuffer: MAX_OUTPUT_BYTES,
1776
+ stdio: ["ignore", "pipe", "ignore"],
1777
+ windowsHide: true
1778
+ }).trim() || null;
1779
+ } catch (error62) {
1780
+ if (command.allowedExit !== void 0 && error62 && typeof error62 === "object" && "status" in error62 && error62.status === command.allowedExit) {
1781
+ return command.keepOutputOnAllowedExit && "stdout" in error62 && typeof error62.stdout === "string" ? error62.stdout.trim() || null : null;
1782
+ }
1783
+ throw error62;
1784
+ }
1785
+ }
1786
+ async function runAsync(cwd, command, deadline, signal) {
1787
+ return await new Promise((resolve, reject) => {
1788
+ execFile(
1789
+ "git",
1790
+ ["-C", cwd, ...command.args],
1791
+ {
1792
+ encoding: "utf8",
1793
+ timeout: remaining(deadline),
1794
+ maxBuffer: MAX_OUTPUT_BYTES,
1795
+ windowsHide: true,
1796
+ signal
1797
+ },
1798
+ (error62, stdout) => {
1799
+ if (signal?.aborted)
1800
+ reject(signal.reason ?? new Error("Git lookup aborted"));
1801
+ else if (error62 && error62.code !== command.allowedExit) reject(error62);
1802
+ else
1803
+ resolve(
1804
+ error62 && !command.keepOutputOnAllowedExit ? null : stdout.trim() || null
1805
+ );
1806
+ }
1807
+ );
1808
+ });
1809
+ }
1810
+ function inspectCheckout(cwd) {
1811
+ if (!cwd || cwd.trim() !== cwd || new RegExp("\\p{Cc}", "u").test(cwd) || Buffer.byteLength(cwd) > 4096)
1812
+ return null;
1813
+ try {
1814
+ const directory = fs.realpathSync(cwd);
1815
+ if (!fs.statSync(directory).isDirectory()) return null;
1816
+ if (GIT_DISCOVERY_OVERRIDES.some((key) => process.env[key] !== void 0))
1817
+ return { key: directory, signature: null };
1818
+ const layout = findGitDirectory(directory);
1819
+ if (!layout) return null;
1820
+ if (process.env.GIT_CONFIG !== void 0)
1821
+ return { key: layout.worktreeRoot, signature: null };
1822
+ const local = checkoutSignature(layout);
1823
+ const environment = Object.keys(process.env).filter(
1824
+ (key) => key.startsWith("GIT_") || key === "HOME" || key === "XDG_CONFIG_HOME"
1825
+ ).sort().map((key) => [key, process.env[key]]);
1826
+ return {
1827
+ key: layout.worktreeRoot,
1828
+ headRef: local.headRef,
1829
+ signature: local.signature === null ? null : createHash("sha256").update(JSON.stringify([local.signature, environment])).digest("hex")
1830
+ };
1831
+ } catch {
1832
+ return null;
1833
+ }
1834
+ }
1835
+ function createGitStateResolver(options = {}) {
1836
+ const entries = /* @__PURE__ */ new Map();
1837
+ const pending = /* @__PURE__ */ new Map();
1838
+ const now = options.nowMs ?? Date.now;
1839
+ const maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
1840
+ const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
1841
+ const readDisk = () => {
1842
+ if (!options.cacheFile) return;
1843
+ try {
1844
+ const file2 = readRegularFile(options.cacheFile, 8 * 1024 * 1024).toString(
1845
+ "utf8"
1846
+ );
1847
+ const parsed = JSON.parse(file2);
1848
+ if (!isRecord2(parsed) || parsed.version !== 1 || !Array.isArray(parsed.entries) || parsed.entries.length > maxEntries)
1849
+ return;
1850
+ const timestamp = now();
1851
+ for (const pair of parsed.entries) {
1852
+ if (!Array.isArray(pair) || pair.length !== 2) continue;
1853
+ const cwd = pair[0];
1854
+ const entry = pair[1];
1855
+ if (typeof cwd !== "string" || !isRecord2(entry)) continue;
1856
+ const state = parseGitState(entry.state);
1857
+ if (state && typeof entry.signature === "string" && typeof entry.expiresAtMs === "number" && Number.isFinite(entry.expiresAtMs) && entry.expiresAtMs > timestamp && state.observedAtMs <= timestamp && entry.expiresAtMs <= state.observedAtMs + ttlMs) {
1858
+ entries.set(cwd, {
1859
+ signature: entry.signature,
1860
+ state,
1861
+ expiresAtMs: entry.expiresAtMs
1862
+ });
1863
+ }
1864
+ }
1865
+ trim();
1866
+ } catch {
1867
+ }
1868
+ };
1869
+ const trim = () => {
1870
+ while (entries.size > Math.max(0, maxEntries)) {
1871
+ const key = entries.keys().next().value;
1872
+ if (key === void 0) break;
1873
+ entries.delete(key);
1874
+ }
1875
+ };
1876
+ const cached2 = (cwd, signature) => {
1877
+ if (!signature || ttlMs <= 0) return void 0;
1878
+ if (!entries.has(cwd)) readDisk();
1879
+ const entry = entries.get(cwd);
1880
+ if (!entry || entry.signature !== signature || entry.expiresAtMs <= now() || entry.state.observedAtMs > now())
1881
+ return void 0;
1882
+ entries.delete(cwd);
1883
+ entries.set(cwd, entry);
1884
+ return entry.state;
1885
+ };
1886
+ const finish = (cwd, signature, value) => {
1887
+ const state = parseGitState(value && { ...value, observedAtMs: now() });
1888
+ if (!state) return null;
1889
+ const after = inspectCheckout(cwd)?.signature ?? null;
1890
+ if (signature !== after) return null;
1891
+ if (signature && maxEntries > 0 && ttlMs > 0) {
1892
+ entries.delete(cwd);
1893
+ entries.set(cwd, {
1894
+ signature,
1895
+ state,
1896
+ expiresAtMs: state.observedAtMs + ttlMs
1897
+ });
1898
+ trim();
1899
+ if (options.cacheFile) {
1900
+ const temporary = `${options.cacheFile}.${process.pid}.${randomUUID()}.tmp`;
1901
+ try {
1902
+ fs.mkdirSync(path.dirname(options.cacheFile), {
1903
+ recursive: true,
1904
+ mode: 448
1905
+ });
1906
+ fs.writeFileSync(
1907
+ temporary,
1908
+ JSON.stringify({ version: 1, entries: [...entries] }),
1909
+ { mode: 384 }
1910
+ );
1911
+ fs.renameSync(temporary, options.cacheFile);
1912
+ } catch {
1913
+ try {
1914
+ fs.rmSync(temporary, { force: true });
1915
+ } catch {
1916
+ }
1917
+ }
1918
+ }
1919
+ }
1920
+ return state;
1921
+ };
1922
+ return {
1923
+ resolve(cwd, lookup = {}) {
1924
+ lookup.signal?.throwIfAborted();
1925
+ const snapshot = inspectCheckout(cwd);
1926
+ if (!snapshot) return null;
1927
+ const { key, signature } = snapshot;
1928
+ const hit = cached2(key, signature);
1929
+ if (hit) return hit;
1930
+ try {
1931
+ const deadline = performance.now() + (lookup.timeoutMs ?? DEFAULT_TIMEOUT_MS);
1932
+ const recipe = readState(key, snapshot.headRef);
1933
+ let step = recipe.next();
1934
+ while (!step.done) {
1935
+ lookup.signal?.throwIfAborted();
1936
+ step = recipe.next(runSync(key, step.value, deadline));
1937
+ }
1938
+ return finish(key, signature, step.value);
1939
+ } catch {
1940
+ lookup.signal?.throwIfAborted();
1941
+ return null;
1942
+ }
1943
+ },
1944
+ async resolveAsync(cwd, lookup = {}) {
1945
+ lookup.signal?.throwIfAborted();
1946
+ const snapshot = inspectCheckout(cwd);
1947
+ if (!snapshot) return null;
1948
+ const { key, signature } = snapshot;
1949
+ const hit = cached2(key, signature);
1950
+ if (hit) return hit;
1951
+ const existing = !lookup.signal ? pending.get(key) : void 0;
1952
+ if (existing) return await existing;
1953
+ const work = (async () => {
1954
+ try {
1955
+ const deadline = performance.now() + (lookup.timeoutMs ?? DEFAULT_TIMEOUT_MS);
1956
+ const recipe = readState(key, snapshot.headRef);
1957
+ let step = recipe.next();
1958
+ while (!step.done)
1959
+ step = recipe.next(
1960
+ await runAsync(key, step.value, deadline, lookup.signal)
1961
+ );
1962
+ return finish(key, signature, step.value);
1963
+ } catch {
1964
+ lookup.signal?.throwIfAborted();
1965
+ return null;
1966
+ }
1967
+ })();
1968
+ if (!lookup.signal) pending.set(key, work);
1969
+ try {
1970
+ return await work;
1971
+ } finally {
1972
+ if (pending.get(key) === work) pending.delete(key);
1973
+ }
1974
+ }
1975
+ };
1976
+ }
1977
+ function readRegularFile(filePath, maxBytes) {
1978
+ const fd = fs.openSync(
1979
+ filePath,
1980
+ fs.constants.O_RDONLY | fs.constants.O_NONBLOCK
1981
+ );
1982
+ try {
1983
+ const stat = fs.fstatSync(fd);
1984
+ if (!stat.isFile() || stat.size > maxBytes)
1985
+ throw new Error("Git metadata is not a bounded regular file");
1986
+ const buffer = Buffer.alloc(stat.size + 1);
1987
+ let length = 0;
1988
+ while (length < buffer.length) {
1989
+ const bytes = fs.readSync(
1990
+ fd,
1991
+ buffer,
1992
+ length,
1993
+ buffer.length - length,
1994
+ null
1995
+ );
1996
+ if (bytes === 0) break;
1997
+ length += bytes;
1998
+ }
1999
+ if (length > stat.size) throw new Error("Git metadata grew during read");
2000
+ return buffer.subarray(0, length);
2001
+ } finally {
2002
+ fs.closeSync(fd);
2003
+ }
2004
+ }
2005
+ function fileContentIdentity(filePath) {
2006
+ try {
2007
+ const contents = readRegularFile(filePath, 4 * 1024 * 1024);
2008
+ return `sha256:${createHash("sha256").update(contents).digest("hex")}`;
2009
+ } catch (error62) {
2010
+ const code = error62 && typeof error62 === "object" && "code" in error62 ? error62.code : void 0;
2011
+ return code === "ENOENT" || code === "ENOTDIR" ? "missing" : null;
2012
+ }
2013
+ }
2014
+ function fileStatIdentity(filePath) {
2015
+ try {
2016
+ const stat = fs.statSync(filePath, { bigint: true });
2017
+ if (!stat.isFile() || stat.ino === 0n) return null;
2018
+ return [stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].join(
2019
+ ":"
2020
+ );
2021
+ } catch (error62) {
2022
+ const code = error62 && typeof error62 === "object" && "code" in error62 ? error62.code : void 0;
2023
+ return code === "ENOENT" || code === "ENOTDIR" ? "missing" : null;
2024
+ }
2025
+ }
2026
+ function readSmallText(filePath) {
2027
+ try {
2028
+ return readRegularFile(filePath, MAX_OUTPUT_BYTES).toString("utf8").trim();
2029
+ } catch {
2030
+ return null;
2031
+ }
2032
+ }
2033
+ function findGitDirectory(cwd) {
2034
+ let current = path.resolve(cwd);
2035
+ for (; ; ) {
2036
+ const candidate = path.join(current, ".git");
2037
+ let stat;
2038
+ try {
2039
+ stat = fs.statSync(candidate);
2040
+ } catch {
2041
+ stat = void 0;
2042
+ }
2043
+ if (stat?.isDirectory()) {
2044
+ return { worktreeRoot: current, gitDir: candidate, commonDir: candidate };
2045
+ }
2046
+ if (stat?.isFile()) {
2047
+ const pointer = readSmallText(candidate);
2048
+ const match = pointer ? /^gitdir:\s*(.+)$/u.exec(pointer) : null;
2049
+ if (!match?.[1]) return null;
2050
+ const gitDir = path.resolve(current, match[1]);
2051
+ const common = readSmallText(path.join(gitDir, "commondir"));
2052
+ return {
2053
+ worktreeRoot: current,
2054
+ gitDir,
2055
+ commonDir: common ? path.resolve(gitDir, common) : gitDir
2056
+ };
2057
+ }
2058
+ const parent = path.dirname(current);
2059
+ if (parent === current) return null;
2060
+ try {
2061
+ if (fs.statSync(current).dev !== fs.statSync(parent).dev) return null;
2062
+ } catch {
2063
+ return null;
2064
+ }
2065
+ current = parent;
2066
+ }
2067
+ }
2068
+ function checkoutSignature(layout) {
2069
+ const head = readSmallText(path.join(layout.gitDir, "HEAD"));
2070
+ const configIdentity = [
2071
+ fileStatIdentity(path.join(layout.commonDir, "config")),
2072
+ fileStatIdentity(path.join(layout.gitDir, "config.worktree"))
2073
+ ];
2074
+ if (head === null || configIdentity.some((value) => value === null))
2075
+ return { signature: null };
2076
+ const parts = [layout.gitDir, layout.commonDir, head, ...configIdentity];
2077
+ const ref = /^ref:\s*(.+)$/u.exec(head)?.[1];
2078
+ if (ref) {
2079
+ const looseRefPath = path.join(layout.commonDir, ref);
2080
+ const looseRef = readSmallText(looseRefPath);
2081
+ if (/^ref:/u.test(looseRef ?? "")) return { signature: null };
2082
+ const packedIdentity = fileStatIdentity(
2083
+ path.join(layout.commonDir, "packed-refs")
2084
+ );
2085
+ const reftableIdentity = fileContentIdentity(
2086
+ path.join(layout.commonDir, "reftable", "tables.list")
2087
+ );
2088
+ const looseIdentity = looseRef ?? fileStatIdentity(looseRefPath);
2089
+ if (looseIdentity === null || packedIdentity === null || reftableIdentity === null)
2090
+ return { signature: null };
2091
+ if (looseRef === null && looseIdentity !== "missing")
2092
+ return { signature: null };
2093
+ parts.push(looseIdentity, packedIdentity, reftableIdentity);
2094
+ return {
2095
+ // A fresh unborn checkout has no refs yet. Cache its missing-file
2096
+ // identities after Git validates the snapshot; the first ref invalidates it.
2097
+ signature: parts.join("\0"),
2098
+ ...reftableIdentity === "missing" ? { headRef: ref } : {}
2099
+ };
2100
+ }
2101
+ return {
2102
+ signature: parts.join("\0"),
2103
+ .../^(?:[a-f0-9]{40}|[a-f0-9]{64})$/i.test(head) ? { headRef: null } : {}
2104
+ };
2105
+ }
2106
+
1615
2107
  // src/pi/extension.ts
1616
- import { randomUUID as randomUUID4 } from "node:crypto";
1617
- import fs9 from "node:fs";
2108
+ import { randomUUID as randomUUID5 } from "node:crypto";
2109
+ import fs10 from "node:fs";
1618
2110
  import os2 from "node:os";
1619
- import path20 from "node:path";
2111
+ import path22 from "node:path";
1620
2112
 
1621
2113
  // ../../packages/agent-targets/src/pi-agent-dir.ts
1622
- import path from "node:path";
2114
+ import path2 from "node:path";
1623
2115
  function resolvePiAgentDir(context) {
1624
- const pathApi = (context.platform ?? process.platform) === "win32" ? path.win32 : path.posix;
2116
+ const pathApi = (context.platform ?? process.platform) === "win32" ? path2.win32 : path2.posix;
1625
2117
  const configured = context.env.PI_CODING_AGENT_DIR?.trim();
1626
2118
  if (!configured) {
1627
2119
  return pathApi.join(context.homeDir, ".pi", "agent");
@@ -1634,23 +2126,23 @@ function resolvePiAgentDir(context) {
1634
2126
  }
1635
2127
 
1636
2128
  // ../../packages/agent-targets/src/artifacts/targets/claude.ts
1637
- import path4 from "node:path";
2129
+ import path5 from "node:path";
1638
2130
 
1639
2131
  // ../../packages/agent-targets/src/config-paths.ts
1640
- import path2 from "node:path";
2132
+ import path3 from "node:path";
1641
2133
  function claudeConfigDirectory(context) {
1642
2134
  const configuredDir = context.claudeConfigDir ?? context.env?.CLAUDE_CONFIG_DIR;
1643
- return typeof configuredDir === "string" && configuredDir.trim().length > 0 ? path2.resolve(configuredDir) : path2.join(context.homeDir, ".claude");
2135
+ return typeof configuredDir === "string" && configuredDir.trim().length > 0 ? path3.resolve(configuredDir) : path3.join(context.homeDir, ".claude");
1644
2136
  }
1645
2137
  function geminiConfigDirectory(context) {
1646
2138
  const configuredHome = context.env.GEMINI_CLI_HOME;
1647
- const homeRoot = typeof configuredHome === "string" && configuredHome.trim().length > 0 ? path2.resolve(configuredHome) : context.homeDir;
1648
- return path2.join(homeRoot, ".gemini");
2139
+ const homeRoot = typeof configuredHome === "string" && configuredHome.trim().length > 0 ? path3.resolve(configuredHome) : context.homeDir;
2140
+ return path3.join(homeRoot, ".gemini");
1649
2141
  }
1650
2142
 
1651
2143
  // ../../packages/agent-targets/src/artifacts/filesystem.ts
1652
- import fs from "node:fs";
1653
- import path3 from "node:path";
2144
+ import fs2 from "node:fs";
2145
+ import path4 from "node:path";
1654
2146
  var DEFAULT_SKIP_DIRS = /* @__PURE__ */ new Set([
1655
2147
  "node_modules",
1656
2148
  "dist",
@@ -1667,11 +2159,11 @@ var DEFAULT_SKIP_DIRS = /* @__PURE__ */ new Set([
1667
2159
  "designqc-captures"
1668
2160
  ]);
1669
2161
  function normalizeFilePath(filePath) {
1670
- return path3.resolve(filePath);
2162
+ return path4.resolve(filePath);
1671
2163
  }
1672
2164
  function safeReaddir(dir) {
1673
2165
  try {
1674
- return fs.readdirSync(dir, { withFileTypes: true });
2166
+ return fs2.readdirSync(dir, { withFileTypes: true });
1675
2167
  } catch {
1676
2168
  return [];
1677
2169
  }
@@ -1684,7 +2176,7 @@ function walkFiles(input2) {
1684
2176
  function walk(dir, depth) {
1685
2177
  if (depth > input2.maxDepth) return;
1686
2178
  for (const entry of safeReaddir(dir)) {
1687
- const entryPath = path3.join(dir, entry.name);
2179
+ const entryPath = path4.join(dir, entry.name);
1688
2180
  if (entry.isDirectory()) {
1689
2181
  if (!skipDirs.has(entry.name)) walk(entryPath, depth + 1);
1690
2182
  continue;
@@ -1720,7 +2212,7 @@ function transcriptCandidate(input2) {
1720
2212
  }
1721
2213
 
1722
2214
  // ../../packages/agent-targets/src/artifacts/harness-install-id.ts
1723
- import fs2 from "node:fs";
2215
+ import fs3 from "node:fs";
1724
2216
  var MAX_HARNESS_INSTALL_ID_LENGTH = 128;
1725
2217
  function normalizeHarnessInstallId(value) {
1726
2218
  if (typeof value !== "string") return null;
@@ -1731,7 +2223,7 @@ function normalizeHarnessInstallId(value) {
1731
2223
  }
1732
2224
  function readHarnessInstallIdFile(filePath) {
1733
2225
  try {
1734
- return normalizeHarnessInstallId(fs2.readFileSync(filePath, "utf8"));
2226
+ return normalizeHarnessInstallId(fs3.readFileSync(filePath, "utf8"));
1735
2227
  } catch {
1736
2228
  return null;
1737
2229
  }
@@ -1740,7 +2232,7 @@ var claudeMachineIdCache = /* @__PURE__ */ new Map();
1740
2232
  function readClaudeMachineId(configFile) {
1741
2233
  let stat;
1742
2234
  try {
1743
- stat = fs2.statSync(configFile);
2235
+ stat = fs3.statSync(configFile);
1744
2236
  } catch {
1745
2237
  claudeMachineIdCache.delete(configFile);
1746
2238
  return null;
@@ -1751,7 +2243,7 @@ function readClaudeMachineId(configFile) {
1751
2243
  }
1752
2244
  let machineId = null;
1753
2245
  try {
1754
- const parsed = JSON.parse(fs2.readFileSync(configFile, "utf8"));
2246
+ const parsed = JSON.parse(fs3.readFileSync(configFile, "utf8"));
1755
2247
  if (parsed && typeof parsed === "object") {
1756
2248
  machineId = normalizeHarnessInstallId(
1757
2249
  parsed.machineID
@@ -1769,55 +2261,8 @@ function readClaudeMachineId(configFile) {
1769
2261
  }
1770
2262
 
1771
2263
  // ../../packages/agent-targets/src/artifacts/repository-context.ts
1772
- import { createHash } from "node:crypto";
1773
- import fs3 from "node:fs";
1774
-
1775
- // ../../packages/shared/src/github-repository.ts
1776
- var MAX_REPOSITORY_BYTES = 201;
1777
- var UTF8_ENCODER = new TextEncoder();
1778
- function utf8Bytes(value) {
1779
- return UTF8_ENCODER.encode(value).byteLength;
1780
- }
1781
- function githubRepositoryPath(pathname) {
1782
- const parts = pathname.replace(/^\/+|\/+$/g, "").split("/");
1783
- if (parts.length !== 2) return null;
1784
- const owner = parts[0];
1785
- const repository = parts[1]?.replace(/\.git$/i, "");
1786
- if (!owner || !repository) return null;
1787
- const segmentPattern = /^[A-Za-z0-9_.-]+$/;
1788
- if (!segmentPattern.test(owner) || !segmentPattern.test(repository)) {
1789
- return null;
1790
- }
1791
- const canonical = `${owner.toLowerCase()}/${repository.toLowerCase()}`;
1792
- if (canonical.startsWith("./") || canonical.startsWith("../") || repository === "." || repository === ".." || repository.toLowerCase().endsWith(".git")) {
1793
- return null;
1794
- }
1795
- return utf8Bytes(canonical) <= MAX_REPOSITORY_BYTES ? canonical : null;
1796
- }
1797
- function canonicalGitHubRepository(remote) {
1798
- const trimmed = remote.trim();
1799
- if (!trimmed || trimmed.includes("\0")) return null;
1800
- const scp = trimmed.match(/^git@github\.com:([^?#]+)$/i);
1801
- if (scp?.[1]) return githubRepositoryPath(scp[1]);
1802
- let url2;
1803
- try {
1804
- url2 = new URL(trimmed);
1805
- } catch {
1806
- return null;
1807
- }
1808
- if (url2.hostname.toLowerCase() !== "github.com") return null;
1809
- if (url2.search || url2.hash || url2.password || url2.port) return null;
1810
- if (url2.protocol === "https:" || url2.protocol === "http:") {
1811
- if (url2.username) return null;
1812
- } else if (url2.protocol === "ssh:") {
1813
- if (url2.username && url2.username !== "git") return null;
1814
- } else {
1815
- return null;
1816
- }
1817
- return githubRepositoryPath(url2.pathname);
1818
- }
1819
-
1820
- // ../../packages/agent-targets/src/artifacts/repository-context.ts
2264
+ import { createHash as createHash2 } from "node:crypto";
2265
+ import fs4 from "node:fs";
1821
2266
  var MAX_ARTIFACT_CONTEXT_PREFIX_BYTES = 256 * 1024;
1822
2267
  var MAX_ARTIFACT_CONTEXT_RECORDS = 64;
1823
2268
  var ARTIFACT_CONTEXT_READ_CHUNK_BYTES = 32 * 1024;
@@ -1840,12 +2285,12 @@ function recordValue(value) {
1840
2285
  function readJsonlContextRecords(sourcePath) {
1841
2286
  let file2;
1842
2287
  try {
1843
- file2 = fs3.openSync(sourcePath, "r");
2288
+ file2 = fs4.openSync(sourcePath, "r");
1844
2289
  } catch {
1845
2290
  return [];
1846
2291
  }
1847
2292
  try {
1848
- const stat = fs3.fstatSync(file2);
2293
+ const stat = fs4.fstatSync(file2);
1849
2294
  const byteLimit = Math.min(stat.size, MAX_ARTIFACT_CONTEXT_PREFIX_BYTES);
1850
2295
  if (byteLimit <= 0) return [];
1851
2296
  const records = [];
@@ -1863,7 +2308,7 @@ function readJsonlContextRecords(sourcePath) {
1863
2308
  const chunk = Buffer.allocUnsafe(ARTIFACT_CONTEXT_READ_CHUNK_BYTES);
1864
2309
  while (position < byteLimit && records.length < MAX_ARTIFACT_CONTEXT_RECORDS) {
1865
2310
  const want = Math.min(chunk.length, byteLimit - position);
1866
- const bytesRead = fs3.readSync(file2, chunk, 0, want, position);
2311
+ const bytesRead = fs4.readSync(file2, chunk, 0, want, position);
1867
2312
  if (bytesRead <= 0) break;
1868
2313
  position += bytesRead;
1869
2314
  const buffered = Buffer.concat([carry, chunk.subarray(0, bytesRead)]);
@@ -1886,7 +2331,7 @@ function readJsonlContextRecords(sourcePath) {
1886
2331
  } catch {
1887
2332
  return [];
1888
2333
  } finally {
1889
- fs3.closeSync(file2);
2334
+ fs4.closeSync(file2);
1890
2335
  }
1891
2336
  }
1892
2337
  function contextTimestampMs(value, fallback) {
@@ -1914,7 +2359,7 @@ function repositoryObservation(input2) {
1914
2359
  branch ?? null,
1915
2360
  commitHash ?? null
1916
2361
  ]);
1917
- const id = `repo_observation:${createHash("sha256").update(identity).digest("hex")}`;
2362
+ const id = `repo_observation:${createHash2("sha256").update(identity).digest("hex")}`;
1918
2363
  return {
1919
2364
  id,
1920
2365
  repository: input2.repository,
@@ -1925,14 +2370,29 @@ function repositoryObservation(input2) {
1925
2370
  ...commitHash ? { commitHash } : {}
1926
2371
  };
1927
2372
  }
2373
+ function localGitStateMetadata(states, source = "transcript_discovery") {
2374
+ const bounded = [];
2375
+ for (const state of states) {
2376
+ if (!state || Buffer.byteLength(
2377
+ JSON.stringify({ version: 1, source, states: [...bounded, state] }),
2378
+ "utf8"
2379
+ ) > 4 * 1024)
2380
+ continue;
2381
+ bounded.push(state);
2382
+ }
2383
+ return bounded.length ? { localGitStates: { version: 1, source, states: bounded } } : {};
2384
+ }
1928
2385
 
1929
2386
  // ../../packages/agent-targets/src/artifacts/targets/claude.ts
1930
2387
  function claudeTranscriptCandidate(sourcePath, context, harnessInstallId) {
1931
2388
  const fallbackObservedAtMs = (context.nowMs ?? Date.now)();
2389
+ const states = /* @__PURE__ */ new Map();
1932
2390
  const observations = /* @__PURE__ */ new Map();
1933
2391
  for (const record2 of readJsonlContextRecords(sourcePath)) {
1934
2392
  if (typeof record2.cwd !== "string" || !record2.cwd.trim()) continue;
1935
2393
  const origin = context.resolveGitHubOrigin?.(record2.cwd);
2394
+ const state = origin?.gitState ?? context.resolveGitState?.(record2.cwd);
2395
+ if (state) states.set(state.worktreeRoot, state);
1936
2396
  if (!origin) continue;
1937
2397
  const observation = repositoryObservation({
1938
2398
  repository: origin.repository,
@@ -1947,7 +2407,10 @@ function claudeTranscriptCandidate(sourcePath, context, harnessInstallId) {
1947
2407
  return transcriptCandidate({
1948
2408
  targetId: "claude",
1949
2409
  sourcePath,
1950
- metadata: harnessInstallId ? { harnessInstallId } : void 0,
2410
+ metadata: {
2411
+ ...harnessInstallId ? { harnessInstallId } : {},
2412
+ ...localGitStateMetadata([...states.values()])
2413
+ },
1951
2414
  repositoryObservations: [...observations.values()]
1952
2415
  });
1953
2416
  }
@@ -1956,9 +2419,9 @@ var claudeArtifactDiscovery = Object.freeze({
1956
2419
  discover: (context) => {
1957
2420
  const { homeDir, env } = context;
1958
2421
  const configDir = claudeConfigDirectory({ homeDir, env });
1959
- const harnessInstallId = readClaudeMachineId(path4.join(configDir, ".claude.json")) ?? readClaudeMachineId(path4.join(homeDir, ".claude.json"));
2422
+ const harnessInstallId = readClaudeMachineId(path5.join(configDir, ".claude.json")) ?? readClaudeMachineId(path5.join(homeDir, ".claude.json"));
1960
2423
  return walkFiles({
1961
- root: path4.join(configDir, "projects"),
2424
+ root: path5.join(configDir, "projects"),
1962
2425
  maxDepth: 6,
1963
2426
  includeFile: (filePath) => filePath.endsWith(".jsonl")
1964
2427
  }).map(
@@ -1968,10 +2431,10 @@ var claudeArtifactDiscovery = Object.freeze({
1968
2431
  });
1969
2432
 
1970
2433
  // ../../packages/agent-targets/src/artifacts/targets/codex.ts
1971
- import path5 from "node:path";
2434
+ import path6 from "node:path";
1972
2435
  var CODEX_ROLLOUT_ID = /-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl(?:\.zst)?$/i;
1973
2436
  function codexRolloutId(sourcePath) {
1974
- return CODEX_ROLLOUT_ID.exec(path5.basename(sourcePath))?.[1]?.toLowerCase() ?? null;
2437
+ return CODEX_ROLLOUT_ID.exec(path6.basename(sourcePath))?.[1]?.toLowerCase() ?? null;
1975
2438
  }
1976
2439
  function codexTranscriptCandidate(sourcePath, readPath, store, context, harnessInstallId) {
1977
2440
  const rolloutId = codexRolloutId(sourcePath);
@@ -1984,9 +2447,10 @@ function codexTranscriptCandidate(sourcePath, readPath, store, context, harnessI
1984
2447
  if (!payload) continue;
1985
2448
  const sessionId = typeof payload.id === "string" && payload.id.trim() ? payload.id.trim() : null;
1986
2449
  const cwd = typeof payload.cwd === "string" ? payload.cwd : void 0;
1987
- const git2 = recordValue(payload.git);
1988
- const nativeRepository = typeof git2?.repository_url === "string" ? canonicalGitHubRepository(git2.repository_url) : null;
2450
+ const git = recordValue(payload.git);
2451
+ const nativeRepository = typeof git?.repository_url === "string" ? canonicalGitHubRepository(git.repository_url) : null;
1989
2452
  const origin = cwd ? context.resolveGitHubOrigin?.(cwd) : null;
2453
+ const state = origin?.gitState ?? (cwd ? context.resolveGitState?.(cwd) : null);
1990
2454
  const observations = [];
1991
2455
  if (nativeRepository) {
1992
2456
  observations.push(
@@ -1998,8 +2462,8 @@ function codexTranscriptCandidate(sourcePath, readPath, store, context, harnessI
1998
2462
  fallbackObservedAtMs
1999
2463
  ),
2000
2464
  cwd,
2001
- branch: git2?.branch,
2002
- commitHash: git2?.commit_hash
2465
+ branch: git?.branch,
2466
+ commitHash: git?.commit_hash
2003
2467
  })
2004
2468
  );
2005
2469
  }
@@ -2021,7 +2485,11 @@ function codexTranscriptCandidate(sourcePath, readPath, store, context, harnessI
2021
2485
  identityKey,
2022
2486
  readPath,
2023
2487
  sessionId,
2024
- metadata: { store, ...identityMetadata },
2488
+ metadata: {
2489
+ store,
2490
+ ...identityMetadata,
2491
+ ...localGitStateMetadata([state])
2492
+ },
2025
2493
  repositoryObservations: observations
2026
2494
  });
2027
2495
  }
@@ -2041,15 +2509,15 @@ var codexArtifactDiscovery = Object.freeze({
2041
2509
  discover: (context) => {
2042
2510
  const { homeDir, env } = context;
2043
2511
  const configuredHome = env.CODEX_HOME?.trim();
2044
- const codexHome = configuredHome ? path5.resolve(configuredHome) : path5.join(homeDir, ".codex");
2512
+ const codexHome = configuredHome ? path6.resolve(configuredHome) : path6.join(homeDir, ".codex");
2045
2513
  const harnessInstallId = readHarnessInstallIdFile(
2046
- path5.join(codexHome, "installation_id")
2514
+ path6.join(codexHome, "installation_id")
2047
2515
  );
2048
2516
  const roots = [
2049
- { store: "sessions", root: path5.join(codexHome, "sessions") },
2517
+ { store: "sessions", root: path6.join(codexHome, "sessions") },
2050
2518
  {
2051
2519
  store: "archived_sessions",
2052
- root: path5.join(codexHome, "archived_sessions")
2520
+ root: path6.join(codexHome, "archived_sessions")
2053
2521
  }
2054
2522
  ];
2055
2523
  const files = roots.flatMap(
@@ -2087,27 +2555,27 @@ var codexArtifactDiscovery = Object.freeze({
2087
2555
  });
2088
2556
 
2089
2557
  // ../../packages/agent-targets/src/artifacts/targets/copilot-cli.ts
2090
- import path6 from "node:path";
2558
+ import path7 from "node:path";
2091
2559
  var copilotCliArtifactDiscovery = Object.freeze({
2092
2560
  targetId: "copilot-cli",
2093
2561
  discover: ({ homeDir }) => walkFiles({
2094
- root: path6.join(homeDir, ".copilot", "session-state"),
2562
+ root: path7.join(homeDir, ".copilot", "session-state"),
2095
2563
  maxDepth: 3,
2096
- includeFile: (filePath) => path6.basename(filePath) === "events.jsonl"
2564
+ includeFile: (filePath) => path7.basename(filePath) === "events.jsonl"
2097
2565
  }).map(
2098
2566
  (sourcePath) => transcriptCandidate({ targetId: "copilot-cli", sourcePath })
2099
2567
  )
2100
2568
  });
2101
2569
 
2102
2570
  // ../../packages/agent-targets/src/artifacts/targets/cursor.ts
2103
- import path7 from "node:path";
2571
+ import path8 from "node:path";
2104
2572
  function hasPathSegment(filePath, segment) {
2105
- return normalizeFilePath(filePath).split(path7.sep).includes(segment);
2573
+ return normalizeFilePath(filePath).split(path8.sep).includes(segment);
2106
2574
  }
2107
2575
  var cursorArtifactDiscovery = Object.freeze({
2108
2576
  targetId: "cursor",
2109
2577
  discover: ({ homeDir }) => walkFiles({
2110
- root: path7.join(homeDir, ".cursor", "projects"),
2578
+ root: path8.join(homeDir, ".cursor", "projects"),
2111
2579
  maxDepth: 5,
2112
2580
  includeFile: (filePath) => filePath.endsWith(".jsonl") && hasPathSegment(filePath, "agent-transcripts")
2113
2581
  }).map(
@@ -2116,11 +2584,11 @@ var cursorArtifactDiscovery = Object.freeze({
2116
2584
  });
2117
2585
 
2118
2586
  // ../../packages/agent-targets/src/artifacts/targets/factory-droid.ts
2119
- import path8 from "node:path";
2587
+ import path9 from "node:path";
2120
2588
  var factoryDroidArtifactDiscovery = Object.freeze({
2121
2589
  targetId: "factory-droid",
2122
2590
  discover: ({ homeDir }) => walkFiles({
2123
- root: path8.join(homeDir, ".factory", "sessions"),
2591
+ root: path9.join(homeDir, ".factory", "sessions"),
2124
2592
  maxDepth: 4,
2125
2593
  includeFile: (filePath) => filePath.endsWith(".jsonl")
2126
2594
  }).map(
@@ -2129,7 +2597,7 @@ var factoryDroidArtifactDiscovery = Object.freeze({
2129
2597
  });
2130
2598
 
2131
2599
  // ../../packages/agent-targets/src/artifacts/targets/gemini.ts
2132
- import path9 from "node:path";
2600
+ import path10 from "node:path";
2133
2601
  var GEMINI_DISCOVER_SKIP_DIRS = /* @__PURE__ */ new Set([
2134
2602
  "bin",
2135
2603
  "checkpoints",
@@ -2144,15 +2612,15 @@ var geminiArtifactDiscovery = Object.freeze({
2144
2612
  discover: ({ homeDir, env }) => {
2145
2613
  const geminiDir = geminiConfigDirectory({ homeDir, env });
2146
2614
  const harnessInstallId = readHarnessInstallIdFile(
2147
- path9.join(geminiDir, "installation_id")
2615
+ path10.join(geminiDir, "installation_id")
2148
2616
  );
2149
- const root = path9.join(geminiDir, "tmp");
2617
+ const root = path10.join(geminiDir, "tmp");
2150
2618
  const files = [];
2151
2619
  const seen = /* @__PURE__ */ new Set();
2152
2620
  function walk(dir, depth) {
2153
2621
  if (depth > 4) return;
2154
2622
  for (const entry of safeReaddir(dir)) {
2155
- const entryPath = path9.join(dir, entry.name);
2623
+ const entryPath = path10.join(dir, entry.name);
2156
2624
  if (!entry.isDirectory()) continue;
2157
2625
  if (GEMINI_DISCOVER_SKIP_DIRS.has(entry.name)) continue;
2158
2626
  if (entry.name === "chats") {
@@ -2161,7 +2629,7 @@ var geminiArtifactDiscovery = Object.freeze({
2161
2629
  continue;
2162
2630
  }
2163
2631
  const sourcePath = normalizeFilePath(
2164
- path9.join(entryPath, chatEntry.name)
2632
+ path10.join(entryPath, chatEntry.name)
2165
2633
  );
2166
2634
  if (seen.has(sourcePath)) continue;
2167
2635
  seen.add(sourcePath);
@@ -2184,52 +2652,52 @@ var geminiArtifactDiscovery = Object.freeze({
2184
2652
  });
2185
2653
 
2186
2654
  // ../../packages/agent-targets/src/artifacts/targets/grok.ts
2187
- import path10 from "node:path";
2655
+ import path11 from "node:path";
2188
2656
  function grokHome(homeDir, env) {
2189
2657
  const configuredHome = env.GROK_HOME?.trim();
2190
- return configuredHome ? path10.resolve(configuredHome) : path10.join(homeDir, ".grok");
2658
+ return configuredHome ? path11.resolve(configuredHome) : path11.join(homeDir, ".grok");
2191
2659
  }
2192
2660
  var grokArtifactDiscovery = Object.freeze({
2193
2661
  targetId: "grok",
2194
2662
  discover: ({ homeDir, env }) => walkFiles({
2195
- root: path10.join(grokHome(homeDir, env), "sessions"),
2663
+ root: path11.join(grokHome(homeDir, env), "sessions"),
2196
2664
  maxDepth: 5,
2197
- includeFile: (filePath) => path10.basename(filePath) === "updates.jsonl"
2665
+ includeFile: (filePath) => path11.basename(filePath) === "updates.jsonl"
2198
2666
  }).map(
2199
2667
  (sourcePath) => transcriptCandidate({ targetId: "grok", sourcePath })
2200
2668
  )
2201
2669
  });
2202
2670
 
2203
2671
  // ../../packages/agent-targets/src/artifacts/targets/mini-swe-agent.ts
2204
- import fs4 from "node:fs";
2205
- import path11 from "node:path";
2672
+ import fs5 from "node:fs";
2673
+ import path12 from "node:path";
2206
2674
  var SESSION_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2207
2675
  function dataDirectory({ homeDir, env }) {
2208
2676
  if (env.FML_DATA_DIR?.trim()) {
2209
- if (!path11.isAbsolute(env.FML_DATA_DIR)) {
2677
+ if (!path12.isAbsolute(env.FML_DATA_DIR)) {
2210
2678
  throw new Error("FML_DATA_DIR must be an absolute path");
2211
2679
  }
2212
- return path11.normalize(env.FML_DATA_DIR);
2680
+ return path12.normalize(env.FML_DATA_DIR);
2213
2681
  }
2214
2682
  if (process.platform === "darwin") {
2215
- return path11.join(homeDir, "Library", "Application Support", "fml");
2683
+ return path12.join(homeDir, "Library", "Application Support", "fml");
2216
2684
  }
2217
2685
  if (process.platform === "win32") {
2218
- const roaming = env.APPDATA && path11.isAbsolute(env.APPDATA) ? env.APPDATA : path11.join(homeDir, "AppData", "Roaming");
2219
- return path11.join(roaming, "fml");
2686
+ const roaming = env.APPDATA && path12.isAbsolute(env.APPDATA) ? env.APPDATA : path12.join(homeDir, "AppData", "Roaming");
2687
+ return path12.join(roaming, "fml");
2220
2688
  }
2221
- const xdgDataHome = env.XDG_DATA_HOME && path11.isAbsolute(env.XDG_DATA_HOME) ? env.XDG_DATA_HOME : path11.join(homeDir, ".local", "share");
2222
- return path11.join(xdgDataHome, "fml");
2689
+ const xdgDataHome = env.XDG_DATA_HOME && path12.isAbsolute(env.XDG_DATA_HOME) ? env.XDG_DATA_HOME : path12.join(homeDir, ".local", "share");
2690
+ return path12.join(xdgDataHome, "fml");
2223
2691
  }
2224
2692
  function snapshotCandidate(sourcePath, context) {
2225
2693
  let file2;
2226
2694
  try {
2227
- file2 = fs4.openSync(sourcePath, "r");
2695
+ file2 = fs5.openSync(sourcePath, "r");
2228
2696
  } catch {
2229
2697
  return null;
2230
2698
  }
2231
2699
  try {
2232
- const stat = fs4.fstatSync(file2);
2700
+ const stat = fs5.fstatSync(file2);
2233
2701
  if (!stat.isFile()) return null;
2234
2702
  if (context.maxFileBytes !== void 0 && stat.size > context.maxFileBytes) {
2235
2703
  return transcriptCandidate({ targetId: "mini-swe-agent", sourcePath });
@@ -2237,7 +2705,7 @@ function snapshotCandidate(sourcePath, context) {
2237
2705
  const bytes = Buffer.allocUnsafe(stat.size);
2238
2706
  let offset = 0;
2239
2707
  while (offset < bytes.length) {
2240
- const read = fs4.readSync(
2708
+ const read = fs5.readSync(
2241
2709
  file2,
2242
2710
  bytes,
2243
2711
  offset,
@@ -2247,7 +2715,7 @@ function snapshotCandidate(sourcePath, context) {
2247
2715
  if (read <= 0) return null;
2248
2716
  offset += read;
2249
2717
  }
2250
- if (fs4.fstatSync(file2).size !== stat.size) return null;
2718
+ if (fs5.fstatSync(file2).size !== stat.size) return null;
2251
2719
  const value = JSON.parse(bytes.toString("utf8"));
2252
2720
  if (typeof value !== "object" || value === null || !("fml" in value)) {
2253
2721
  return null;
@@ -2277,13 +2745,13 @@ function snapshotCandidate(sourcePath, context) {
2277
2745
  } catch {
2278
2746
  return null;
2279
2747
  } finally {
2280
- fs4.closeSync(file2);
2748
+ fs5.closeSync(file2);
2281
2749
  }
2282
2750
  }
2283
2751
  var miniSweAgentArtifactDiscovery = Object.freeze({
2284
2752
  targetId: "mini-swe-agent",
2285
2753
  discover: (context) => walkFiles({
2286
- root: path11.join(dataDirectory(context), "mini-swe-agent", "trajectories"),
2754
+ root: path12.join(dataDirectory(context), "mini-swe-agent", "trajectories"),
2287
2755
  maxDepth: 0,
2288
2756
  includeFile: (filePath) => filePath.endsWith(".traj.json")
2289
2757
  }).flatMap((sourcePath) => {
@@ -2293,7 +2761,7 @@ var miniSweAgentArtifactDiscovery = Object.freeze({
2293
2761
  });
2294
2762
 
2295
2763
  // ../../packages/agent-targets/src/artifacts/targets/pi.ts
2296
- import path12 from "node:path";
2764
+ import path13 from "node:path";
2297
2765
 
2298
2766
  // ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/external.js
2299
2767
  var external_exports = {};
@@ -3112,10 +3580,10 @@ function mergeDefs(...defs) {
3112
3580
  function cloneDef(schema) {
3113
3581
  return mergeDefs(schema._zod.def);
3114
3582
  }
3115
- function getElementAtPath(obj, path21) {
3116
- if (!path21)
3583
+ function getElementAtPath(obj, path23) {
3584
+ if (!path23)
3117
3585
  return obj;
3118
- return path21.reduce((acc, key) => acc?.[key], obj);
3586
+ return path23.reduce((acc, key) => acc?.[key], obj);
3119
3587
  }
3120
3588
  function promiseAllObject(promisesObj) {
3121
3589
  const keys = Object.keys(promisesObj);
@@ -3455,11 +3923,11 @@ function explicitlyAborted(x, startIndex = 0) {
3455
3923
  }
3456
3924
  return false;
3457
3925
  }
3458
- function prefixIssues(path21, issues) {
3926
+ function prefixIssues(path23, issues) {
3459
3927
  return issues.map((iss) => {
3460
3928
  var _a3;
3461
3929
  (_a3 = iss).path ?? (_a3.path = []);
3462
- iss.path.unshift(path21);
3930
+ iss.path.unshift(path23);
3463
3931
  return iss;
3464
3932
  });
3465
3933
  }
@@ -3921,16 +4389,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
3921
4389
  }
3922
4390
  function formatError(error62, mapper = (issue2) => issue2.message) {
3923
4391
  const fieldErrors = { _errors: [] };
3924
- const processError = (error63, path21 = []) => {
4392
+ const processError = (error63, path23 = []) => {
3925
4393
  for (const issue2 of error63.issues) {
3926
4394
  if (issue2.code === "invalid_union" && issue2.errors.length) {
3927
- issue2.errors.map((issues) => processError({ issues }, [...path21, ...issue2.path]));
4395
+ issue2.errors.map((issues) => processError({ issues }, [...path23, ...issue2.path]));
3928
4396
  } else if (issue2.code === "invalid_key") {
3929
- processError({ issues: issue2.issues }, [...path21, ...issue2.path]);
4397
+ processError({ issues: issue2.issues }, [...path23, ...issue2.path]);
3930
4398
  } else if (issue2.code === "invalid_element") {
3931
- processError({ issues: issue2.issues }, [...path21, ...issue2.path]);
4399
+ processError({ issues: issue2.issues }, [...path23, ...issue2.path]);
3932
4400
  } else {
3933
- const fullpath = [...path21, ...issue2.path];
4401
+ const fullpath = [...path23, ...issue2.path];
3934
4402
  if (fullpath.length === 0) {
3935
4403
  fieldErrors._errors.push(mapper(issue2));
3936
4404
  } else {
@@ -3969,17 +4437,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
3969
4437
  }
3970
4438
  function treeifyError(error62, mapper = (issue2) => issue2.message) {
3971
4439
  const result = { errors: [] };
3972
- const processError = (error63, path21 = []) => {
4440
+ const processError = (error63, path23 = []) => {
3973
4441
  var _a3;
3974
4442
  for (const issue2 of error63.issues) {
3975
4443
  if (issue2.code === "invalid_union" && issue2.errors.length) {
3976
- issue2.errors.map((issues) => processError({ issues }, [...path21, ...issue2.path]));
4444
+ issue2.errors.map((issues) => processError({ issues }, [...path23, ...issue2.path]));
3977
4445
  } else if (issue2.code === "invalid_key") {
3978
- processError({ issues: issue2.issues }, [...path21, ...issue2.path]);
4446
+ processError({ issues: issue2.issues }, [...path23, ...issue2.path]);
3979
4447
  } else if (issue2.code === "invalid_element") {
3980
- processError({ issues: issue2.issues }, [...path21, ...issue2.path]);
4448
+ processError({ issues: issue2.issues }, [...path23, ...issue2.path]);
3981
4449
  } else {
3982
- const fullpath = [...path21, ...issue2.path];
4450
+ const fullpath = [...path23, ...issue2.path];
3983
4451
  if (fullpath.length === 0) {
3984
4452
  result.errors.push(mapper(issue2));
3985
4453
  continue;
@@ -4018,8 +4486,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
4018
4486
  }
4019
4487
  function toDotPath(_path) {
4020
4488
  const segs = [];
4021
- const path21 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
4022
- for (const seg of path21) {
4489
+ const path23 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
4490
+ for (const seg of path23) {
4023
4491
  if (typeof seg === "number")
4024
4492
  segs.push(`[${seg}]`);
4025
4493
  else if (typeof seg === "symbol")
@@ -21157,13 +21625,13 @@ function resolveRef(ref, ctx) {
21157
21625
  if (!ref.startsWith("#")) {
21158
21626
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
21159
21627
  }
21160
- const path21 = ref.slice(1).split("/").filter(Boolean);
21161
- if (path21.length === 0) {
21628
+ const path23 = ref.slice(1).split("/").filter(Boolean);
21629
+ if (path23.length === 0) {
21162
21630
  return ctx.rootSchema;
21163
21631
  }
21164
21632
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
21165
- if (path21[0] === defsKey) {
21166
- const key = path21[1] === void 0 ? void 0 : decodeJSONPointerSegment(path21[1]);
21633
+ if (path23[0] === defsKey) {
21634
+ const key = path23[1] === void 0 ? void 0 : decodeJSONPointerSegment(path23[1]);
21167
21635
  if (!key || !ctx.defs[key]) {
21168
21636
  throw new Error(`Reference not found: ${ref}`);
21169
21637
  }
@@ -22017,6 +22485,12 @@ var PI_REPOSITORY_ORIGIN_ENTRY_TYPE = "fml-repository-origin";
22017
22485
  var RepositorySlugSchema = external_exports.string().regex(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u).transform((slug) => canonicalGitHubRepository(`https://github.com/${slug}`)).pipe(external_exports.string());
22018
22486
  var PiRepositoryOriginDataSchema = external_exports.object({
22019
22487
  version: external_exports.literal(1),
22488
+ gitState: external_exports.unknown().transform((value, ctx) => {
22489
+ const state = parseGitState(value);
22490
+ if (state) return state;
22491
+ ctx.addIssue({ code: "custom", message: "Invalid Git state snapshot" });
22492
+ return external_exports.NEVER;
22493
+ }).nullable().optional(),
22020
22494
  repository: RepositorySlugSchema.nullable(),
22021
22495
  cwd: external_exports.string().min(1).refine(
22022
22496
  (cwd) => cwd.trim().length > 0 && !new RegExp("\\p{Cc}", "u").test(cwd) && Buffer.byteLength(cwd, "utf8") <= 4096
@@ -22058,12 +22532,15 @@ function piTranscriptCandidate(sourcePath) {
22058
22532
  targetId: "pi",
22059
22533
  sourcePath,
22060
22534
  sessionId,
22535
+ metadata: localGitStateMetadata([snapshot?.gitState], "pi_origin"),
22061
22536
  repositoryObservations: snapshot?.repository ? [
22062
22537
  repositoryObservation({
22063
22538
  repository: snapshot.repository,
22064
22539
  source: "git_origin",
22065
22540
  observedAtMs: snapshot.observedAtMs,
22066
- cwd: snapshot.cwd
22541
+ cwd: snapshot.cwd,
22542
+ branch: snapshot.gitState?.branch,
22543
+ commitHash: snapshot.gitState?.commitHash
22067
22544
  })
22068
22545
  ] : []
22069
22546
  });
@@ -22072,7 +22549,7 @@ var piArtifactDiscovery = Object.freeze({
22072
22549
  targetId: "pi",
22073
22550
  discover: (context) => {
22074
22551
  return walkFiles({
22075
- root: path12.join(resolvePiAgentDir(context), "sessions"),
22552
+ root: path13.join(resolvePiAgentDir(context), "sessions"),
22076
22553
  maxDepth: 8,
22077
22554
  includeFile: (filePath) => filePath.endsWith(".jsonl"),
22078
22555
  skipDirs: PI_SKIP_DIRS
@@ -22098,9 +22575,9 @@ var MAX_MATERIALIZED_ARTIFACT_BYTES = 100 * 1024 * 1024;
22098
22575
  var DEFAULT_MATERIALIZED_CACHE_BYTES = 2 * 1024 * 1024 * 1024;
22099
22576
 
22100
22577
  // ../../packages/agent-targets/src/install/pi/index.ts
22101
- import fs5 from "node:fs";
22102
- import path13 from "node:path";
22103
- import { randomUUID } from "node:crypto";
22578
+ import fs6 from "node:fs";
22579
+ import path14 from "node:path";
22580
+ import { randomUUID as randomUUID2 } from "node:crypto";
22104
22581
  var FML_PI_EXTENSION_MARKER = "__FML_PI_EXTENSION_V1__";
22105
22582
  var LEGACY_PI_EXTENSION_MARKER = "__panopticonPiExtensionTest";
22106
22583
  var LEGACY_PI_EXTENSION_SIGNATURES = Object.freeze([
@@ -22112,11 +22589,11 @@ var LEGACY_PI_EXTENSION_SIGNATURES = Object.freeze([
22112
22589
  "SessionEnd"
22113
22590
  ]);
22114
22591
  function pathApiFor(platform) {
22115
- return platform === "win32" ? path13.win32 : path13.posix;
22592
+ return platform === "win32" ? path14.win32 : path14.posix;
22116
22593
  }
22117
22594
  function readFileIfPresent(filePath) {
22118
22595
  try {
22119
- return fs5.readFileSync(filePath, "utf8");
22596
+ return fs6.readFileSync(filePath, "utf8");
22120
22597
  } catch (error62) {
22121
22598
  if (error62.code === "ENOENT") return null;
22122
22599
  throw error62;
@@ -22133,23 +22610,23 @@ function removeLegacyExtension(filePath) {
22133
22610
  if (!isOwnedExtension(source, true)) {
22134
22611
  return { removed: false, preserved: true };
22135
22612
  }
22136
- fs5.rmSync(filePath);
22613
+ fs6.rmSync(filePath);
22137
22614
  return { removed: true, preserved: false };
22138
22615
  }
22139
22616
  function writeExtensionAtomically(filePath, source) {
22140
- fs5.mkdirSync(path13.dirname(filePath), { recursive: true, mode: 448 });
22141
- const temporaryPath = path13.join(
22142
- path13.dirname(filePath),
22143
- `.${path13.basename(filePath)}.${process.pid}.${randomUUID()}.tmp`
22617
+ fs6.mkdirSync(path14.dirname(filePath), { recursive: true, mode: 448 });
22618
+ const temporaryPath = path14.join(
22619
+ path14.dirname(filePath),
22620
+ `.${path14.basename(filePath)}.${process.pid}.${randomUUID2()}.tmp`
22144
22621
  );
22145
22622
  try {
22146
- fs5.writeFileSync(temporaryPath, source, { encoding: "utf8", mode: 384 });
22147
- if (process.platform === "win32" && fs5.existsSync(filePath)) {
22148
- fs5.rmSync(filePath);
22623
+ fs6.writeFileSync(temporaryPath, source, { encoding: "utf8", mode: 384 });
22624
+ if (process.platform === "win32" && fs6.existsSync(filePath)) {
22625
+ fs6.rmSync(filePath);
22149
22626
  }
22150
- fs5.renameSync(temporaryPath, filePath);
22627
+ fs6.renameSync(temporaryPath, filePath);
22151
22628
  } finally {
22152
- fs5.rmSync(temporaryPath, { force: true });
22629
+ fs6.rmSync(temporaryPath, { force: true });
22153
22630
  }
22154
22631
  }
22155
22632
  var PI_EXTENSION_INSTALLER = Object.freeze({
@@ -22172,7 +22649,7 @@ var PI_EXTENSION_INSTALLER = Object.freeze({
22172
22649
  };
22173
22650
  },
22174
22651
  install(paths) {
22175
- const source = fs5.readFileSync(paths.sourcePath, "utf8");
22652
+ const source = fs6.readFileSync(paths.sourcePath, "utf8");
22176
22653
  if (!source.includes(FML_PI_EXTENSION_MARKER)) {
22177
22654
  throw new Error(
22178
22655
  `Packaged Pi extension is invalid: ${paths.sourcePath} does not contain the FML ownership marker`
@@ -22221,7 +22698,7 @@ var PI_EXTENSION_INSTALLER = Object.freeze({
22221
22698
  legacyPreserved: legacy.preserved
22222
22699
  };
22223
22700
  }
22224
- fs5.rmSync(paths.destinationPath);
22701
+ fs6.rmSync(paths.destinationPath);
22225
22702
  return {
22226
22703
  status: "removed",
22227
22704
  destinationPath: paths.destinationPath,
@@ -22232,11 +22709,11 @@ var PI_EXTENSION_INSTALLER = Object.freeze({
22232
22709
  });
22233
22710
 
22234
22711
  // src/hooks/context.ts
22235
- import { createHash as createHash5 } from "node:crypto";
22236
- import path19 from "node:path";
22712
+ import { createHash as createHash6 } from "node:crypto";
22713
+ import path21 from "node:path";
22237
22714
 
22238
22715
  // ../../packages/agent-targets/src/hooks/normalize.ts
22239
- import { createHash as createHash2 } from "node:crypto";
22716
+ import { createHash as createHash3 } from "node:crypto";
22240
22717
 
22241
22718
  // ../../packages/agent-targets/src/hooks/types.ts
22242
22719
  var HOOK_TARGETS = Object.freeze([
@@ -22352,7 +22829,7 @@ var MINI_SWE_AGENT_HOOK_EVENTS = Object.freeze([
22352
22829
  ]);
22353
22830
 
22354
22831
  // ../../packages/agent-targets/src/hooks/read-command.ts
22355
- import path14 from "node:path";
22832
+ import path15 from "node:path";
22356
22833
  var SHELL_TOOL_NAMES = /* @__PURE__ */ new Set([
22357
22834
  "bash",
22358
22835
  "exec_command",
@@ -22468,12 +22945,12 @@ function splitCommandSegments(tokens) {
22468
22945
  return segments;
22469
22946
  }
22470
22947
  function stripSimpleOptions(args, flagsWithValues = /* @__PURE__ */ new Set(), patternFlags = /* @__PURE__ */ new Set()) {
22471
- const remaining = [];
22948
+ const remaining2 = [];
22472
22949
  let consumedPatternFlag = false;
22473
22950
  for (let index = 0; index < args.length; index += 1) {
22474
22951
  const argument = args[index];
22475
22952
  if (argument === "--") {
22476
- remaining.push(...args.slice(index + 1));
22953
+ remaining2.push(...args.slice(index + 1));
22477
22954
  break;
22478
22955
  }
22479
22956
  if (argument.startsWith("-") && argument.length > 1) {
@@ -22483,9 +22960,9 @@ function stripSimpleOptions(args, flagsWithValues = /* @__PURE__ */ new Set(), p
22483
22960
  }
22484
22961
  continue;
22485
22962
  }
22486
- remaining.push(argument);
22963
+ remaining2.push(argument);
22487
22964
  }
22488
- return { args: remaining, consumedPatternFlag };
22965
+ return { args: remaining2, consumedPatternFlag };
22489
22966
  }
22490
22967
  function readCommandFileArgs(args, flagsWithValues, patternFlags = flagsWithValues) {
22491
22968
  const stripped = stripSimpleOptions(args, flagsWithValues, patternFlags);
@@ -22496,7 +22973,7 @@ function isPlausibleReadPath(argument) {
22496
22973
  if (COMMAND_SEPARATORS.has(argument)) return false;
22497
22974
  if (/[\0*?[{}`$]/.test(argument)) return false;
22498
22975
  if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(argument)) return false;
22499
- return path14.posix.isAbsolute(argument) || path14.win32.isAbsolute(argument) || argument.startsWith("~/") || argument.startsWith("./") || argument.startsWith("../") || argument.includes("/") || /\.[A-Za-z0-9][A-Za-z0-9_-]*$/.test(argument) || /^[A-Z][A-Za-z0-9_.-]*$/.test(argument);
22976
+ return path15.posix.isAbsolute(argument) || path15.win32.isAbsolute(argument) || argument.startsWith("~/") || argument.startsWith("./") || argument.startsWith("../") || argument.includes("/") || /\.[A-Za-z0-9][A-Za-z0-9_-]*$/.test(argument) || /^[A-Z][A-Za-z0-9_.-]*$/.test(argument);
22500
22977
  }
22501
22978
  function filePathFromSegment(tokens) {
22502
22979
  const commandTokens = [...tokens];
@@ -22567,7 +23044,7 @@ function extractReadFilePathFromCommand(command) {
22567
23044
 
22568
23045
  // ../../packages/agent-targets/src/hooks/normalize.ts
22569
23046
  var MAX_EXTRACTED_TEXT_BYTES = 64 * 1024;
22570
- function isRecord(value) {
23047
+ function isRecord3(value) {
22571
23048
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
22572
23049
  }
22573
23050
  function firstString(record2, keys) {
@@ -22614,7 +23091,7 @@ function boundedExtractedText(value, field) {
22614
23091
  if (value === void 0) return void 0;
22615
23092
  const originalBytes = Buffer.byteLength(value, "utf8");
22616
23093
  if (originalBytes <= MAX_EXTRACTED_TEXT_BYTES) return value;
22617
- const digest = createHash2("sha256").update(value).digest("hex");
23094
+ const digest = createHash3("sha256").update(value).digest("hex");
22618
23095
  const marker = `
22619
23096
  [fml:${field}:truncated originalBytes=${originalBytes} sha256=${digest}]`;
22620
23097
  return `${utf8Prefix(
@@ -22625,7 +23102,7 @@ function boundedExtractedText(value, field) {
22625
23102
  function nestedRecord(record2, keys) {
22626
23103
  for (const key of keys) {
22627
23104
  const value = record2[key];
22628
- if (isRecord(value)) return value;
23105
+ if (isRecord3(value)) return value;
22629
23106
  }
22630
23107
  return void 0;
22631
23108
  }
@@ -22683,9 +23160,9 @@ function grokEventType(eventType) {
22683
23160
  }
22684
23161
  function geminiUserPrompt(payload) {
22685
23162
  const request = payload.llm_request;
22686
- if (!isRecord(request) || !Array.isArray(request.messages)) return void 0;
23163
+ if (!isRecord3(request) || !Array.isArray(request.messages)) return void 0;
22687
23164
  const userMessage = [...request.messages].reverse().find(
22688
- (message) => isRecord(message) && message.role === "user"
23165
+ (message) => isRecord3(message) && message.role === "user"
22689
23166
  );
22690
23167
  if (!userMessage) return void 0;
22691
23168
  if (typeof userMessage.content === "string") {
@@ -22693,7 +23170,7 @@ function geminiUserPrompt(payload) {
22693
23170
  }
22694
23171
  if (!Array.isArray(userMessage.content)) return void 0;
22695
23172
  const text = userMessage.content.filter(
22696
- (part) => isRecord(part) && part.type === "text" && typeof part.text === "string"
23173
+ (part) => isRecord3(part) && part.type === "text" && typeof part.text === "string"
22697
23174
  ).map((part) => part.text).join("\n");
22698
23175
  return text || void 0;
22699
23176
  }
@@ -22792,51 +23269,15 @@ init_token_store();
22792
23269
  init_dirs();
22793
23270
 
22794
23271
  // src/repo.ts
22795
- import { execFile, execFileSync as execFileSync2 } from "node:child_process";
22796
- var REPO_CACHE = /* @__PURE__ */ new Map();
22797
- var GIT_TIMEOUT_MS = 5e3;
22798
- function cachedRepo(cwd) {
22799
- const entry = REPO_CACHE.get(cwd);
22800
- if (!entry) return { hit: false };
22801
- if (entry.expiresAtMs <= Date.now()) {
22802
- REPO_CACHE.delete(cwd);
22803
- return { hit: false };
22804
- }
22805
- REPO_CACHE.delete(cwd);
22806
- REPO_CACHE.set(cwd, entry);
22807
- return { hit: true, value: entry.value };
22808
- }
22809
- function git(cwd, args, timeoutMs) {
22810
- try {
22811
- return execFileSync2("git", ["-C", cwd, ...args], {
22812
- encoding: "utf8",
22813
- timeout: timeoutMs,
22814
- stdio: ["ignore", "pipe", "ignore"],
22815
- windowsHide: true
22816
- }).trim() || null;
22817
- } catch {
22818
- return null;
22819
- }
22820
- }
22821
- function repositorySlug(remote) {
22822
- const ssh = remote.match(/^[^@]+@[^:]+:([^/]+\/[^/]+?)(?:\.git)?$/);
22823
- if (ssh?.[1]) return ssh[1].toLowerCase();
22824
- try {
22825
- const url2 = new URL(remote);
22826
- const slug = url2.pathname.replace(/^\/+|\/+$/g, "").replace(/\.git$/, "");
22827
- return /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(slug) ? slug.toLowerCase() : null;
22828
- } catch {
22829
- return null;
22830
- }
22831
- }
22832
- function resolveRepositorySlugFromCwd(cwd, timeoutMs = GIT_TIMEOUT_MS) {
22833
- const cached2 = cachedRepo(cwd);
22834
- if (cached2.hit) return cached2.value?.repo ?? null;
22835
- const remote = git(cwd, ["remote", "get-url", "origin"], timeoutMs);
22836
- return remote ? repositorySlug(remote) : null;
23272
+ import path20 from "node:path";
23273
+ init_dirs();
23274
+ var CACHE_FILE = path20.join(FML_OBSERVER_DIR, GIT_STATE_CACHE_FILENAME);
23275
+ var resolver = createGitStateResolver({ cacheFile: CACHE_FILE });
23276
+ function resolveGitStateFromCwd(cwd, timeoutMs = 1e3) {
23277
+ return resolver.resolve(cwd, { timeoutMs });
22837
23278
  }
22838
- function resolveRepositoryRootFromCwd(cwd, timeoutMs = GIT_TIMEOUT_MS) {
22839
- return git(cwd, ["rev-parse", "--show-toplevel"], timeoutMs);
23279
+ function resolveRepositoryRootFromCwd(cwd, timeoutMs = 1e3) {
23280
+ return resolveGitStateFromCwd(cwd, timeoutMs)?.worktreeRoot ?? null;
22840
23281
  }
22841
23282
 
22842
23283
  // src/hooks/sync-loop.ts
@@ -22921,14 +23362,14 @@ function sameDestination(left, right) {
22921
23362
  return left.environment === right.environment && left.targetUrl === right.targetUrl && left.destinationIdentity === right.destinationIdentity;
22922
23363
  }
22923
23364
  function hookContextInjectionId(target, syncId) {
22924
- const digest = createHash5("sha256").update(`${INJECTION_ID_VERSION}\0${target}\0${syncId}`).digest("base64url");
23365
+ const digest = createHash6("sha256").update(`${INJECTION_ID_VERSION}\0${target}\0${syncId}`).digest("base64url");
22925
23366
  return `${INJECTION_ID_VERSION}_${digest}`;
22926
23367
  }
22927
23368
  function canonicalHookContextFilePath(filePath, cwd, repositoryRoot) {
22928
23369
  const windows = [filePath, cwd, repositoryRoot].some(
22929
23370
  (value) => value !== void 0 && value !== null && /^[A-Za-z]:[\\/]/.test(value)
22930
23371
  );
22931
- const pathApi = windows ? path19.win32 : path19.posix;
23372
+ const pathApi = windows ? path21.win32 : path21.posix;
22932
23373
  const normalized = pathApi.normalize(filePath);
22933
23374
  const resolved = pathApi.isAbsolute(normalized) ? normalized : cwd && pathApi.isAbsolute(cwd) ? pathApi.resolve(cwd, normalized) : normalized;
22934
23375
  if (repositoryRoot && pathApi.isAbsolute(resolved)) {
@@ -23088,16 +23529,16 @@ function wrapReceiptDetail(label, detail) {
23088
23529
  const firstPrefix = `\u2502 ${label}: `;
23089
23530
  const continuationPrefix = "\u2502 ";
23090
23531
  const lines = [];
23091
- let remaining = detail;
23532
+ let remaining2 = detail;
23092
23533
  let prefix = firstPrefix;
23093
- while (remaining) {
23534
+ while (remaining2) {
23094
23535
  const available = RECEIPT_LINE_WIDTH - receiptDisplayWidth(prefix);
23095
- if (receiptDisplayWidth(remaining) <= available) {
23096
- lines.push(`${prefix}${remaining}`);
23536
+ if (receiptDisplayWidth(remaining2) <= available) {
23537
+ lines.push(`${prefix}${remaining2}`);
23097
23538
  break;
23098
23539
  }
23099
- const tokenEnd = remaining.search(/\s/u);
23100
- const token = tokenEnd === -1 ? remaining : remaining.slice(0, tokenEnd);
23540
+ const tokenEnd = remaining2.search(/\s/u);
23541
+ const token = tokenEnd === -1 ? remaining2 : remaining2.slice(0, tokenEnd);
23101
23542
  const inlineCodeToken = splitInlineCodeToken(token);
23102
23543
  const inlineCodeWidth = inlineCodeToken ? receiptDisplayWidth(inlineCodeToken.inlineCode) + receiptDisplayWidth(inlineCodeToken.trailingPunctuation) : 0;
23103
23544
  if (inlineCodeToken && inlineCodeWidth > available) {
@@ -23109,7 +23550,7 @@ function wrapReceiptDetail(label, detail) {
23109
23550
  codeWidth
23110
23551
  )}${inlineCodeToken.trailingPunctuation}`
23111
23552
  );
23112
- remaining = tokenEnd === -1 ? "" : remaining.slice(tokenEnd).trimStart();
23553
+ remaining2 = tokenEnd === -1 ? "" : remaining2.slice(tokenEnd).trimStart();
23113
23554
  } else {
23114
23555
  lines.push(
23115
23556
  `${prefix}${truncateInlineCodeToken(
@@ -23117,13 +23558,13 @@ function wrapReceiptDetail(label, detail) {
23117
23558
  available
23118
23559
  )}`
23119
23560
  );
23120
- remaining = `${inlineCodeToken.trailingPunctuation}${tokenEnd === -1 ? "" : ` ${remaining.slice(tokenEnd).trimStart()}`}`;
23561
+ remaining2 = `${inlineCodeToken.trailingPunctuation}${tokenEnd === -1 ? "" : ` ${remaining2.slice(tokenEnd).trimStart()}`}`;
23121
23562
  }
23122
23563
  } else {
23123
- const { head, tail } = splitAtDisplayWidth(remaining, available);
23564
+ const { head, tail } = splitAtDisplayWidth(remaining2, available);
23124
23565
  if (!head) return [];
23125
23566
  lines.push(`${prefix}${head}`);
23126
- remaining = tail;
23567
+ remaining2 = tail;
23127
23568
  }
23128
23569
  prefix = continuationPrefix;
23129
23570
  }
@@ -23257,17 +23698,17 @@ function absoluteEnvPath(value) {
23257
23698
  const trimmed = value.trim();
23258
23699
  if (trimmed === "~") return os2.homedir();
23259
23700
  if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
23260
- return path20.join(os2.homedir(), trimmed.slice(2));
23701
+ return path22.join(os2.homedir(), trimmed.slice(2));
23261
23702
  }
23262
- return path20.isAbsolute(trimmed) ? path20.normalize(trimmed) : void 0;
23703
+ return path22.isAbsolute(trimmed) ? path22.normalize(trimmed) : void 0;
23263
23704
  }
23264
23705
  function observerDirectory() {
23265
23706
  const fmlHome = absoluteEnvPath(process.env.FML_HOME);
23266
- return absoluteEnvPath(process.env.FML_OBSERVER_DIR) ?? absoluteEnvPath(process.env.PANOPTICON_HOME) ?? (fmlHome ? path20.join(fmlHome, "panopticon") : path20.join(os2.homedir(), ".fml", "panopticon"));
23707
+ return absoluteEnvPath(process.env.FML_OBSERVER_DIR) ?? absoluteEnvPath(process.env.PANOPTICON_HOME) ?? (fmlHome ? path22.join(fmlHome, "panopticon") : path22.join(os2.homedir(), ".fml", "panopticon"));
23267
23708
  }
23268
23709
  function readObserverEndpoint(filePath) {
23269
23710
  try {
23270
- const parsed = JSON.parse(fs9.readFileSync(filePath, "utf8"));
23711
+ const parsed = JSON.parse(fs10.readFileSync(filePath, "utf8"));
23271
23712
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
23272
23713
  return null;
23273
23714
  }
@@ -23282,15 +23723,15 @@ function readObserverEndpoint(filePath) {
23282
23723
  }
23283
23724
  function readObserverAuthToken(filePath) {
23284
23725
  try {
23285
- return fs9.readFileSync(filePath, "utf8").trim() || null;
23726
+ return fs10.readFileSync(filePath, "utf8").trim() || null;
23286
23727
  } catch {
23287
23728
  return null;
23288
23729
  }
23289
23730
  }
23290
23731
  async function deliver(event, repository) {
23291
23732
  const directory = observerDirectory();
23292
- const endpoint = readObserverEndpoint(path20.join(directory, "endpoint.json"));
23293
- const authToken = readObserverAuthToken(path20.join(directory, "auth-token"));
23733
+ const endpoint = readObserverEndpoint(path22.join(directory, "endpoint.json"));
23734
+ const authToken = readObserverAuthToken(path22.join(directory, "auth-token"));
23294
23735
  if (!endpoint || !authToken) return;
23295
23736
  const host = endpoint.host === "::1" ? "[::1]" : endpoint.host;
23296
23737
  try {
@@ -23302,7 +23743,10 @@ async function deliver(event, repository) {
23302
23743
  },
23303
23744
  body: JSON.stringify({
23304
23745
  target: "pi",
23305
- payload: event,
23746
+ payload: {
23747
+ ...event,
23748
+ [GIT_STATE_PAYLOAD_KEY]: event.cwd ? resolveGitStateFromCwd(event.cwd, 250) : null
23749
+ },
23306
23750
  ...event.cwd ? { cwd: event.cwd } : {},
23307
23751
  ...repository ? { repository } : {}
23308
23752
  }),
@@ -23360,16 +23804,16 @@ var RECEIPT_GRAPHEME_SEGMENTER = new Intl.Segmenter(void 0, {
23360
23804
  });
23361
23805
  function wrapReceiptLine(text, width) {
23362
23806
  if (width < 1) return [""];
23363
- let remaining = text.trim();
23364
- if (!remaining) return [""];
23807
+ let remaining2 = text.trim();
23808
+ if (!remaining2) return [""];
23365
23809
  const lines = [];
23366
- while (remaining) {
23810
+ while (remaining2) {
23367
23811
  let line = "";
23368
23812
  let lineWidth = 0;
23369
23813
  let end = 0;
23370
23814
  let whitespaceEnd = 0;
23371
23815
  for (const { segment, index } of RECEIPT_GRAPHEME_SEGMENTER.segment(
23372
- remaining
23816
+ remaining2
23373
23817
  )) {
23374
23818
  const segmentWidth = receiptDisplayWidth(segment);
23375
23819
  if (segmentWidth > width) {
@@ -23384,16 +23828,16 @@ function wrapReceiptLine(text, width) {
23384
23828
  end = index + segment.length;
23385
23829
  if (/^\s$/u.test(segment)) whitespaceEnd = end;
23386
23830
  }
23387
- if (end === remaining.length) {
23831
+ if (end === remaining2.length) {
23388
23832
  lines.push(line.trimEnd());
23389
23833
  break;
23390
23834
  }
23391
23835
  if (whitespaceEnd > 0) {
23392
- lines.push(remaining.slice(0, whitespaceEnd).trimEnd());
23393
- remaining = remaining.slice(whitespaceEnd).trimStart();
23836
+ lines.push(remaining2.slice(0, whitespaceEnd).trimEnd());
23837
+ remaining2 = remaining2.slice(whitespaceEnd).trimStart();
23394
23838
  } else {
23395
23839
  lines.push(line);
23396
- remaining = remaining.slice(end);
23840
+ remaining2 = remaining2.slice(end);
23397
23841
  }
23398
23842
  }
23399
23843
  return lines;
@@ -23433,7 +23877,7 @@ var PiReceiptComponent = class {
23433
23877
  }
23434
23878
  };
23435
23879
  function fmlPiExtension(pi) {
23436
- let sessionId = randomUUID4();
23880
+ let sessionId = randomUUID5();
23437
23881
  let initializedSessionId = null;
23438
23882
  let cwd;
23439
23883
  let repository = null;
@@ -23558,7 +24002,7 @@ function fmlPiExtension(pi) {
23558
24002
  }
23559
24003
  repository = null;
23560
24004
  try {
23561
- repository = cwd ? resolveRepositorySlugFromCwd(cwd, 250) : null;
24005
+ repository = cwd ? resolveGitStateFromCwd(cwd, 250)?.repository ?? null : null;
23562
24006
  } catch {
23563
24007
  }
23564
24008
  if (newIdentity && evidence?.status === "absent" && (event.reason === "startup" || event.reason === "new" || event.reason === "resume") && entries.every(
@@ -23567,7 +24011,8 @@ function fmlPiExtension(pi) {
23567
24011
  const origin = PiRepositoryOriginDataSchema.safeParse({
23568
24012
  version: 1,
23569
24013
  repository,
23570
- cwd
24014
+ cwd,
24015
+ gitState: cwd ? resolveGitStateFromCwd(cwd, 250) : null
23571
24016
  });
23572
24017
  if (origin.success) {
23573
24018
  capturedRepository = origin.data.repository;