@askexenow/exe-os 0.8.85 → 0.8.87

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.
Files changed (57) hide show
  1. package/dist/bin/cleanup-stale-review-tasks.js +57 -19
  2. package/dist/bin/cli.js +510 -340
  3. package/dist/bin/exe-agent-config.js +242 -0
  4. package/dist/bin/exe-agent.js +3 -3
  5. package/dist/bin/exe-boot.js +344 -346
  6. package/dist/bin/exe-dispatch.js +375 -250
  7. package/dist/bin/exe-forget.js +5 -1
  8. package/dist/bin/exe-gateway.js +260 -135
  9. package/dist/bin/exe-healthcheck.js +133 -1
  10. package/dist/bin/exe-heartbeat.js +72 -31
  11. package/dist/bin/exe-link.js +25 -2
  12. package/dist/bin/exe-new-employee.js +22 -0
  13. package/dist/bin/exe-pending-messages.js +55 -17
  14. package/dist/bin/exe-pending-reviews.js +57 -19
  15. package/dist/bin/exe-search.js +6 -2
  16. package/dist/bin/exe-session-cleanup.js +260 -135
  17. package/dist/bin/exe-start-codex.js +2598 -0
  18. package/dist/bin/exe-start.sh +15 -3
  19. package/dist/bin/exe-status.js +57 -19
  20. package/dist/bin/git-sweep.js +391 -266
  21. package/dist/bin/install.js +22 -0
  22. package/dist/bin/scan-tasks.js +394 -269
  23. package/dist/bin/setup.js +50 -5
  24. package/dist/gateway/index.js +257 -132
  25. package/dist/hooks/bug-report-worker.js +242 -117
  26. package/dist/hooks/commit-complete.js +389 -264
  27. package/dist/hooks/error-recall.js +6 -2
  28. package/dist/hooks/ingest-worker.js +314 -193
  29. package/dist/hooks/post-compact.js +84 -46
  30. package/dist/hooks/pre-compact.js +272 -147
  31. package/dist/hooks/pre-tool-use.js +104 -66
  32. package/dist/hooks/prompt-submit.js +126 -66
  33. package/dist/hooks/session-end.js +277 -152
  34. package/dist/hooks/session-start.js +70 -28
  35. package/dist/hooks/stop.js +90 -52
  36. package/dist/hooks/subagent-stop.js +84 -46
  37. package/dist/hooks/summary-worker.js +175 -114
  38. package/dist/index.js +296 -171
  39. package/dist/lib/agent-config.js +167 -0
  40. package/dist/lib/cloud-sync.js +25 -2
  41. package/dist/lib/exe-daemon.js +338 -213
  42. package/dist/lib/hybrid-search.js +7 -2
  43. package/dist/lib/messaging.js +95 -39
  44. package/dist/lib/runtime-table.js +16 -0
  45. package/dist/lib/session-wrappers.js +22 -0
  46. package/dist/lib/tasks.js +242 -117
  47. package/dist/lib/tmux-routing.js +314 -189
  48. package/dist/mcp/server.js +573 -274
  49. package/dist/mcp/tools/create-task.js +260 -135
  50. package/dist/mcp/tools/list-tasks.js +68 -30
  51. package/dist/mcp/tools/send-message.js +100 -44
  52. package/dist/mcp/tools/update-task.js +123 -67
  53. package/dist/runtime/index.js +276 -151
  54. package/dist/tui/App.js +479 -354
  55. package/package.json +1 -1
  56. package/src/commands/exe/agent-config.md +27 -0
  57. package/src/commands/exe/cc-doctor.md +10 -0
@@ -59,34 +59,34 @@ var init_mcp_prefix = __esm({
59
59
  });
60
60
 
61
61
  // src/lib/project-name.ts
62
- import { execSync as execSync2 } from "child_process";
63
- import path2 from "path";
62
+ import { execSync } from "child_process";
63
+ import path from "path";
64
64
  function getProjectName(cwd) {
65
65
  const dir = cwd ?? process.cwd();
66
66
  if (_cached && _cachedCwd === dir) return _cached;
67
67
  try {
68
68
  let repoRoot;
69
69
  try {
70
- const gitCommonDir = execSync2("git rev-parse --path-format=absolute --git-common-dir", {
70
+ const gitCommonDir = execSync("git rev-parse --path-format=absolute --git-common-dir", {
71
71
  cwd: dir,
72
72
  encoding: "utf8",
73
73
  timeout: 2e3,
74
74
  stdio: ["pipe", "pipe", "pipe"]
75
75
  }).trim();
76
- repoRoot = path2.dirname(gitCommonDir);
76
+ repoRoot = path.dirname(gitCommonDir);
77
77
  } catch {
78
- repoRoot = execSync2("git rev-parse --show-toplevel", {
78
+ repoRoot = execSync("git rev-parse --show-toplevel", {
79
79
  cwd: dir,
80
80
  encoding: "utf8",
81
81
  timeout: 2e3,
82
82
  stdio: ["pipe", "pipe", "pipe"]
83
83
  }).trim();
84
84
  }
85
- _cached = path2.basename(repoRoot);
85
+ _cached = path.basename(repoRoot);
86
86
  _cachedCwd = dir;
87
87
  return _cached;
88
88
  } catch {
89
- _cached = path2.basename(dir);
89
+ _cached = path.basename(dir);
90
90
  _cachedCwd = dir;
91
91
  return _cached;
92
92
  }
@@ -181,15 +181,15 @@ __export(config_exports, {
181
181
  saveConfig: () => saveConfig
182
182
  });
183
183
  import { readFile, writeFile, mkdir, chmod } from "fs/promises";
184
- import { readFileSync as readFileSync2, existsSync as existsSync2, renameSync } from "fs";
185
- import path3 from "path";
186
- import os2 from "os";
184
+ import { readFileSync, existsSync, renameSync } from "fs";
185
+ import path2 from "path";
186
+ import os from "os";
187
187
  function resolveDataDir() {
188
188
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
189
189
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
190
- const newDir = path3.join(os2.homedir(), ".exe-os");
191
- const legacyDir = path3.join(os2.homedir(), ".exe-mem");
192
- if (!existsSync2(newDir) && existsSync2(legacyDir)) {
190
+ const newDir = path2.join(os.homedir(), ".exe-os");
191
+ const legacyDir = path2.join(os.homedir(), ".exe-mem");
192
+ if (!existsSync(newDir) && existsSync(legacyDir)) {
193
193
  try {
194
194
  renameSync(legacyDir, newDir);
195
195
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -253,9 +253,9 @@ function normalizeAutoUpdate(raw) {
253
253
  async function loadConfig() {
254
254
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
255
255
  await mkdir(dir, { recursive: true });
256
- const configPath = path3.join(dir, "config.json");
257
- if (!existsSync2(configPath)) {
258
- return { ...DEFAULT_CONFIG, dbPath: path3.join(dir, "memories.db") };
256
+ const configPath = path2.join(dir, "config.json");
257
+ if (!existsSync(configPath)) {
258
+ return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
259
259
  }
260
260
  const raw = await readFile(configPath, "utf-8");
261
261
  try {
@@ -273,38 +273,38 @@ async function loadConfig() {
273
273
  normalizeScalingRoadmap(migratedCfg);
274
274
  normalizeSessionLifecycle(migratedCfg);
275
275
  normalizeAutoUpdate(migratedCfg);
276
- const config = { ...DEFAULT_CONFIG, dbPath: path3.join(dir, "memories.db"), ...migratedCfg };
276
+ const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
277
277
  if (config.dbPath.startsWith("~")) {
278
- config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
278
+ config.dbPath = config.dbPath.replace(/^~/, os.homedir());
279
279
  }
280
280
  return config;
281
281
  } catch {
282
- return { ...DEFAULT_CONFIG, dbPath: path3.join(dir, "memories.db") };
282
+ return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
283
283
  }
284
284
  }
285
285
  function loadConfigSync() {
286
286
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
287
- const configPath = path3.join(dir, "config.json");
288
- if (!existsSync2(configPath)) {
289
- return { ...DEFAULT_CONFIG, dbPath: path3.join(dir, "memories.db") };
287
+ const configPath = path2.join(dir, "config.json");
288
+ if (!existsSync(configPath)) {
289
+ return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
290
290
  }
291
291
  try {
292
- const raw = readFileSync2(configPath, "utf-8");
292
+ const raw = readFileSync(configPath, "utf-8");
293
293
  let parsed = JSON.parse(raw);
294
294
  parsed = migrateLegacyConfig(parsed);
295
295
  const { config: migratedCfg } = migrateConfig(parsed);
296
296
  normalizeScalingRoadmap(migratedCfg);
297
297
  normalizeSessionLifecycle(migratedCfg);
298
298
  normalizeAutoUpdate(migratedCfg);
299
- return { ...DEFAULT_CONFIG, dbPath: path3.join(dir, "memories.db"), ...migratedCfg };
299
+ return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
300
300
  } catch {
301
- return { ...DEFAULT_CONFIG, dbPath: path3.join(dir, "memories.db") };
301
+ return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
302
302
  }
303
303
  }
304
304
  async function saveConfig(config) {
305
305
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
306
306
  await mkdir(dir, { recursive: true });
307
- const configPath = path3.join(dir, "config.json");
307
+ const configPath = path2.join(dir, "config.json");
308
308
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
309
309
  if (config.cloud?.apiKey) {
310
310
  await chmod(configPath, 384);
@@ -329,10 +329,10 @@ var init_config = __esm({
329
329
  "src/lib/config.ts"() {
330
330
  "use strict";
331
331
  EXE_AI_DIR = resolveDataDir();
332
- DB_PATH = path3.join(EXE_AI_DIR, "memories.db");
333
- MODELS_DIR = path3.join(EXE_AI_DIR, "models");
334
- CONFIG_PATH = path3.join(EXE_AI_DIR, "config.json");
335
- LEGACY_LANCE_PATH = path3.join(EXE_AI_DIR, "local.lance");
332
+ DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
333
+ MODELS_DIR = path2.join(EXE_AI_DIR, "models");
334
+ CONFIG_PATH = path2.join(EXE_AI_DIR, "config.json");
335
+ LEGACY_LANCE_PATH = path2.join(EXE_AI_DIR, "local.lance");
336
336
  CURRENT_CONFIG_VERSION = 1;
337
337
  DEFAULT_CONFIG = {
338
338
  config_version: CURRENT_CONFIG_VERSION,
@@ -406,10 +406,10 @@ var init_config = __esm({
406
406
 
407
407
  // src/lib/employees.ts
408
408
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
409
- import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
410
- import { execSync as execSync3 } from "child_process";
411
- import path4 from "path";
412
- import os3 from "os";
409
+ import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
410
+ import { execSync as execSync2 } from "child_process";
411
+ import path3 from "path";
412
+ import os2 from "os";
413
413
  function normalizeRole(role) {
414
414
  return (role ?? "").trim().toLowerCase();
415
415
  }
@@ -430,9 +430,9 @@ function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
430
430
  return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
431
431
  }
432
432
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
433
- if (!existsSync3(employeesPath)) return [];
433
+ if (!existsSync2(employeesPath)) return [];
434
434
  try {
435
- return JSON.parse(readFileSync3(employeesPath, "utf-8"));
435
+ return JSON.parse(readFileSync2(employeesPath, "utf-8"));
436
436
  } catch {
437
437
  return [];
438
438
  }
@@ -451,7 +451,7 @@ var init_employees = __esm({
451
451
  "src/lib/employees.ts"() {
452
452
  "use strict";
453
453
  init_config();
454
- EMPLOYEES_PATH = path4.join(EXE_AI_DIR, "exe-employees.json");
454
+ EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
455
455
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
456
456
  COORDINATOR_ROLE = "COO";
457
457
  MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
@@ -462,8 +462,8 @@ var init_employees = __esm({
462
462
  import net from "net";
463
463
  import { spawn } from "child_process";
464
464
  import { randomUUID } from "crypto";
465
- import { existsSync as existsSync4, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync as statSync2 } from "fs";
466
- import path5 from "path";
465
+ import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
466
+ import path4 from "path";
467
467
  import { fileURLToPath } from "url";
468
468
  function handleData(chunk) {
469
469
  _buffer += chunk.toString();
@@ -491,9 +491,9 @@ function handleData(chunk) {
491
491
  }
492
492
  }
493
493
  function cleanupStaleFiles() {
494
- if (existsSync4(PID_PATH)) {
494
+ if (existsSync3(PID_PATH)) {
495
495
  try {
496
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
496
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
497
497
  if (pid > 0) {
498
498
  try {
499
499
  process.kill(pid, 0);
@@ -514,11 +514,11 @@ function cleanupStaleFiles() {
514
514
  }
515
515
  }
516
516
  function findPackageRoot() {
517
- let dir = path5.dirname(fileURLToPath(import.meta.url));
518
- const { root } = path5.parse(dir);
517
+ let dir = path4.dirname(fileURLToPath(import.meta.url));
518
+ const { root } = path4.parse(dir);
519
519
  while (dir !== root) {
520
- if (existsSync4(path5.join(dir, "package.json"))) return dir;
521
- dir = path5.dirname(dir);
520
+ if (existsSync3(path4.join(dir, "package.json"))) return dir;
521
+ dir = path4.dirname(dir);
522
522
  }
523
523
  return null;
524
524
  }
@@ -528,8 +528,8 @@ function spawnDaemon() {
528
528
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
529
529
  return;
530
530
  }
531
- const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
532
- if (!existsSync4(daemonPath)) {
531
+ const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
532
+ if (!existsSync3(daemonPath)) {
533
533
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
534
534
  `);
535
535
  return;
@@ -537,7 +537,7 @@ function spawnDaemon() {
537
537
  const resolvedPath = daemonPath;
538
538
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
539
539
  `);
540
- const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
540
+ const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
541
541
  let stderrFd = "ignore";
542
542
  try {
543
543
  stderrFd = openSync(logPath, "a");
@@ -571,7 +571,7 @@ function acquireSpawnLock() {
571
571
  return true;
572
572
  } catch {
573
573
  try {
574
- const stat = statSync2(SPAWN_LOCK_PATH);
574
+ const stat = statSync(SPAWN_LOCK_PATH);
575
575
  if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
576
576
  try {
577
577
  unlinkSync2(SPAWN_LOCK_PATH);
@@ -687,9 +687,9 @@ async function pingDaemon() {
687
687
  }
688
688
  function killAndRespawnDaemon() {
689
689
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
690
- if (existsSync4(PID_PATH)) {
690
+ if (existsSync3(PID_PATH)) {
691
691
  try {
692
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
692
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
693
693
  if (pid > 0) {
694
694
  try {
695
695
  process.kill(pid, "SIGKILL");
@@ -776,9 +776,9 @@ var init_exe_daemon_client = __esm({
776
776
  "src/lib/exe-daemon-client.ts"() {
777
777
  "use strict";
778
778
  init_config();
779
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
780
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
781
- SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
779
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
780
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
781
+ SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
782
782
  SPAWN_LOCK_STALE_MS = 3e4;
783
783
  CONNECT_TIMEOUT_MS = 15e3;
784
784
  REQUEST_TIMEOUT_MS = 3e4;
@@ -2072,12 +2072,12 @@ __export(shard_manager_exports, {
2072
2072
  listShards: () => listShards,
2073
2073
  shardExists: () => shardExists
2074
2074
  });
2075
- import path7 from "path";
2076
- import { existsSync as existsSync6, mkdirSync, readdirSync as readdirSync2 } from "fs";
2075
+ import path6 from "path";
2076
+ import { existsSync as existsSync5, mkdirSync, readdirSync } from "fs";
2077
2077
  import { createClient as createClient2 } from "@libsql/client";
2078
2078
  function initShardManager(encryptionKey) {
2079
2079
  _encryptionKey = encryptionKey;
2080
- if (!existsSync6(SHARDS_DIR)) {
2080
+ if (!existsSync5(SHARDS_DIR)) {
2081
2081
  mkdirSync(SHARDS_DIR, { recursive: true });
2082
2082
  }
2083
2083
  _shardingEnabled = true;
@@ -2098,7 +2098,7 @@ function getShardClient(projectName) {
2098
2098
  }
2099
2099
  const cached = _shards.get(safeName);
2100
2100
  if (cached) return cached;
2101
- const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
2101
+ const dbPath = path6.join(SHARDS_DIR, `${safeName}.db`);
2102
2102
  const client = createClient2({
2103
2103
  url: `file:${dbPath}`,
2104
2104
  encryptionKey: _encryptionKey
@@ -2108,11 +2108,11 @@ function getShardClient(projectName) {
2108
2108
  }
2109
2109
  function shardExists(projectName) {
2110
2110
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
2111
- return existsSync6(path7.join(SHARDS_DIR, `${safeName}.db`));
2111
+ return existsSync5(path6.join(SHARDS_DIR, `${safeName}.db`));
2112
2112
  }
2113
2113
  function listShards() {
2114
- if (!existsSync6(SHARDS_DIR)) return [];
2115
- return readdirSync2(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
2114
+ if (!existsSync5(SHARDS_DIR)) return [];
2115
+ return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
2116
2116
  }
2117
2117
  async function ensureShardSchema(client) {
2118
2118
  await client.execute("PRAGMA journal_mode = WAL");
@@ -2297,7 +2297,7 @@ var init_shard_manager = __esm({
2297
2297
  "src/lib/shard-manager.ts"() {
2298
2298
  "use strict";
2299
2299
  init_config();
2300
- SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
2300
+ SHARDS_DIR = path6.join(EXE_AI_DIR, "shards");
2301
2301
  _shards = /* @__PURE__ */ new Map();
2302
2302
  _encryptionKey = null;
2303
2303
  _shardingEnabled = false;
@@ -2487,13 +2487,13 @@ ${p.content}`).join("\n\n");
2487
2487
 
2488
2488
  // src/lib/notifications.ts
2489
2489
  import crypto2 from "crypto";
2490
- import path8 from "path";
2491
- import os5 from "os";
2490
+ import path7 from "path";
2491
+ import os4 from "os";
2492
2492
  import {
2493
- readFileSync as readFileSync5,
2494
- readdirSync as readdirSync3,
2493
+ readFileSync as readFileSync4,
2494
+ readdirSync as readdirSync2,
2495
2495
  unlinkSync as unlinkSync3,
2496
- existsSync as existsSync7,
2496
+ existsSync as existsSync6,
2497
2497
  rmdirSync
2498
2498
  } from "fs";
2499
2499
  async function writeNotification(notification) {
@@ -2538,9 +2538,9 @@ var init_notifications = __esm({
2538
2538
  });
2539
2539
 
2540
2540
  // src/lib/license.ts
2541
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync2 } from "fs";
2541
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
2542
2542
  import { randomUUID as randomUUID3 } from "crypto";
2543
- import path9 from "path";
2543
+ import path8 from "path";
2544
2544
  import { jwtVerify, importSPKI } from "jose";
2545
2545
  async function fetchRetry(url, init) {
2546
2546
  try {
@@ -2551,17 +2551,17 @@ async function fetchRetry(url, init) {
2551
2551
  }
2552
2552
  }
2553
2553
  function loadDeviceId() {
2554
- const deviceJsonPath = path9.join(EXE_AI_DIR, "device.json");
2554
+ const deviceJsonPath = path8.join(EXE_AI_DIR, "device.json");
2555
2555
  try {
2556
- if (existsSync8(deviceJsonPath)) {
2557
- const data = JSON.parse(readFileSync6(deviceJsonPath, "utf8"));
2556
+ if (existsSync7(deviceJsonPath)) {
2557
+ const data = JSON.parse(readFileSync5(deviceJsonPath, "utf8"));
2558
2558
  if (data.deviceId) return data.deviceId;
2559
2559
  }
2560
2560
  } catch {
2561
2561
  }
2562
2562
  try {
2563
- if (existsSync8(DEVICE_ID_PATH)) {
2564
- const id2 = readFileSync6(DEVICE_ID_PATH, "utf8").trim();
2563
+ if (existsSync7(DEVICE_ID_PATH)) {
2564
+ const id2 = readFileSync5(DEVICE_ID_PATH, "utf8").trim();
2565
2565
  if (id2) return id2;
2566
2566
  }
2567
2567
  } catch {
@@ -2573,8 +2573,8 @@ function loadDeviceId() {
2573
2573
  }
2574
2574
  function loadLicense() {
2575
2575
  try {
2576
- if (!existsSync8(LICENSE_PATH)) return null;
2577
- return readFileSync6(LICENSE_PATH, "utf8").trim();
2576
+ if (!existsSync7(LICENSE_PATH)) return null;
2577
+ return readFileSync5(LICENSE_PATH, "utf8").trim();
2578
2578
  } catch {
2579
2579
  return null;
2580
2580
  }
@@ -2607,8 +2607,8 @@ async function verifyLicenseJwt(token) {
2607
2607
  }
2608
2608
  async function getCachedLicense() {
2609
2609
  try {
2610
- if (!existsSync8(CACHE_PATH)) return null;
2611
- const raw = JSON.parse(readFileSync6(CACHE_PATH, "utf8"));
2610
+ if (!existsSync7(CACHE_PATH)) return null;
2611
+ const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
2612
2612
  if (!raw.token || typeof raw.token !== "string") return null;
2613
2613
  return await verifyLicenseJwt(raw.token);
2614
2614
  } catch {
@@ -2617,8 +2617,8 @@ async function getCachedLicense() {
2617
2617
  }
2618
2618
  function readCachedToken() {
2619
2619
  try {
2620
- if (!existsSync8(CACHE_PATH)) return null;
2621
- const raw = JSON.parse(readFileSync6(CACHE_PATH, "utf8"));
2620
+ if (!existsSync7(CACHE_PATH)) return null;
2621
+ const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
2622
2622
  return typeof raw.token === "string" ? raw.token : null;
2623
2623
  } catch {
2624
2624
  return null;
@@ -2705,8 +2705,8 @@ async function validateLicense(apiKey, deviceId) {
2705
2705
  }
2706
2706
  function getCacheAgeMs() {
2707
2707
  try {
2708
- const { statSync: statSync3 } = __require("fs");
2709
- const s = statSync3(CACHE_PATH);
2708
+ const { statSync: statSync2 } = __require("fs");
2709
+ const s = statSync2(CACHE_PATH);
2710
2710
  return Date.now() - s.mtimeMs;
2711
2711
  } catch {
2712
2712
  return Infinity;
@@ -2716,9 +2716,9 @@ async function checkLicense() {
2716
2716
  let key = loadLicense();
2717
2717
  if (!key) {
2718
2718
  try {
2719
- const configPath = path9.join(EXE_AI_DIR, "config.json");
2720
- if (existsSync8(configPath)) {
2721
- const raw = JSON.parse(readFileSync6(configPath, "utf8"));
2719
+ const configPath = path8.join(EXE_AI_DIR, "config.json");
2720
+ if (existsSync7(configPath)) {
2721
+ const raw = JSON.parse(readFileSync5(configPath, "utf8"));
2722
2722
  const cloud = raw.cloud;
2723
2723
  if (cloud?.apiKey) {
2724
2724
  key = cloud.apiKey;
@@ -2739,9 +2739,9 @@ var init_license = __esm({
2739
2739
  "src/lib/license.ts"() {
2740
2740
  "use strict";
2741
2741
  init_config();
2742
- LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
2743
- CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
2744
- DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
2742
+ LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
2743
+ CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
2744
+ DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
2745
2745
  API_BASE = "https://askexe.com/cloud";
2746
2746
  RETRY_DELAY_MS = 500;
2747
2747
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
@@ -2770,12 +2770,12 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
2770
2770
  });
2771
2771
 
2772
2772
  // src/lib/plan-limits.ts
2773
- import { readFileSync as readFileSync7, existsSync as existsSync9 } from "fs";
2774
- import path10 from "path";
2773
+ import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
2774
+ import path9 from "path";
2775
2775
  function getLicenseSync() {
2776
2776
  try {
2777
- if (!existsSync9(CACHE_PATH2)) return freeLicense();
2778
- const raw = JSON.parse(readFileSync7(CACHE_PATH2, "utf8"));
2777
+ if (!existsSync8(CACHE_PATH2)) return freeLicense();
2778
+ const raw = JSON.parse(readFileSync6(CACHE_PATH2, "utf8"));
2779
2779
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
2780
2780
  const parts = raw.token.split(".");
2781
2781
  if (parts.length !== 3) return freeLicense();
@@ -2832,8 +2832,8 @@ function assertEmployeeLimitSync(rosterPath) {
2832
2832
  const filePath = rosterPath ?? EMPLOYEES_PATH;
2833
2833
  let count = 0;
2834
2834
  try {
2835
- if (existsSync9(filePath)) {
2836
- const raw = readFileSync7(filePath, "utf8");
2835
+ if (existsSync8(filePath)) {
2836
+ const raw = readFileSync6(filePath, "utf8");
2837
2837
  const employees = JSON.parse(raw);
2838
2838
  count = Array.isArray(employees) ? employees.length : 0;
2839
2839
  }
@@ -2862,7 +2862,7 @@ var init_plan_limits = __esm({
2862
2862
  this.name = "PlanLimitError";
2863
2863
  }
2864
2864
  };
2865
- CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
2865
+ CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
2866
2866
  }
2867
2867
  });
2868
2868
 
@@ -2935,12 +2935,12 @@ var init_embedder = __esm({
2935
2935
  });
2936
2936
 
2937
2937
  // src/lib/session-registry.ts
2938
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync10 } from "fs";
2939
- import path11 from "path";
2940
- import os6 from "os";
2938
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync9 } from "fs";
2939
+ import path10 from "path";
2940
+ import os5 from "os";
2941
2941
  function registerSession(entry) {
2942
- const dir = path11.dirname(REGISTRY_PATH);
2943
- if (!existsSync10(dir)) {
2942
+ const dir = path10.dirname(REGISTRY_PATH);
2943
+ if (!existsSync9(dir)) {
2944
2944
  mkdirSync3(dir, { recursive: true });
2945
2945
  }
2946
2946
  const sessions = listSessions();
@@ -2954,7 +2954,7 @@ function registerSession(entry) {
2954
2954
  }
2955
2955
  function listSessions() {
2956
2956
  try {
2957
- const raw = readFileSync8(REGISTRY_PATH, "utf8");
2957
+ const raw = readFileSync7(REGISTRY_PATH, "utf8");
2958
2958
  return JSON.parse(raw);
2959
2959
  } catch {
2960
2960
  return [];
@@ -2964,18 +2964,18 @@ var REGISTRY_PATH;
2964
2964
  var init_session_registry = __esm({
2965
2965
  "src/lib/session-registry.ts"() {
2966
2966
  "use strict";
2967
- REGISTRY_PATH = path11.join(os6.homedir(), ".exe-os", "session-registry.json");
2967
+ REGISTRY_PATH = path10.join(os5.homedir(), ".exe-os", "session-registry.json");
2968
2968
  }
2969
2969
  });
2970
2970
 
2971
2971
  // src/lib/session-key.ts
2972
- import { execSync as execSync4 } from "child_process";
2972
+ import { execSync as execSync3 } from "child_process";
2973
2973
  function getSessionKey() {
2974
2974
  if (_cached2) return _cached2;
2975
2975
  let pid = process.ppid;
2976
2976
  for (let i = 0; i < 10; i++) {
2977
2977
  try {
2978
- const info = execSync4(`ps -p ${pid} -o ppid=,comm=`, {
2978
+ const info = execSync3(`ps -p ${pid} -o ppid=,comm=`, {
2979
2979
  encoding: "utf8",
2980
2980
  timeout: 2e3
2981
2981
  }).trim();
@@ -3111,14 +3111,14 @@ var init_transport = __esm({
3111
3111
  });
3112
3112
 
3113
3113
  // src/lib/cc-agent-support.ts
3114
- import { execSync as execSync5 } from "child_process";
3114
+ import { execSync as execSync4 } from "child_process";
3115
3115
  function _resetCcAgentSupportCache() {
3116
3116
  _cachedSupport = null;
3117
3117
  }
3118
3118
  function claudeSupportsAgentFlag() {
3119
3119
  if (_cachedSupport !== null) return _cachedSupport;
3120
3120
  try {
3121
- const helpOutput = execSync5("claude --help 2>&1", {
3121
+ const helpOutput = execSync4("claude --help 2>&1", {
3122
3122
  encoding: "utf-8",
3123
3123
  timeout: 5e3
3124
3124
  });
@@ -3160,13 +3160,64 @@ var init_provider_table = __esm({
3160
3160
  }
3161
3161
  });
3162
3162
 
3163
+ // src/lib/runtime-table.ts
3164
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
3165
+ var init_runtime_table = __esm({
3166
+ "src/lib/runtime-table.ts"() {
3167
+ "use strict";
3168
+ RUNTIME_TABLE = {
3169
+ codex: {
3170
+ binary: "codex",
3171
+ launchMode: "exec",
3172
+ autoApproveFlag: "--full-auto",
3173
+ inlineFlag: "--no-alt-screen",
3174
+ apiKeyEnv: "OPENAI_API_KEY",
3175
+ defaultModel: "gpt-5.4"
3176
+ }
3177
+ };
3178
+ DEFAULT_RUNTIME = "claude";
3179
+ }
3180
+ });
3181
+
3182
+ // src/lib/agent-config.ts
3183
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync4, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
3184
+ import path11 from "path";
3185
+ function loadAgentConfig() {
3186
+ if (!existsSync10(AGENT_CONFIG_PATH)) return {};
3187
+ try {
3188
+ return JSON.parse(readFileSync8(AGENT_CONFIG_PATH, "utf-8"));
3189
+ } catch {
3190
+ return {};
3191
+ }
3192
+ }
3193
+ function getAgentRuntime(agentId) {
3194
+ const config = loadAgentConfig();
3195
+ const entry = config[agentId];
3196
+ if (entry) return entry;
3197
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
3198
+ }
3199
+ var AGENT_CONFIG_PATH, DEFAULT_MODELS;
3200
+ var init_agent_config = __esm({
3201
+ "src/lib/agent-config.ts"() {
3202
+ "use strict";
3203
+ init_config();
3204
+ init_runtime_table();
3205
+ AGENT_CONFIG_PATH = path11.join(EXE_AI_DIR, "agent-config.json");
3206
+ DEFAULT_MODELS = {
3207
+ claude: "claude-opus-4",
3208
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
3209
+ opencode: "minimax-m2.7"
3210
+ };
3211
+ }
3212
+ });
3213
+
3163
3214
  // src/lib/intercom-queue.ts
3164
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync11, mkdirSync as mkdirSync4 } from "fs";
3215
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync11, mkdirSync as mkdirSync5 } from "fs";
3165
3216
  import path12 from "path";
3166
- import os7 from "os";
3217
+ import os6 from "os";
3167
3218
  function ensureDir() {
3168
3219
  const dir = path12.dirname(QUEUE_PATH);
3169
- if (!existsSync11(dir)) mkdirSync4(dir, { recursive: true });
3220
+ if (!existsSync11(dir)) mkdirSync5(dir, { recursive: true });
3170
3221
  }
3171
3222
  function readQueue() {
3172
3223
  try {
@@ -3179,7 +3230,7 @@ function readQueue() {
3179
3230
  function writeQueue(queue) {
3180
3231
  ensureDir();
3181
3232
  const tmp = `${QUEUE_PATH}.tmp`;
3182
- writeFileSync4(tmp, JSON.stringify(queue, null, 2));
3233
+ writeFileSync5(tmp, JSON.stringify(queue, null, 2));
3183
3234
  renameSync3(tmp, QUEUE_PATH);
3184
3235
  }
3185
3236
  function queueIntercom(targetSession, reason) {
@@ -3203,9 +3254,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
3203
3254
  var init_intercom_queue = __esm({
3204
3255
  "src/lib/intercom-queue.ts"() {
3205
3256
  "use strict";
3206
- QUEUE_PATH = path12.join(os7.homedir(), ".exe-os", "intercom-queue.json");
3257
+ QUEUE_PATH = path12.join(os6.homedir(), ".exe-os", "intercom-queue.json");
3207
3258
  TTL_MS = 60 * 60 * 1e3;
3208
- INTERCOM_LOG = path12.join(os7.homedir(), ".exe-os", "intercom.log");
3259
+ INTERCOM_LOG = path12.join(os6.homedir(), ".exe-os", "intercom.log");
3209
3260
  }
3210
3261
  });
3211
3262
 
@@ -3552,10 +3603,10 @@ __export(tmux_routing_exports, {
3552
3603
  spawnEmployee: () => spawnEmployee,
3553
3604
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
3554
3605
  });
3555
- import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
3556
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync12, appendFileSync } from "fs";
3606
+ import { execFileSync as execFileSync2, execSync as execSync5 } from "child_process";
3607
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync } from "fs";
3557
3608
  import path13 from "path";
3558
- import os8 from "os";
3609
+ import os7 from "os";
3559
3610
  import { fileURLToPath as fileURLToPath2 } from "url";
3560
3611
  import { unlinkSync as unlinkSync4 } from "fs";
3561
3612
  function spawnLockPath(sessionName) {
@@ -3571,7 +3622,7 @@ function isProcessAlive(pid) {
3571
3622
  }
3572
3623
  function acquireSpawnLock2(sessionName) {
3573
3624
  if (!existsSync12(SPAWN_LOCK_DIR)) {
3574
- mkdirSync5(SPAWN_LOCK_DIR, { recursive: true });
3625
+ mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
3575
3626
  }
3576
3627
  const lockFile = spawnLockPath(sessionName);
3577
3628
  if (existsSync12(lockFile)) {
@@ -3584,7 +3635,7 @@ function acquireSpawnLock2(sessionName) {
3584
3635
  } catch {
3585
3636
  }
3586
3637
  }
3587
- writeFileSync5(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
3638
+ writeFileSync6(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
3588
3639
  return true;
3589
3640
  }
3590
3641
  function releaseSpawnLock2(sessionName) {
@@ -3669,11 +3720,11 @@ function extractRootExe(name) {
3669
3720
  }
3670
3721
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
3671
3722
  if (!existsSync12(SESSION_CACHE)) {
3672
- mkdirSync5(SESSION_CACHE, { recursive: true });
3723
+ mkdirSync6(SESSION_CACHE, { recursive: true });
3673
3724
  }
3674
3725
  const rootExe = extractRootExe(parentExe) ?? parentExe;
3675
3726
  const filePath = path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
3676
- writeFileSync5(filePath, JSON.stringify({
3727
+ writeFileSync6(filePath, JSON.stringify({
3677
3728
  parentExe: rootExe,
3678
3729
  dispatchedBy: dispatchedBy || rootExe,
3679
3730
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -3752,31 +3803,49 @@ async function verifyPaneAtCapacity(sessionName) {
3752
3803
  function readDebounceState() {
3753
3804
  try {
3754
3805
  if (!existsSync12(DEBOUNCE_FILE)) return {};
3755
- return JSON.parse(readFileSync10(DEBOUNCE_FILE, "utf8"));
3806
+ const raw = JSON.parse(readFileSync10(DEBOUNCE_FILE, "utf8"));
3807
+ const state = {};
3808
+ for (const [key, val] of Object.entries(raw)) {
3809
+ if (typeof val === "number") {
3810
+ state[key] = { lastSent: val, pending: 0 };
3811
+ } else if (val && typeof val === "object" && "lastSent" in val) {
3812
+ state[key] = val;
3813
+ }
3814
+ }
3815
+ return state;
3756
3816
  } catch {
3757
3817
  return {};
3758
3818
  }
3759
3819
  }
3760
3820
  function writeDebounceState(state) {
3761
3821
  try {
3762
- if (!existsSync12(SESSION_CACHE)) mkdirSync5(SESSION_CACHE, { recursive: true });
3763
- writeFileSync5(DEBOUNCE_FILE, JSON.stringify(state));
3822
+ if (!existsSync12(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
3823
+ writeFileSync6(DEBOUNCE_FILE, JSON.stringify(state));
3764
3824
  } catch {
3765
3825
  }
3766
3826
  }
3767
3827
  function isDebounced(targetSession) {
3768
3828
  const state = readDebounceState();
3769
- const lastSent = state[targetSession] ?? 0;
3770
- return Date.now() - lastSent < INTERCOM_DEBOUNCE_MS;
3829
+ const entry = state[targetSession];
3830
+ const lastSent = entry?.lastSent ?? 0;
3831
+ if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
3832
+ if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
3833
+ state[targetSession].pending++;
3834
+ writeDebounceState(state);
3835
+ return true;
3836
+ }
3837
+ return false;
3771
3838
  }
3772
3839
  function recordDebounce(targetSession) {
3773
3840
  const state = readDebounceState();
3774
- state[targetSession] = Date.now();
3841
+ const batched = state[targetSession]?.pending ?? 0;
3842
+ state[targetSession] = { lastSent: Date.now(), pending: 0 };
3775
3843
  const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
3776
3844
  for (const key of Object.keys(state)) {
3777
- if ((state[key] ?? 0) < cutoff) delete state[key];
3845
+ if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
3778
3846
  }
3779
3847
  writeDebounceState(state);
3848
+ return batched;
3780
3849
  }
3781
3850
  function logIntercom(msg) {
3782
3851
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
@@ -3821,7 +3890,7 @@ function sendIntercom(targetSession) {
3821
3890
  return "skipped_exe";
3822
3891
  }
3823
3892
  if (isDebounced(targetSession)) {
3824
- logIntercom(`DEBOUNCE \u2192 ${targetSession} (cross-process file debounce)`);
3893
+ logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
3825
3894
  return "debounced";
3826
3895
  }
3827
3896
  try {
@@ -3833,14 +3902,14 @@ function sendIntercom(targetSession) {
3833
3902
  const sessionState = getSessionState(targetSession);
3834
3903
  if (sessionState === "no_claude") {
3835
3904
  queueIntercom(targetSession, "claude not running in session");
3836
- recordDebounce(targetSession);
3837
- logIntercom(`QUEUED \u2192 ${targetSession} (no claude process \u2014 raw shell detected)`);
3905
+ const batched2 = recordDebounce(targetSession);
3906
+ logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
3838
3907
  return "queued";
3839
3908
  }
3840
3909
  if (sessionState === "thinking" || sessionState === "tool") {
3841
3910
  queueIntercom(targetSession, "session busy at send time");
3842
- recordDebounce(targetSession);
3843
- logIntercom(`QUEUED \u2192 ${targetSession} (session busy, will retry from queue)`);
3911
+ const batched2 = recordDebounce(targetSession);
3912
+ logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
3844
3913
  return "queued";
3845
3914
  }
3846
3915
  if (transport.isPaneInCopyMode(targetSession)) {
@@ -3848,8 +3917,8 @@ function sendIntercom(targetSession) {
3848
3917
  transport.sendKeys(targetSession, "q");
3849
3918
  }
3850
3919
  transport.sendKeys(targetSession, "/exe-intercom");
3851
- recordDebounce(targetSession);
3852
- logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
3920
+ const batched = recordDebounce(targetSession);
3921
+ logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
3853
3922
  return "delivered";
3854
3923
  } catch {
3855
3924
  logIntercom(`FAIL \u2192 ${targetSession}`);
@@ -3951,10 +4020,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3951
4020
  const transport = getTransport();
3952
4021
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
3953
4022
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
3954
- const logDir = path13.join(os8.homedir(), ".exe-os", "session-logs");
4023
+ const logDir = path13.join(os7.homedir(), ".exe-os", "session-logs");
3955
4024
  const logFile = path13.join(logDir, `${instanceLabel}-${Date.now()}.log`);
3956
4025
  if (!existsSync12(logDir)) {
3957
- mkdirSync5(logDir, { recursive: true });
4026
+ mkdirSync6(logDir, { recursive: true });
3958
4027
  }
3959
4028
  transport.kill(sessionName);
3960
4029
  let cleanupSuffix = "";
@@ -3967,7 +4036,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3967
4036
  } catch {
3968
4037
  }
3969
4038
  try {
3970
- const claudeJsonPath = path13.join(os8.homedir(), ".claude.json");
4039
+ const claudeJsonPath = path13.join(os7.homedir(), ".claude.json");
3971
4040
  let claudeJson = {};
3972
4041
  try {
3973
4042
  claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
@@ -3978,11 +4047,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3978
4047
  const trustDir = opts?.cwd ?? projectDir;
3979
4048
  if (!projects[trustDir]) projects[trustDir] = {};
3980
4049
  projects[trustDir].hasTrustDialogAccepted = true;
3981
- writeFileSync5(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
4050
+ writeFileSync6(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
3982
4051
  } catch {
3983
4052
  }
3984
4053
  try {
3985
- const settingsDir = path13.join(os8.homedir(), ".claude", "projects");
4054
+ const settingsDir = path13.join(os7.homedir(), ".claude", "projects");
3986
4055
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
3987
4056
  const projSettingsDir = path13.join(settingsDir, normalizedKey);
3988
4057
  const settingsPath = path13.join(projSettingsDir, "settings.json");
@@ -4016,21 +4085,24 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4016
4085
  if (changed) {
4017
4086
  perms.allow = allow;
4018
4087
  settings.permissions = perms;
4019
- mkdirSync5(projSettingsDir, { recursive: true });
4020
- writeFileSync5(settingsPath, JSON.stringify(settings, null, 2) + "\n");
4088
+ mkdirSync6(projSettingsDir, { recursive: true });
4089
+ writeFileSync6(settingsPath, JSON.stringify(settings, null, 2) + "\n");
4021
4090
  }
4022
4091
  } catch {
4023
4092
  }
4024
4093
  const spawnCwd = opts?.cwd ?? projectDir;
4025
4094
  const useExeAgent = !!(opts?.model && opts?.provider);
4026
- const ccProvider = useExeAgent ? DEFAULT_PROVIDER : detectActiveProvider();
4095
+ const agentRtConfig = getAgentRuntime(employeeName);
4096
+ const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
4097
+ const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
4098
+ const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
4027
4099
  const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
4028
4100
  let identityFlag = "";
4029
4101
  let behaviorsFlag = "";
4030
4102
  let legacyFallbackWarned = false;
4031
4103
  if (!useExeAgent && !useBinSymlink) {
4032
4104
  const identityPath = path13.join(
4033
- os8.homedir(),
4105
+ os7.homedir(),
4034
4106
  ".exe-os",
4035
4107
  "identity",
4036
4108
  `${employeeName}.md`
@@ -4060,8 +4132,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4060
4132
  }
4061
4133
  let sessionContextFlag = "";
4062
4134
  try {
4063
- const ctxDir = path13.join(os8.homedir(), ".exe-os", "session-cache");
4064
- mkdirSync5(ctxDir, { recursive: true });
4135
+ const ctxDir = path13.join(os7.homedir(), ".exe-os", "session-cache");
4136
+ mkdirSync6(ctxDir, { recursive: true });
4065
4137
  const ctxFile = path13.join(ctxDir, `session-context-${sessionName}.md`);
4066
4138
  const ctxContent = [
4067
4139
  `## Session Context`,
@@ -4069,7 +4141,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4069
4141
  `Your parent coordinator session is ${exeSession}.`,
4070
4142
  `Your employees (if any) use the -${exeSession} suffix.`
4071
4143
  ].join("\n");
4072
- writeFileSync5(ctxFile, ctxContent);
4144
+ writeFileSync6(ctxFile, ctxContent);
4073
4145
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
4074
4146
  } catch {
4075
4147
  }
@@ -4083,9 +4155,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4083
4155
  }
4084
4156
  }
4085
4157
  }
4158
+ if (useCodex) {
4159
+ const codexCfg = RUNTIME_TABLE.codex;
4160
+ if (codexCfg?.apiKeyEnv) {
4161
+ const keyVal = process.env[codexCfg.apiKeyEnv];
4162
+ if (keyVal) {
4163
+ envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
4164
+ }
4165
+ }
4166
+ envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
4167
+ }
4168
+ if (useOpencode) {
4169
+ const ocCfg = PROVIDER_TABLE.opencode;
4170
+ if (ocCfg?.apiKeyEnv) {
4171
+ const keyVal = process.env[ocCfg.apiKeyEnv];
4172
+ if (keyVal) {
4173
+ envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
4174
+ }
4175
+ }
4176
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
4177
+ }
4178
+ if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
4179
+ const defaultClaudeModel = DEFAULT_MODELS.claude;
4180
+ if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
4181
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
4182
+ }
4183
+ }
4086
4184
  let spawnCommand;
4087
4185
  if (useExeAgent) {
4088
4186
  spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
4187
+ } else if (useCodex) {
4188
+ process.stderr.write(
4189
+ `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
4190
+ `
4191
+ );
4192
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
4193
+ } else if (useOpencode) {
4194
+ const binName = `${employeeName}-opencode`;
4195
+ process.stderr.write(
4196
+ `[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
4197
+ `
4198
+ );
4199
+ spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
4089
4200
  } else if (useBinSymlink) {
4090
4201
  const binName = `${employeeName}-${ccProvider}`;
4091
4202
  process.stderr.write(
@@ -4108,10 +4219,12 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4108
4219
  try {
4109
4220
  const mySession = getMySession();
4110
4221
  const dispatchInfo = path13.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
4111
- writeFileSync5(dispatchInfo, JSON.stringify({
4222
+ writeFileSync6(dispatchInfo, JSON.stringify({
4112
4223
  dispatchedBy: mySession,
4113
4224
  rootExe: exeSession,
4114
- provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
4225
+ provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
4226
+ runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
4227
+ model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
4115
4228
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
4116
4229
  }));
4117
4230
  } catch {
@@ -4119,7 +4232,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4119
4232
  let booted = false;
4120
4233
  for (let i = 0; i < 30; i++) {
4121
4234
  try {
4122
- execSync6("sleep 0.5");
4235
+ execSync5("sleep 0.5");
4123
4236
  } catch {
4124
4237
  }
4125
4238
  try {
@@ -4129,6 +4242,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4129
4242
  booted = true;
4130
4243
  break;
4131
4244
  }
4245
+ } else if (useCodex) {
4246
+ if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
4247
+ booted = true;
4248
+ break;
4249
+ }
4132
4250
  } else {
4133
4251
  if (pane.includes("Claude Code") || pane.includes("\u276F")) {
4134
4252
  booted = true;
@@ -4140,9 +4258,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4140
4258
  }
4141
4259
  if (!booted) {
4142
4260
  releaseSpawnLock2(sessionName);
4143
- return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
4261
+ const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
4262
+ return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
4144
4263
  }
4145
- if (!useExeAgent) {
4264
+ if (!useExeAgent && !useCodex) {
4146
4265
  try {
4147
4266
  transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
4148
4267
  } catch {
@@ -4169,16 +4288,18 @@ var init_tmux_routing = __esm({
4169
4288
  init_cc_agent_support();
4170
4289
  init_mcp_prefix();
4171
4290
  init_provider_table();
4291
+ init_agent_config();
4292
+ init_runtime_table();
4172
4293
  init_intercom_queue();
4173
4294
  init_plan_limits();
4174
4295
  init_employees();
4175
- SPAWN_LOCK_DIR = path13.join(os8.homedir(), ".exe-os", "spawn-locks");
4176
- SESSION_CACHE = path13.join(os8.homedir(), ".exe-os", "session-cache");
4296
+ SPAWN_LOCK_DIR = path13.join(os7.homedir(), ".exe-os", "spawn-locks");
4297
+ SESSION_CACHE = path13.join(os7.homedir(), ".exe-os", "session-cache");
4177
4298
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
4178
4299
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
4179
4300
  VERIFY_PANE_LINES = 200;
4180
4301
  INTERCOM_DEBOUNCE_MS = 3e4;
4181
- INTERCOM_LOG2 = path13.join(os8.homedir(), ".exe-os", "intercom.log");
4302
+ INTERCOM_LOG2 = path13.join(os7.homedir(), ".exe-os", "intercom.log");
4182
4303
  DEBOUNCE_FILE = path13.join(SESSION_CACHE, "intercom-debounce.json");
4183
4304
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
4184
4305
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
@@ -4212,8 +4333,8 @@ var init_task_scope = __esm({
4212
4333
  // src/lib/tasks-crud.ts
4213
4334
  import crypto4 from "crypto";
4214
4335
  import path14 from "path";
4215
- import os9 from "os";
4216
- import { execSync as execSync7 } from "child_process";
4336
+ import os8 from "os";
4337
+ import { execSync as execSync6 } from "child_process";
4217
4338
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
4218
4339
  import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
4219
4340
  async function writeCheckpoint(input2) {
@@ -4427,7 +4548,7 @@ ${laneWarning}` : laneWarning;
4427
4548
  });
4428
4549
  if (input2.baseDir) {
4429
4550
  try {
4430
- const EXE_OS_DIR = path14.join(os9.homedir(), ".exe-os");
4551
+ const EXE_OS_DIR = path14.join(os8.homedir(), ".exe-os");
4431
4552
  const mdPath = path14.join(EXE_OS_DIR, taskFile);
4432
4553
  const mdDir = path14.dirname(mdPath);
4433
4554
  if (!existsSync13(mdDir)) await mkdir4(mdDir, { recursive: true });
@@ -4455,7 +4576,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
4455
4576
  Do NOT let a failed commit or any error prevent you from calling update_task(done).
4456
4577
  `;
4457
4578
  await writeFile4(mdPath, mdContent, "utf-8");
4458
- } catch {
4579
+ } catch (err) {
4580
+ process.stderr.write(
4581
+ `[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
4582
+ `
4583
+ );
4459
4584
  }
4460
4585
  }
4461
4586
  return {
@@ -4530,14 +4655,14 @@ function isTmuxSessionAlive(identifier) {
4530
4655
  if (!identifier || identifier === "unknown") return true;
4531
4656
  try {
4532
4657
  if (identifier.startsWith("%")) {
4533
- const output = execSync7("tmux list-panes -a -F '#{pane_id}'", {
4658
+ const output = execSync6("tmux list-panes -a -F '#{pane_id}'", {
4534
4659
  timeout: 2e3,
4535
4660
  encoding: "utf8",
4536
4661
  stdio: ["pipe", "pipe", "pipe"]
4537
4662
  });
4538
4663
  return output.split("\n").some((l) => l.trim() === identifier);
4539
4664
  } else {
4540
- execSync7(`tmux has-session -t ${JSON.stringify(identifier)}`, {
4665
+ execSync6(`tmux has-session -t ${JSON.stringify(identifier)}`, {
4541
4666
  timeout: 2e3,
4542
4667
  stdio: ["pipe", "pipe", "pipe"]
4543
4668
  });
@@ -4546,7 +4671,7 @@ function isTmuxSessionAlive(identifier) {
4546
4671
  } catch {
4547
4672
  if (identifier.startsWith("%")) return true;
4548
4673
  try {
4549
- execSync7("tmux list-sessions", {
4674
+ execSync6("tmux list-sessions", {
4550
4675
  timeout: 2e3,
4551
4676
  stdio: ["pipe", "pipe", "pipe"]
4552
4677
  });
@@ -4561,12 +4686,12 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
4561
4686
  if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
4562
4687
  try {
4563
4688
  const since = new Date(taskCreatedAt).toISOString();
4564
- const branch = execSync7(
4689
+ const branch = execSync6(
4565
4690
  "git rev-parse --abbrev-ref HEAD 2>/dev/null",
4566
4691
  { encoding: "utf8", timeout: 3e3 }
4567
4692
  ).trim();
4568
4693
  const branchArg = branch && branch !== "HEAD" ? branch : "";
4569
- const commitCount = execSync7(
4694
+ const commitCount = execSync6(
4570
4695
  `git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
4571
4696
  { encoding: "utf8", timeout: 5e3 }
4572
4697
  ).trim();
@@ -4785,7 +4910,7 @@ var init_tasks_crud = __esm({
4785
4910
 
4786
4911
  // src/lib/tasks-review.ts
4787
4912
  import path15 from "path";
4788
- import { existsSync as existsSync14, readdirSync as readdirSync4, unlinkSync as unlinkSync5 } from "fs";
4913
+ import { existsSync as existsSync14, readdirSync as readdirSync3, unlinkSync as unlinkSync5 } from "fs";
4789
4914
  async function countPendingReviews(sessionScope) {
4790
4915
  const client = getClient();
4791
4916
  if (sessionScope) {
@@ -4968,7 +5093,7 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
4968
5093
  try {
4969
5094
  const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
4970
5095
  if (existsSync14(cacheDir)) {
4971
- for (const f of readdirSync4(cacheDir)) {
5096
+ for (const f of readdirSync3(cacheDir)) {
4972
5097
  if (f.startsWith("review-notified-")) {
4973
5098
  unlinkSync5(path15.join(cacheDir, f));
4974
5099
  }
@@ -5535,7 +5660,7 @@ __export(tasks_exports, {
5535
5660
  writeCheckpoint: () => writeCheckpoint
5536
5661
  });
5537
5662
  import path17 from "path";
5538
- import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, unlinkSync as unlinkSync6 } from "fs";
5663
+ import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, unlinkSync as unlinkSync6 } from "fs";
5539
5664
  async function createTask(input2) {
5540
5665
  const result = await createTaskCore(input2);
5541
5666
  if (!input2.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -5557,8 +5682,8 @@ async function updateTask(input2) {
5557
5682
  const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
5558
5683
  const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
5559
5684
  if (input2.status === "in_progress") {
5560
- mkdirSync6(cacheDir, { recursive: true });
5561
- writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
5685
+ mkdirSync7(cacheDir, { recursive: true });
5686
+ writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
5562
5687
  } else if (input2.status === "done" || input2.status === "blocked" || input2.status === "cancelled") {
5563
5688
  try {
5564
5689
  unlinkSync6(cachePath);
@@ -5725,15 +5850,15 @@ __export(worker_gate_exports, {
5725
5850
  tryAcquireBackfillLock: () => tryAcquireBackfillLock,
5726
5851
  tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
5727
5852
  });
5728
- import { readdirSync as readdirSync5, writeFileSync as writeFileSync7, unlinkSync as unlinkSync7, mkdirSync as mkdirSync7, existsSync as existsSync15 } from "fs";
5853
+ import { readdirSync as readdirSync4, writeFileSync as writeFileSync8, unlinkSync as unlinkSync7, mkdirSync as mkdirSync8, existsSync as existsSync15 } from "fs";
5729
5854
  import path18 from "path";
5730
5855
  function tryAcquireWorkerSlot() {
5731
5856
  try {
5732
- mkdirSync7(WORKER_PID_DIR, { recursive: true });
5857
+ mkdirSync8(WORKER_PID_DIR, { recursive: true });
5733
5858
  const reservationId = `res-${process.pid}-${Date.now()}`;
5734
5859
  const reservationPath = path18.join(WORKER_PID_DIR, `${reservationId}.pid`);
5735
- writeFileSync7(reservationPath, String(process.pid));
5736
- const files = readdirSync5(WORKER_PID_DIR);
5860
+ writeFileSync8(reservationPath, String(process.pid));
5861
+ const files = readdirSync4(WORKER_PID_DIR);
5737
5862
  let alive = 0;
5738
5863
  for (const f of files) {
5739
5864
  if (!f.endsWith(".pid")) continue;
@@ -5772,8 +5897,8 @@ function tryAcquireWorkerSlot() {
5772
5897
  }
5773
5898
  function registerWorkerPid(pid) {
5774
5899
  try {
5775
- mkdirSync7(WORKER_PID_DIR, { recursive: true });
5776
- writeFileSync7(path18.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
5900
+ mkdirSync8(WORKER_PID_DIR, { recursive: true });
5901
+ writeFileSync8(path18.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
5777
5902
  } catch {
5778
5903
  }
5779
5904
  }
@@ -5785,7 +5910,7 @@ function cleanupWorkerPid() {
5785
5910
  }
5786
5911
  function tryAcquireBackfillLock() {
5787
5912
  try {
5788
- mkdirSync7(WORKER_PID_DIR, { recursive: true });
5913
+ mkdirSync8(WORKER_PID_DIR, { recursive: true });
5789
5914
  if (existsSync15(BACKFILL_LOCK)) {
5790
5915
  try {
5791
5916
  const pid = parseInt(
@@ -5802,7 +5927,7 @@ function tryAcquireBackfillLock() {
5802
5927
  } catch {
5803
5928
  }
5804
5929
  }
5805
- writeFileSync7(BACKFILL_LOCK, String(process.pid));
5930
+ writeFileSync8(BACKFILL_LOCK, String(process.pid));
5806
5931
  return true;
5807
5932
  } catch {
5808
5933
  return true;
@@ -5827,8 +5952,8 @@ var init_worker_gate = __esm({
5827
5952
 
5828
5953
  // src/adapters/claude/hooks/ingest-worker.ts
5829
5954
  import crypto7 from "crypto";
5830
- import { execSync as execSync8 } from "child_process";
5831
- import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "fs";
5955
+ import { execSync as execSync7 } from "child_process";
5956
+ import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
5832
5957
  import path19 from "path";
5833
5958
 
5834
5959
  // src/lib/error-detector.ts
@@ -5926,10 +6051,6 @@ function detectError(data) {
5926
6051
  }
5927
6052
 
5928
6053
  // src/lib/task-scanner.ts
5929
- import { readdirSync, readFileSync, existsSync, statSync } from "fs";
5930
- import { execSync } from "child_process";
5931
- import path from "path";
5932
- import os from "os";
5933
6054
  var STATUS_RE = /^\*\*Status:\*\*\s*(\w+)/m;
5934
6055
  var TITLE_RE = /^# (.+)/m;
5935
6056
 
@@ -5943,16 +6064,16 @@ import { createHash } from "crypto";
5943
6064
 
5944
6065
  // src/lib/keychain.ts
5945
6066
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
5946
- import { existsSync as existsSync5 } from "fs";
5947
- import path6 from "path";
5948
- import os4 from "os";
6067
+ import { existsSync as existsSync4 } from "fs";
6068
+ import path5 from "path";
6069
+ import os3 from "os";
5949
6070
  var SERVICE = "exe-mem";
5950
6071
  var ACCOUNT = "master-key";
5951
6072
  function getKeyDir() {
5952
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os4.homedir(), ".exe-os");
6073
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path5.join(os3.homedir(), ".exe-os");
5953
6074
  }
5954
6075
  function getKeyPath() {
5955
- return path6.join(getKeyDir(), "master.key");
6076
+ return path5.join(getKeyDir(), "master.key");
5956
6077
  }
5957
6078
  async function tryKeytar() {
5958
6079
  try {
@@ -5973,9 +6094,9 @@ async function getMasterKey() {
5973
6094
  }
5974
6095
  }
5975
6096
  const keyPath = getKeyPath();
5976
- if (!existsSync5(keyPath)) {
6097
+ if (!existsSync4(keyPath)) {
5977
6098
  process.stderr.write(
5978
- `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
6099
+ `[keychain] Key not found at ${keyPath} (HOME=${os3.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
5979
6100
  `
5980
6101
  );
5981
6102
  return null;
@@ -6631,7 +6752,7 @@ process.stdin.on("end", async () => {
6631
6752
  try {
6632
6753
  const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
6633
6754
  const flagPath = path19.join(exeDir, "session-cache", "needs-backfill");
6634
- writeFileSync8(flagPath, "1");
6755
+ writeFileSync9(flagPath, "1");
6635
6756
  } catch (err) {
6636
6757
  process.stderr.write(`[ingest-worker] backfill flag write failed: ${err instanceof Error ? err.message : String(err)}
6637
6758
  `);
@@ -6659,9 +6780,9 @@ process.stdin.on("end", async () => {
6659
6780
  if (status === "done") {
6660
6781
  const cwd = data.cwd ?? process.cwd();
6661
6782
  try {
6662
- execSync8("git add -u", { cwd, timeout: 1e4, stdio: "ignore" });
6783
+ execSync7("git add -u", { cwd, timeout: 1e4, stdio: "ignore" });
6663
6784
  const msg = `task(${agentId}): ${title}`;
6664
- execSync8(`git commit --no-verify -m ${JSON.stringify(msg)}`, { cwd, timeout: 3e4, stdio: "ignore" });
6785
+ execSync7(`git commit --no-verify -m ${JSON.stringify(msg)}`, { cwd, timeout: 3e4, stdio: "ignore" });
6665
6786
  } catch (err) {
6666
6787
  process.stderr.write(`[ingest-worker] auto-commit failed: ${err instanceof Error ? err.message : String(err)}
6667
6788
  `);
@@ -6742,8 +6863,8 @@ process.stdin.on("end", async () => {
6742
6863
  }
6743
6864
  const cwd = data.cwd ?? process.cwd();
6744
6865
  try {
6745
- mkdirSync8(path19.join(cwd, "exe/output"), { recursive: true });
6746
- mkdirSync8(path19.join(cwd, "exe/research"), { recursive: true });
6866
+ mkdirSync9(path19.join(cwd, "exe/output"), { recursive: true });
6867
+ mkdirSync9(path19.join(cwd, "exe/research"), { recursive: true });
6747
6868
  const { ensureGitignoreExe: ensureGitignoreExe2 } = await Promise.resolve().then(() => (init_tasks(), tasks_exports));
6748
6869
  await ensureGitignoreExe2(cwd);
6749
6870
  } catch (err) {