@ai-matrx/coding-sessions 0.1.81 → 0.1.83

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.
@@ -44,17 +44,17 @@ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
44
44
 
45
45
  // src/server/index.ts
46
46
  var import_node_crypto5 = require("crypto");
47
- var import_node_fs4 = require("fs");
47
+ var import_node_fs5 = require("fs");
48
48
  var import_promises7 = __toESM(require("fs/promises"), 1);
49
49
  var import_node_http = __toESM(require("http"), 1);
50
- var import_node_path7 = __toESM(require("path"), 1);
50
+ var import_node_path8 = __toESM(require("path"), 1);
51
51
  var import_node_url2 = require("url");
52
52
  var import_ws = require("ws");
53
53
 
54
54
  // src/engine/index.ts
55
- var import_node_child_process2 = require("child_process");
55
+ var import_node_child_process3 = require("child_process");
56
56
  var import_node_crypto4 = require("crypto");
57
- var import_node_fs3 = require("fs");
57
+ var import_node_fs4 = require("fs");
58
58
  var import_format = require("@ai-matrx/kit/format");
59
59
 
60
60
  // src/engine/raw-backup.ts
@@ -116,8 +116,8 @@ function createRawBackupScheduler(options) {
116
116
 
117
117
  // src/engine/index.ts
118
118
  var import_promises6 = __toESM(require("fs/promises"), 1);
119
- var import_node_os4 = __toESM(require("os"), 1);
120
- var import_node_path6 = __toESM(require("path"), 1);
119
+ var import_node_os5 = __toESM(require("os"), 1);
120
+ var import_node_path7 = __toESM(require("path"), 1);
121
121
  var import_node_url = require("url");
122
122
 
123
123
  // src/normalize/claude.ts
@@ -783,9 +783,177 @@ function describeProblem(provider, problem) {
783
783
  };
784
784
  }
785
785
 
786
- // src/engine/clean-env.ts
786
+ // src/engine/binaries.ts
787
+ var import_node_child_process = require("child_process");
788
+ var import_node_fs = require("fs");
787
789
  var import_node_os = __toESM(require("os"), 1);
788
790
  var import_node_path = __toESM(require("path"), 1);
791
+ var SOURCE_LABEL = {
792
+ codex: { "desktop-app": "from ChatGPT app", path: "from PATH", "npm-global": "from npm", configured: "set by host" },
793
+ claude: { "desktop-app": "from Claude app", path: "from PATH", "npm-global": "from npm", configured: "set by host" }
794
+ };
795
+ function sourceLabel(provider, source) {
796
+ return SOURCE_LABEL[provider][source];
797
+ }
798
+ function parseVersion(text) {
799
+ const match = /(\d+)\.(\d+)\.(\d+)(?:[-.][0-9A-Za-z.]+)?/.exec(text ?? "");
800
+ return match ? match[0] : null;
801
+ }
802
+ function compareVersions(a, b) {
803
+ const [aCore = "", aPre] = a.split("-", 2);
804
+ const [bCore = "", bPre] = b.split("-", 2);
805
+ const pa = aCore.split(".").map((n) => Number.parseInt(n, 10) || 0);
806
+ const pb = bCore.split(".").map((n) => Number.parseInt(n, 10) || 0);
807
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
808
+ const d = (pa[i] ?? 0) - (pb[i] ?? 0);
809
+ if (d !== 0) return d < 0 ? -1 : 1;
810
+ }
811
+ if (aPre && !bPre) return -1;
812
+ if (!aPre && bPre) return 1;
813
+ return 0;
814
+ }
815
+ function pickBinary(candidates) {
816
+ let best = null;
817
+ for (const c of candidates) {
818
+ if (!best) {
819
+ best = c;
820
+ continue;
821
+ }
822
+ if (c.version && (!best.version || compareVersions(c.version, best.version) > 0)) best = c;
823
+ }
824
+ return best;
825
+ }
826
+ function executable(file) {
827
+ try {
828
+ (0, import_node_fs.accessSync)(file, import_node_fs.constants.X_OK);
829
+ return true;
830
+ } catch {
831
+ return false;
832
+ }
833
+ }
834
+ function exeName(name, platform) {
835
+ return platform === "win32" ? [`${name}.exe`, `${name}.cmd`, name] : [name];
836
+ }
837
+ function allOnPath(name, pathValue, platform = process.platform) {
838
+ const found = [];
839
+ for (const dir of pathValue.split(import_node_path.default.delimiter)) {
840
+ if (!dir) continue;
841
+ for (const file of exeName(name, platform)) {
842
+ const candidate = import_node_path.default.join(dir, file);
843
+ if (executable(candidate)) {
844
+ found.push(candidate);
845
+ break;
846
+ }
847
+ }
848
+ }
849
+ return found;
850
+ }
851
+ function desktopAppCandidates(provider, platform, home, applications) {
852
+ if (platform !== "darwin") return [];
853
+ if (provider === "codex") {
854
+ return applications.map(
855
+ (apps) => import_node_path.default.join(apps, "ChatGPT.app", "Contents", "Resources", "codex-cli", "bin", "codex")
856
+ );
857
+ }
858
+ const root = import_node_path.default.join(home, "Library", "Application Support", "Claude", "claude-code");
859
+ let versions = [];
860
+ try {
861
+ versions = (0, import_node_fs.readdirSync)(root);
862
+ } catch {
863
+ return [];
864
+ }
865
+ return versions.map((v) => import_node_path.default.join(root, v, "claude.app", "Contents", "MacOS", "claude"));
866
+ }
867
+ function npmGlobalPrefix(env) {
868
+ const fromEnv = env.npm_config_prefix ?? env.NPM_CONFIG_PREFIX;
869
+ if (fromEnv) return Promise.resolve(fromEnv);
870
+ const npm = allOnPath("npm", env.PATH ?? "")[0];
871
+ if (!npm) return Promise.resolve(null);
872
+ return new Promise((resolve) => {
873
+ const child = (0, import_node_child_process.execFile)(npm, ["prefix", "-g"], { timeout: 5e3, env }, (error, stdout) => {
874
+ resolve(error ? null : String(stdout).trim() || null);
875
+ });
876
+ child.stdin?.end();
877
+ });
878
+ }
879
+ function declaredVersion(provider, source, file) {
880
+ if (source !== "desktop-app") return null;
881
+ if (provider === "codex") {
882
+ try {
883
+ const pkg = JSON.parse((0, import_node_fs.readFileSync)(import_node_path.default.join(import_node_path.default.dirname(import_node_path.default.dirname(file)), "codex-package.json"), "utf8"));
884
+ return typeof pkg.version === "string" ? parseVersion(pkg.version) : null;
885
+ } catch {
886
+ return null;
887
+ }
888
+ }
889
+ return parseVersion(import_node_path.default.basename(import_node_path.default.resolve(file, "..", "..", "..", "..")));
890
+ }
891
+ function runVersion(file, env) {
892
+ return new Promise((resolve) => {
893
+ const child = (0, import_node_child_process.execFile)(file, ["--version"], { timeout: 8e3, env }, (error, stdout) => {
894
+ resolve(error ? null : parseVersion(String(stdout)));
895
+ });
896
+ child.stdin?.end();
897
+ });
898
+ }
899
+ async function discoverBinaries(provider, options) {
900
+ const platform = options.platform ?? process.platform;
901
+ const home = options.home ?? options.env.HOME ?? import_node_os.default.homedir();
902
+ const listed2 = [
903
+ ...desktopAppCandidates(provider, platform, home, options.applications ?? ["/Applications", import_node_path.default.join(home, "Applications")]).filter(executable).map((p) => ({ path: p, source: "desktop-app" })),
904
+ ...allOnPath(provider, options.env.PATH ?? "", platform).map((p) => ({ path: p, source: "path" }))
905
+ ];
906
+ const prefix = await npmGlobalPrefix(options.env);
907
+ if (prefix) {
908
+ const dir = platform === "win32" ? prefix : import_node_path.default.join(prefix, "bin");
909
+ for (const file of exeName(provider, platform)) {
910
+ const candidate = import_node_path.default.join(dir, file);
911
+ if (executable(candidate)) {
912
+ listed2.push({ path: candidate, source: "npm-global" });
913
+ break;
914
+ }
915
+ }
916
+ }
917
+ const seen = /* @__PURE__ */ new Set();
918
+ const unique = listed2.filter((c) => {
919
+ let real = c.path;
920
+ try {
921
+ real = (0, import_node_fs.realpathSync)(c.path);
922
+ } catch {
923
+ }
924
+ if (seen.has(real)) return false;
925
+ seen.add(real);
926
+ return true;
927
+ });
928
+ return Promise.all(
929
+ unique.map(async (c) => ({
930
+ ...c,
931
+ version: declaredVersion(provider, c.source, c.path) ?? await runVersion(c.path, options.env)
932
+ }))
933
+ );
934
+ }
935
+ async function resolveBinary(provider, options) {
936
+ const candidates = options.discover === false ? [] : await discoverBinaries(provider, options);
937
+ const best = pickBinary(candidates);
938
+ const inUse = options.forced ? {
939
+ path: options.forced,
940
+ source: "configured",
941
+ version: candidates.find((c) => c.path === options.forced)?.version ?? (options.discover === false ? null : await runVersion(options.forced, options.env))
942
+ } : best;
943
+ if (!inUse) return null;
944
+ const newer = best && best.path !== inUse.path && best.version && (!inUse.version || compareVersions(best.version, inUse.version) > 0) ? { path: best.path, version: best.version, sourceLabel: sourceLabel(provider, best.source) } : null;
945
+ return {
946
+ path: inUse.path,
947
+ version: inUse.version,
948
+ source: inUse.source,
949
+ sourceLabel: sourceLabel(provider, inUse.source),
950
+ newer
951
+ };
952
+ }
953
+
954
+ // src/engine/clean-env.ts
955
+ var import_node_os2 = __toESM(require("os"), 1);
956
+ var import_node_path2 = __toESM(require("path"), 1);
789
957
  var ALLOWED_NAMES = /* @__PURE__ */ new Set([
790
958
  "HOME",
791
959
  "USER",
@@ -807,11 +975,11 @@ var ALLOWED_NAMES = /* @__PURE__ */ new Set([
807
975
  var ALLOWED_PREFIXES = ["LC_"];
808
976
  function wellKnownBinDirs(home) {
809
977
  return [
810
- import_node_path.default.join(home, ".local", "bin"),
811
- import_node_path.default.join(home, ".claude", "local"),
812
- import_node_path.default.join(home, ".npm-global", "bin"),
813
- import_node_path.default.join(home, ".bun", "bin"),
814
- import_node_path.default.join(home, ".volta", "bin"),
978
+ import_node_path2.default.join(home, ".local", "bin"),
979
+ import_node_path2.default.join(home, ".claude", "local"),
980
+ import_node_path2.default.join(home, ".npm-global", "bin"),
981
+ import_node_path2.default.join(home, ".bun", "bin"),
982
+ import_node_path2.default.join(home, ".volta", "bin"),
815
983
  "/opt/homebrew/bin",
816
984
  "/usr/local/bin",
817
985
  "/usr/bin",
@@ -829,31 +997,31 @@ function buildCleanEnv(hostEnv, extra = {}) {
829
997
  if (typeof value !== "string") continue;
830
998
  if (isAllowedEnvName(name)) out[name] = value;
831
999
  }
832
- const home = out.HOME ?? import_node_os.default.homedir();
1000
+ const home = out.HOME ?? import_node_os2.default.homedir();
833
1001
  out.HOME = home;
834
- const hostPath = (hostEnv.PATH ?? "").split(import_node_path.default.delimiter).filter(Boolean);
1002
+ const hostPath = (hostEnv.PATH ?? "").split(import_node_path2.default.delimiter).filter(Boolean);
835
1003
  const merged = [];
836
1004
  for (const dir of [...hostPath, ...wellKnownBinDirs(home)]) {
837
1005
  if (!merged.includes(dir)) merged.push(dir);
838
1006
  }
839
- out.PATH = merged.join(import_node_path.default.delimiter);
1007
+ out.PATH = merged.join(import_node_path2.default.delimiter);
840
1008
  for (const [name, value] of Object.entries(extra)) out[name] = value;
841
1009
  return out;
842
1010
  }
843
1011
 
844
1012
  // src/engine/files.ts
845
- var import_node_fs = require("fs");
1013
+ var import_node_fs2 = require("fs");
846
1014
  var import_promises2 = __toESM(require("fs/promises"), 1);
847
- var import_node_os2 = __toESM(require("os"), 1);
848
- var import_node_path2 = __toESM(require("path"), 1);
1015
+ var import_node_os3 = __toESM(require("os"), 1);
1016
+ var import_node_path3 = __toESM(require("path"), 1);
849
1017
 
850
1018
  // src/engine/activity.ts
851
- var import_node_child_process = require("child_process");
1019
+ var import_node_child_process2 = require("child_process");
852
1020
  var RECENT_WRITE_MS = 2 * 60 * 1e3;
853
1021
  var OPEN_TOOL_WINDOW_MS = 15 * 60 * 1e3;
854
1022
  function listProcesses() {
855
1023
  return new Promise((resolve) => {
856
- (0, import_node_child_process.execFile)("ps", ["-axww", "-o", "pid=,args="], { maxBuffer: 64 * 1024 * 1024, timeout: 5e3 }, (error, stdout) => {
1024
+ (0, import_node_child_process2.execFile)("ps", ["-axww", "-o", "pid=,args="], { maxBuffer: 64 * 1024 * 1024, timeout: 5e3 }, (error, stdout) => {
857
1025
  if (error) {
858
1026
  resolve([]);
859
1027
  return;
@@ -912,11 +1080,11 @@ function describeActivity(input) {
912
1080
  }
913
1081
 
914
1082
  // src/engine/files.ts
915
- function defaultProviderHomes(home = import_node_os2.default.homedir()) {
1083
+ function defaultProviderHomes(home = import_node_os3.default.homedir()) {
916
1084
  return {
917
- claudeProjects: import_node_path2.default.join(home, ".claude", "projects"),
918
- codexSessions: import_node_path2.default.join(home, ".codex", "sessions"),
919
- codexIndex: import_node_path2.default.join(home, ".codex", "session_index.jsonl")
1085
+ claudeProjects: import_node_path3.default.join(home, ".claude", "projects"),
1086
+ codexSessions: import_node_path3.default.join(home, ".codex", "sessions"),
1087
+ codexIndex: import_node_path3.default.join(home, ".codex", "session_index.jsonl")
920
1088
  };
921
1089
  }
922
1090
  var UUIDISH = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;
@@ -941,10 +1109,10 @@ async function claudeCandidates(root) {
941
1109
  const out = [];
942
1110
  for (const project of await safeReaddir(root)) {
943
1111
  if (!project.isDirectory()) continue;
944
- const dir = import_node_path2.default.join(root, project.name);
1112
+ const dir = import_node_path3.default.join(root, project.name);
945
1113
  const entries = await safeReaddir(dir);
946
1114
  const stats = await Promise.all(
947
- entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => statCandidate("claude", import_node_path2.default.join(dir, e.name)))
1115
+ entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => statCandidate("claude", import_node_path3.default.join(dir, e.name)))
948
1116
  );
949
1117
  for (const s of stats) if (s) out.push(s);
950
1118
  }
@@ -954,14 +1122,14 @@ async function codexCandidates(root) {
954
1122
  const out = [];
955
1123
  for (const year of await safeReaddir(root)) {
956
1124
  if (!year.isDirectory()) continue;
957
- for (const month of await safeReaddir(import_node_path2.default.join(root, year.name))) {
1125
+ for (const month of await safeReaddir(import_node_path3.default.join(root, year.name))) {
958
1126
  if (!month.isDirectory()) continue;
959
- for (const day of await safeReaddir(import_node_path2.default.join(root, year.name, month.name))) {
1127
+ for (const day of await safeReaddir(import_node_path3.default.join(root, year.name, month.name))) {
960
1128
  if (!day.isDirectory()) continue;
961
- const dir = import_node_path2.default.join(root, year.name, month.name, day.name);
1129
+ const dir = import_node_path3.default.join(root, year.name, month.name, day.name);
962
1130
  const entries = await safeReaddir(dir);
963
1131
  const stats = await Promise.all(
964
- entries.filter((e) => e.isFile() && e.name.startsWith("rollout-") && e.name.endsWith(".jsonl")).map((e) => statCandidate("codex", import_node_path2.default.join(dir, e.name)))
1132
+ entries.filter((e) => e.isFile() && e.name.startsWith("rollout-") && e.name.endsWith(".jsonl")).map((e) => statCandidate("codex", import_node_path3.default.join(dir, e.name)))
965
1133
  );
966
1134
  for (const s of stats) if (s) out.push(s);
967
1135
  }
@@ -1182,7 +1350,7 @@ async function readSessionFile(provider, file, endOffset, maxBytes = HISTORY_BUD
1182
1350
  let droppedFirst = start > 0;
1183
1351
  let carry = "";
1184
1352
  await new Promise((resolve, reject) => {
1185
- const stream = (0, import_node_fs.createReadStream)(file, { start, end: Math.max(start, end - 1), encoding: "utf8" });
1353
+ const stream = (0, import_node_fs2.createReadStream)(file, { start, end: Math.max(start, end - 1), encoding: "utf8" });
1186
1354
  stream.on("data", (chunk) => {
1187
1355
  const text = carry + String(chunk);
1188
1356
  const lines = text.split("\n");
@@ -1222,7 +1390,7 @@ async function describeSessionFile(candidate, codexNames) {
1222
1390
  // src/engine/preferences.ts
1223
1391
  var import_node_crypto2 = require("crypto");
1224
1392
  var import_promises3 = __toESM(require("fs/promises"), 1);
1225
- var import_node_path3 = __toESM(require("path"), 1);
1393
+ var import_node_path4 = __toESM(require("path"), 1);
1226
1394
  var PreferenceStore = class {
1227
1395
  constructor(dir) {
1228
1396
  this.dir = dir;
@@ -1231,7 +1399,7 @@ var PreferenceStore = class {
1231
1399
  data = {};
1232
1400
  writing = Promise.resolve();
1233
1401
  get file() {
1234
- return import_node_path3.default.join(this.dir, "preferences.json");
1402
+ return import_node_path4.default.join(this.dir, "preferences.json");
1235
1403
  }
1236
1404
  async load() {
1237
1405
  try {
@@ -1286,7 +1454,7 @@ var PreferenceStore = class {
1286
1454
  };
1287
1455
 
1288
1456
  // src/engine/search.ts
1289
- var import_node_fs2 = require("fs");
1457
+ var import_node_fs3 = require("fs");
1290
1458
  function findInLine(provider, value, needle) {
1291
1459
  const items = provider === "claude" ? createClaudeNormalizer().push(value) : createCodexRolloutNormalizer().push(value);
1292
1460
  for (const item of items) {
@@ -1327,7 +1495,7 @@ async function scanFile(provider, file, needle, raw, deadline) {
1327
1495
  return new Promise((resolve) => {
1328
1496
  let carry = Buffer.alloc(0);
1329
1497
  let done = false;
1330
- const stream = (0, import_node_fs2.createReadStream)(file, { highWaterMark: 4 * 1024 * 1024 });
1498
+ const stream = (0, import_node_fs3.createReadStream)(file, { highWaterMark: 4 * 1024 * 1024 });
1331
1499
  const finish = (value) => {
1332
1500
  if (done) return;
1333
1501
  done = true;
@@ -1435,7 +1603,7 @@ async function searchLocalChats(query, options = {}) {
1435
1603
  // src/engine/store.ts
1436
1604
  var import_node_crypto3 = require("crypto");
1437
1605
  var import_promises4 = __toESM(require("fs/promises"), 1);
1438
- var import_node_path4 = __toESM(require("path"), 1);
1606
+ var import_node_path5 = __toESM(require("path"), 1);
1439
1607
  var MAX_RUNS = 2e3;
1440
1608
  var RunIndex = class {
1441
1609
  constructor(dir) {
@@ -1445,7 +1613,7 @@ var RunIndex = class {
1445
1613
  data = { version: 1, sessions: [], runs: [] };
1446
1614
  writing = Promise.resolve();
1447
1615
  get file() {
1448
- return import_node_path4.default.join(this.dir, "index.json");
1616
+ return import_node_path5.default.join(this.dir, "index.json");
1449
1617
  }
1450
1618
  async load() {
1451
1619
  await import_promises4.default.mkdir(this.dir, { recursive: true });
@@ -1504,7 +1672,7 @@ var RunIndex = class {
1504
1672
  /** A stable Paperclip agent/company identity for this install, so the
1505
1673
  * driver's scratch state is reused rather than growing per run. */
1506
1674
  async identity() {
1507
- const file = import_node_path4.default.join(this.dir, "identity.json");
1675
+ const file = import_node_path5.default.join(this.dir, "identity.json");
1508
1676
  try {
1509
1677
  const parsed = JSON.parse(await import_promises4.default.readFile(file, "utf8"));
1510
1678
  if (typeof parsed.companyId === "string" && typeof parsed.agentId === "string") {
@@ -1520,8 +1688,8 @@ var RunIndex = class {
1520
1688
 
1521
1689
  // src/engine/user-defaults.ts
1522
1690
  var import_promises5 = __toESM(require("fs/promises"), 1);
1523
- var import_node_os3 = __toESM(require("os"), 1);
1524
- var import_node_path5 = __toESM(require("path"), 1);
1691
+ var import_node_os4 = __toESM(require("os"), 1);
1692
+ var import_node_path6 = __toESM(require("path"), 1);
1525
1693
  async function readJson(file) {
1526
1694
  try {
1527
1695
  const parsed = JSON.parse(await import_promises5.default.readFile(file, "utf8"));
@@ -1533,10 +1701,10 @@ async function readJson(file) {
1533
1701
  function str3(value) {
1534
1702
  return typeof value === "string" && value.trim() ? value.trim() : null;
1535
1703
  }
1536
- async function claudeDefaults(cwd, home = import_node_os3.default.homedir()) {
1704
+ async function claudeDefaults(cwd, home = import_node_os4.default.homedir()) {
1537
1705
  const files = [
1538
- ...cwd ? [import_node_path5.default.join(cwd, ".claude", "settings.local.json"), import_node_path5.default.join(cwd, ".claude", "settings.json")] : [],
1539
- import_node_path5.default.join(home, ".claude", "settings.json")
1706
+ ...cwd ? [import_node_path6.default.join(cwd, ".claude", "settings.local.json"), import_node_path6.default.join(cwd, ".claude", "settings.json")] : [],
1707
+ import_node_path6.default.join(home, ".claude", "settings.json")
1540
1708
  ];
1541
1709
  let model = null;
1542
1710
  let effort = null;
@@ -1549,10 +1717,10 @@ async function claudeDefaults(cwd, home = import_node_os3.default.homedir()) {
1549
1717
  }
1550
1718
  return { model, effort };
1551
1719
  }
1552
- async function codexDefaults(home = import_node_os3.default.homedir()) {
1720
+ async function codexDefaults(home = import_node_os4.default.homedir()) {
1553
1721
  let text;
1554
1722
  try {
1555
- text = await import_promises5.default.readFile(import_node_path5.default.join(home, ".codex", "config.toml"), "utf8");
1723
+ text = await import_promises5.default.readFile(import_node_path6.default.join(home, ".codex", "config.toml"), "utf8");
1556
1724
  } catch {
1557
1725
  return { model: null, effort: null };
1558
1726
  }
@@ -1576,45 +1744,33 @@ var EFFORT_LEVELS = {
1576
1744
  // src/engine/index.ts
1577
1745
  var RAW_TRANSCRIPT_MAX_BYTES = 200 * 1048576;
1578
1746
  function moduleDir() {
1579
- return import_node_path6.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
1747
+ return import_node_path7.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
1580
1748
  }
1581
1749
  function resolveWorkerPath() {
1582
1750
  const dir = moduleDir();
1583
1751
  for (const candidate of [
1584
- import_node_path6.default.join(dir, "worker.js"),
1585
- import_node_path6.default.join(dir, "worker.ts"),
1586
- import_node_path6.default.join(dir, "..", "engine", "worker.js"),
1587
- import_node_path6.default.join(dir, "..", "engine", "worker.ts")
1752
+ import_node_path7.default.join(dir, "worker.js"),
1753
+ import_node_path7.default.join(dir, "worker.ts"),
1754
+ import_node_path7.default.join(dir, "..", "engine", "worker.js"),
1755
+ import_node_path7.default.join(dir, "..", "engine", "worker.ts")
1588
1756
  ]) {
1589
- if ((0, import_node_fs3.existsSync)(candidate)) return candidate;
1757
+ if ((0, import_node_fs4.existsSync)(candidate)) return candidate;
1590
1758
  }
1591
1759
  throw new Error(`[coding-sessions] The driver worker is missing next to ${dir}.`);
1592
1760
  }
1593
1761
  async function installShim(dataDir) {
1594
- const source = import_node_path6.default.resolve(moduleDir(), "..", "..", "bin", "cli-shim.sh");
1595
- if (!(0, import_node_fs3.existsSync)(source)) throw new Error(`[coding-sessions] The CLI shim is missing at ${source}.`);
1762
+ const source = import_node_path7.default.resolve(moduleDir(), "..", "..", "bin", "cli-shim.sh");
1763
+ if (!(0, import_node_fs4.existsSync)(source)) throw new Error(`[coding-sessions] The CLI shim is missing at ${source}.`);
1596
1764
  const body = await import_promises6.default.readFile(source, "utf8");
1597
- const target = import_node_path6.default.join(dataDir, "bin", "cli-shim.sh");
1765
+ const target = import_node_path7.default.join(dataDir, "bin", "cli-shim.sh");
1598
1766
  const current = await import_promises6.default.readFile(target, "utf8").catch(() => null);
1599
1767
  if (current !== body) {
1600
- await import_promises6.default.mkdir(import_node_path6.default.dirname(target), { recursive: true });
1768
+ await import_promises6.default.mkdir(import_node_path7.default.dirname(target), { recursive: true });
1601
1769
  await import_promises6.default.writeFile(target, body, { mode: 493 });
1602
1770
  }
1603
1771
  await import_promises6.default.chmod(target, 493);
1604
1772
  return target;
1605
1773
  }
1606
- function whichOnPath(name, pathValue) {
1607
- for (const dir of pathValue.split(import_node_path6.default.delimiter)) {
1608
- if (!dir) continue;
1609
- const candidate = import_node_path6.default.join(dir, name);
1610
- try {
1611
- (0, import_node_fs3.accessSync)(candidate, import_node_fs3.constants.X_OK);
1612
- return candidate;
1613
- } catch {
1614
- }
1615
- }
1616
- return null;
1617
- }
1618
1774
  function nowIso() {
1619
1775
  return (/* @__PURE__ */ new Date()).toISOString();
1620
1776
  }
@@ -1653,12 +1809,14 @@ function describeProviderStatus(provider, probe, lastProblem = null, quota = [])
1653
1809
  function describeAuthStatus(provider, probe) {
1654
1810
  const label = PROVIDER_LABEL[provider];
1655
1811
  const installed = probe?.installed === true;
1656
- const version = typeof probe?.version === "string" ? probe.version : null;
1812
+ const binary = probe?.binary ?? null;
1813
+ const version = binary?.version ?? (typeof probe?.version === "string" ? probe.version : null);
1657
1814
  const base = {
1658
1815
  provider,
1659
1816
  label,
1660
1817
  installed,
1661
1818
  version,
1819
+ binary,
1662
1820
  loggedIn: false,
1663
1821
  billing: "unknown",
1664
1822
  plan: null,
@@ -1710,8 +1868,8 @@ function describeAuthStatus(provider, probe) {
1710
1868
  };
1711
1869
  }
1712
1870
  async function createCodingSessionsEngine(options = {}) {
1713
- const dataDir = options.dataDir ?? import_node_path6.default.join(import_node_os4.default.homedir(), ".matrx", "coding-sessions");
1714
- const stateDir = import_node_path6.default.join(dataDir, "paperclip-state");
1871
+ const dataDir = options.dataDir ?? import_node_path7.default.join(import_node_os5.default.homedir(), ".matrx", "coding-sessions");
1872
+ const stateDir = import_node_path7.default.join(dataDir, "paperclip-state");
1715
1873
  const homes = options.homes ?? defaultProviderHomes();
1716
1874
  const index = new RunIndex(dataDir);
1717
1875
  await index.load();
@@ -1725,7 +1883,7 @@ async function createCodingSessionsEngine(options = {}) {
1725
1883
  onError: (error) => console.warn("[coding-sessions] raw transcript backup failed:", error)
1726
1884
  }) : null;
1727
1885
  const discoveredSeen = /* @__PURE__ */ new Map();
1728
- const healthFile = import_node_path6.default.join(dataDir, "provider-health.json");
1886
+ const healthFile = import_node_path7.default.join(dataDir, "provider-health.json");
1729
1887
  let health = {};
1730
1888
  try {
1731
1889
  health = JSON.parse(await import_promises6.default.readFile(healthFile, "utf8"));
@@ -1760,7 +1918,24 @@ async function createCodingSessionsEngine(options = {}) {
1760
1918
  // Marks the forked process as a driver worker (see worker.ts).
1761
1919
  MATRX_CODING_SESSIONS_WORKER: "1"
1762
1920
  });
1763
- const commandFor = (provider) => options.commands?.[provider] ?? whichOnPath(provider, workerEnv.PATH ?? "");
1921
+ const binaryHome = options.hostEnv?.HOME ?? import_node_os5.default.homedir();
1922
+ const binaryCache = /* @__PURE__ */ new Map();
1923
+ function binaryFor(provider, fresh = false) {
1924
+ const cached = binaryCache.get(provider);
1925
+ if (cached && !fresh && Date.now() - cached.at < 5 * 6e4) return cached.value;
1926
+ const forced = options.commands?.[provider];
1927
+ const value = resolveBinary(provider, {
1928
+ env: workerEnv,
1929
+ home: binaryHome,
1930
+ forced,
1931
+ // A forced command needs no discovery to run; the status check still
1932
+ // looks, so it can say when a newer copy exists.
1933
+ discover: !forced || fresh
1934
+ }).catch(() => forced ? { path: forced, version: null, source: "configured", sourceLabel: "set by host", newer: null } : null);
1935
+ if (!forced || !fresh) binaryCache.set(provider, { at: Date.now(), value });
1936
+ return value;
1937
+ }
1938
+ const commandFor = async (provider) => (await binaryFor(provider))?.path ?? null;
1764
1939
  const workerPath = resolveWorkerPath();
1765
1940
  const shimPath = await installShim(dataDir);
1766
1941
  const listeners = /* @__PURE__ */ new Set();
@@ -1810,7 +1985,7 @@ async function createCodingSessionsEngine(options = {}) {
1810
1985
  }
1811
1986
  }
1812
1987
  function forkWorker() {
1813
- return (0, import_node_child_process2.fork)(workerPath, [], {
1988
+ return (0, import_node_child_process3.fork)(workerPath, [], {
1814
1989
  env: workerEnv,
1815
1990
  cwd: dataDir,
1816
1991
  stdio: ["ignore", "pipe", "pipe", "ipc"],
@@ -1831,7 +2006,7 @@ async function createCodingSessionsEngine(options = {}) {
1831
2006
  }
1832
2007
  async function startRun(session, prompt) {
1833
2008
  const provider = session.provider;
1834
- const realCommand = commandFor(provider);
2009
+ const realCommand = await commandFor(provider);
1835
2010
  const runId = (0, import_node_crypto4.randomUUID)();
1836
2011
  const model = session.model ?? (provider === "claude" ? (await userDefaults(provider, session.cwd)).model ?? null : null);
1837
2012
  const run = {
@@ -2063,7 +2238,7 @@ async function createCodingSessionsEngine(options = {}) {
2063
2238
  }, 3e3);
2064
2239
  }
2065
2240
  async function pruneDriverScratch() {
2066
- const runsDir = import_node_path6.default.join(
2241
+ const runsDir = import_node_path7.default.join(
2067
2242
  stateDir,
2068
2243
  "instances",
2069
2244
  "default",
@@ -2076,10 +2251,11 @@ async function createCodingSessionsEngine(options = {}) {
2076
2251
  );
2077
2252
  const entries = await import_promises6.default.readdir(runsDir).catch(() => []);
2078
2253
  await Promise.all(
2079
- entries.filter((name) => !live.has(name)).map((name) => import_promises6.default.rm(import_node_path6.default.join(runsDir, name), { recursive: true, force: true }).catch(() => void 0))
2254
+ entries.filter((name) => !live.has(name)).map((name) => import_promises6.default.rm(import_node_path7.default.join(runsDir, name), { recursive: true, force: true }).catch(() => void 0))
2080
2255
  );
2081
2256
  }
2082
2257
  async function probe() {
2258
+ const [claudeBinary, codexBinary] = await Promise.all([binaryFor("claude", true), binaryFor("codex", true)]);
2083
2259
  return new Promise((resolve) => {
2084
2260
  const child = forkWorker();
2085
2261
  let answered = false;
@@ -2095,8 +2271,8 @@ async function createCodingSessionsEngine(options = {}) {
2095
2271
  answered = true;
2096
2272
  clearTimeout(timer);
2097
2273
  resolve({
2098
- claude: message.claude ?? {},
2099
- codex: message.codex ?? {}
2274
+ claude: { ...message.claude ?? {}, binary: claudeBinary },
2275
+ codex: { ...message.codex ?? {}, binary: codexBinary }
2100
2276
  });
2101
2277
  }
2102
2278
  });
@@ -2108,7 +2284,7 @@ async function createCodingSessionsEngine(options = {}) {
2108
2284
  });
2109
2285
  child.send({
2110
2286
  kind: "probe",
2111
- commands: { claude: commandFor("claude"), codex: commandFor("codex") },
2287
+ commands: { claude: claudeBinary?.path ?? null, codex: codexBinary?.path ?? null },
2112
2288
  quota: options.quota !== false
2113
2289
  });
2114
2290
  });
@@ -2134,7 +2310,7 @@ async function createCodingSessionsEngine(options = {}) {
2134
2310
  function resolveFolder(raw) {
2135
2311
  const trimmed = raw.trim();
2136
2312
  if (!trimmed) return null;
2137
- return import_node_path6.default.resolve(trimmed.replace(/^~(?=$|\/)/, import_node_os4.default.homedir()));
2313
+ return import_node_path7.default.resolve(trimmed.replace(/^~(?=$|\/)/, import_node_os5.default.homedir()));
2138
2314
  }
2139
2315
  async function checkFolder(raw) {
2140
2316
  const target = resolveFolder(raw);
@@ -2143,7 +2319,7 @@ async function createCodingSessionsEngine(options = {}) {
2143
2319
  return { path: target, exists: stat !== null, isDirectory: stat?.isDirectory() === true };
2144
2320
  }
2145
2321
  async function assertFolder(cwd) {
2146
- const resolved = import_node_path6.default.resolve(cwd.replace(/^~(?=$|\/)/, import_node_os4.default.homedir()));
2322
+ const resolved = import_node_path7.default.resolve(cwd.replace(/^~(?=$|\/)/, import_node_os5.default.homedir()));
2147
2323
  const stat = await import_promises6.default.stat(resolved).catch(() => null);
2148
2324
  if (!stat?.isDirectory()) {
2149
2325
  throw new Error(`The folder ${resolved} doesn't exist on this computer.`);
@@ -2297,7 +2473,7 @@ async function createCodingSessionsEngine(options = {}) {
2297
2473
  };
2298
2474
  }
2299
2475
  const content = await import_promises6.default.readFile(file, "utf8");
2300
- return { available: true, fileName: import_node_path6.default.basename(file), bytes: stat.size, content };
2476
+ return { available: true, fileName: import_node_path7.default.basename(file), bytes: stat.size, content };
2301
2477
  },
2302
2478
  async start(input) {
2303
2479
  const prompt = input.prompt.trim();
@@ -2418,7 +2594,7 @@ function openFolderIn(folder, target) {
2418
2594
  args = ["-a", app.app, folder];
2419
2595
  }
2420
2596
  return new Promise((resolve, reject) => {
2421
- (0, import_node_child_process2.execFile)(command, args, { timeout: 1e4 }, (error) => {
2597
+ (0, import_node_child_process3.execFile)(command, args, { timeout: 1e4 }, (error) => {
2422
2598
  if (!error) return resolve();
2423
2599
  const label = target === "finder" ? "the folder" : OPEN_APP[target]?.label ?? target;
2424
2600
  reject(
@@ -2495,14 +2671,14 @@ var MIME = {
2495
2671
  ".map": "application/json"
2496
2672
  };
2497
2673
  function moduleDir2() {
2498
- return import_node_path7.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
2674
+ return import_node_path8.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
2499
2675
  }
2500
2676
  function defaultUiDir() {
2501
2677
  for (const candidate of [
2502
- import_node_path7.default.resolve(moduleDir2(), "..", "ui"),
2503
- import_node_path7.default.resolve(moduleDir2(), "..", "..", "dist", "ui")
2678
+ import_node_path8.default.resolve(moduleDir2(), "..", "ui"),
2679
+ import_node_path8.default.resolve(moduleDir2(), "..", "..", "dist", "ui")
2504
2680
  ]) {
2505
- if ((0, import_node_fs4.existsSync)(import_node_path7.default.join(candidate, "index.html"))) return candidate;
2681
+ if ((0, import_node_fs5.existsSync)(import_node_path8.default.join(candidate, "index.html"))) return candidate;
2506
2682
  }
2507
2683
  return null;
2508
2684
  }
@@ -2525,19 +2701,19 @@ async function serveStatic(uiDir, req, res) {
2525
2701
  return;
2526
2702
  }
2527
2703
  const requested = decodeURIComponent(url.pathname);
2528
- let file = import_node_path7.default.resolve(uiDir, `.${requested}`);
2704
+ let file = import_node_path8.default.resolve(uiDir, `.${requested}`);
2529
2705
  if (!file.startsWith(uiDir)) {
2530
2706
  res.writeHead(403).end();
2531
2707
  return;
2532
2708
  }
2533
2709
  const stat = await import_promises7.default.stat(file).catch(() => null);
2534
- if (!stat || stat.isDirectory()) file = import_node_path7.default.join(uiDir, "index.html");
2710
+ if (!stat || stat.isDirectory()) file = import_node_path8.default.join(uiDir, "index.html");
2535
2711
  const body = await import_promises7.default.readFile(file).catch(() => null);
2536
2712
  if (!body) {
2537
2713
  res.writeHead(404).end();
2538
2714
  return;
2539
2715
  }
2540
- const ext = import_node_path7.default.extname(file);
2716
+ const ext = import_node_path8.default.extname(file);
2541
2717
  res.writeHead(200, {
2542
2718
  "content-type": MIME[ext] ?? "application/octet-stream",
2543
2719
  "cache-control": ext === ".html" ? "no-store" : "public, max-age=31536000, immutable",