@askexenow/exe-os 0.9.113 → 0.9.115

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 (86) hide show
  1. package/dist/bin/agentic-ontology-backfill.js +36 -12
  2. package/dist/bin/agentic-reflection-backfill.js +36 -12
  3. package/dist/bin/agentic-semantic-label.js +36 -12
  4. package/dist/bin/backfill-conversations.js +36 -12
  5. package/dist/bin/backfill-responses.js +36 -12
  6. package/dist/bin/backfill-vectors.js +36 -12
  7. package/dist/bin/bulk-sync-postgres.js +36 -12
  8. package/dist/bin/cleanup-stale-review-tasks.js +470 -113
  9. package/dist/bin/cli.js +413 -62
  10. package/dist/bin/exe-agent.js +27 -0
  11. package/dist/bin/exe-assign.js +36 -12
  12. package/dist/bin/exe-boot.js +246 -54
  13. package/dist/bin/exe-call.js +8 -0
  14. package/dist/bin/exe-cloud.js +47 -12
  15. package/dist/bin/exe-dispatch.js +348 -53
  16. package/dist/bin/exe-doctor.js +51 -13
  17. package/dist/bin/exe-export-behaviors.js +37 -12
  18. package/dist/bin/exe-forget.js +36 -12
  19. package/dist/bin/exe-gateway.js +348 -53
  20. package/dist/bin/exe-heartbeat.js +471 -113
  21. package/dist/bin/exe-kill.js +36 -12
  22. package/dist/bin/exe-launch-agent.js +117 -18
  23. package/dist/bin/exe-new-employee.js +9 -1
  24. package/dist/bin/exe-pending-messages.js +452 -95
  25. package/dist/bin/exe-pending-notifications.js +452 -95
  26. package/dist/bin/exe-pending-reviews.js +452 -95
  27. package/dist/bin/exe-rename.js +36 -12
  28. package/dist/bin/exe-review.js +36 -12
  29. package/dist/bin/exe-search.js +37 -12
  30. package/dist/bin/exe-session-cleanup.js +348 -53
  31. package/dist/bin/exe-settings.js +12 -0
  32. package/dist/bin/exe-start-codex.js +46 -13
  33. package/dist/bin/exe-start-opencode.js +46 -13
  34. package/dist/bin/exe-status.js +460 -114
  35. package/dist/bin/exe-support.js +12 -0
  36. package/dist/bin/exe-team.js +36 -12
  37. package/dist/bin/git-sweep.js +348 -53
  38. package/dist/bin/graph-backfill.js +36 -12
  39. package/dist/bin/graph-export.js +36 -12
  40. package/dist/bin/install.js +9 -1
  41. package/dist/bin/intercom-check.js +255 -53
  42. package/dist/bin/scan-tasks.js +348 -53
  43. package/dist/bin/setup.js +74 -12
  44. package/dist/bin/shard-migrate.js +36 -12
  45. package/dist/gateway/index.js +348 -53
  46. package/dist/hooks/bug-report-worker.js +348 -53
  47. package/dist/hooks/codex-stop-task-finalizer.js +308 -37
  48. package/dist/hooks/commit-complete.js +348 -53
  49. package/dist/hooks/error-recall.js +37 -12
  50. package/dist/hooks/ingest.js +363 -54
  51. package/dist/hooks/instructions-loaded.js +36 -12
  52. package/dist/hooks/notification.js +36 -12
  53. package/dist/hooks/post-compact.js +426 -72
  54. package/dist/hooks/post-tool-combined.js +501 -146
  55. package/dist/hooks/pre-compact.js +348 -53
  56. package/dist/hooks/pre-tool-use.js +92 -13
  57. package/dist/hooks/prompt-submit.js +348 -53
  58. package/dist/hooks/session-end.js +158 -53
  59. package/dist/hooks/session-start.js +66 -13
  60. package/dist/hooks/stop.js +420 -72
  61. package/dist/hooks/subagent-stop.js +419 -72
  62. package/dist/hooks/summary-worker.js +442 -121
  63. package/dist/index.js +375 -53
  64. package/dist/lib/agent-config.js +8 -0
  65. package/dist/lib/cloud-sync.js +35 -12
  66. package/dist/lib/config.js +13 -0
  67. package/dist/lib/consolidation.js +9 -1
  68. package/dist/lib/embedder.js +13 -0
  69. package/dist/lib/employees.js +8 -0
  70. package/dist/lib/exe-daemon.js +524 -60
  71. package/dist/lib/hybrid-search.js +37 -12
  72. package/dist/lib/keychain.js +25 -13
  73. package/dist/lib/messaging.js +395 -74
  74. package/dist/lib/schedules.js +36 -12
  75. package/dist/lib/skill-learning.js +21 -0
  76. package/dist/lib/store.js +36 -12
  77. package/dist/lib/tasks.js +324 -41
  78. package/dist/lib/tmux-routing.js +324 -41
  79. package/dist/mcp/server.js +374 -54
  80. package/dist/mcp/tools/create-task.js +324 -41
  81. package/dist/mcp/tools/list-tasks.js +406 -57
  82. package/dist/mcp/tools/send-message.js +395 -74
  83. package/dist/mcp/tools/update-task.js +324 -41
  84. package/dist/runtime/index.js +375 -53
  85. package/dist/tui/App.js +377 -55
  86. package/package.json +1 -1
@@ -195,6 +195,17 @@ function normalizeOrchestration(raw) {
195
195
  const userOrg = raw.orchestration ?? {};
196
196
  raw.orchestration = { ...defaultOrg, ...userOrg };
197
197
  }
198
+ function normalizeCloudEndpoint(raw) {
199
+ const cloud = raw.cloud;
200
+ if (!cloud?.endpoint) return;
201
+ const ep = String(cloud.endpoint);
202
+ if (ep === "https://askexe.com/cloud" || ep === "https://askexe.com/cloud/") {
203
+ cloud.endpoint = "https://cloud.askexe.com";
204
+ process.stderr.write(
205
+ "[config] Auto-migrated cloud endpoint: askexe.com/cloud \u2192 cloud.askexe.com\n"
206
+ );
207
+ }
208
+ }
198
209
  async function loadConfig() {
199
210
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
200
211
  await ensurePrivateDir(dir);
@@ -220,6 +231,7 @@ async function loadConfig() {
220
231
  normalizeSessionLifecycle(migratedCfg);
221
232
  normalizeAutoUpdate(migratedCfg);
222
233
  normalizeOrchestration(migratedCfg);
234
+ normalizeCloudEndpoint(migratedCfg);
223
235
  const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
224
236
  if (config.dbPath.startsWith("~")) {
225
237
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
@@ -248,6 +260,7 @@ function loadConfigSync() {
248
260
  normalizeSessionLifecycle(migratedCfg);
249
261
  normalizeAutoUpdate(migratedCfg);
250
262
  normalizeOrchestration(migratedCfg);
263
+ normalizeCloudEndpoint(migratedCfg);
251
264
  const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
252
265
  if (config.dbPath.startsWith("~")) {
253
266
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
@@ -349,11 +362,176 @@ var init_config = __esm({
349
362
  }
350
363
  });
351
364
 
365
+ // src/lib/runtime-table.ts
366
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
367
+ var init_runtime_table = __esm({
368
+ "src/lib/runtime-table.ts"() {
369
+ "use strict";
370
+ RUNTIME_TABLE = {
371
+ codex: {
372
+ binary: "codex",
373
+ launchMode: "interactive",
374
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
375
+ inlineFlag: "--no-alt-screen",
376
+ apiKeyEnv: "OPENAI_API_KEY",
377
+ defaultModel: "gpt-5.5"
378
+ },
379
+ opencode: {
380
+ binary: "opencode",
381
+ launchMode: "exec",
382
+ autoApproveFlag: "--dangerously-skip-permissions",
383
+ inlineFlag: "",
384
+ apiKeyEnv: "ANTHROPIC_API_KEY",
385
+ defaultModel: "anthropic/claude-sonnet-4-6"
386
+ }
387
+ };
388
+ DEFAULT_RUNTIME = "claude";
389
+ }
390
+ });
391
+
392
+ // src/lib/agent-config.ts
393
+ var agent_config_exports = {};
394
+ __export(agent_config_exports, {
395
+ AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
396
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
397
+ KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
398
+ RUNTIME_LABELS: () => RUNTIME_LABELS,
399
+ clearAgentRuntime: () => clearAgentRuntime,
400
+ getAgentRuntime: () => getAgentRuntime,
401
+ loadAgentConfig: () => loadAgentConfig,
402
+ normalizeCcModelName: () => normalizeCcModelName,
403
+ saveAgentConfig: () => saveAgentConfig,
404
+ setAgentMcps: () => setAgentMcps,
405
+ setAgentRuntime: () => setAgentRuntime
406
+ });
407
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
408
+ import path2 from "path";
409
+ function loadAgentConfig() {
410
+ if (!existsSync3(AGENT_CONFIG_PATH)) return {};
411
+ try {
412
+ return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
413
+ } catch {
414
+ return {};
415
+ }
416
+ }
417
+ function saveAgentConfig(config) {
418
+ const dir = path2.dirname(AGENT_CONFIG_PATH);
419
+ ensurePrivateDirSync(dir);
420
+ writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
421
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
422
+ }
423
+ function getAgentRuntime(agentId) {
424
+ const config = loadAgentConfig();
425
+ const entry = config[agentId];
426
+ if (entry) return entry;
427
+ const orgDefault = config["default"];
428
+ if (orgDefault) return orgDefault;
429
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
430
+ }
431
+ function normalizeCcModelName(model) {
432
+ let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
433
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
434
+ ccModel += "[1m]";
435
+ }
436
+ return ccModel;
437
+ }
438
+ function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
439
+ const knownModels = KNOWN_RUNTIMES[runtime];
440
+ if (!knownModels) {
441
+ return {
442
+ ok: false,
443
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
444
+ };
445
+ }
446
+ if (!knownModels.includes(model)) {
447
+ return {
448
+ ok: false,
449
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
450
+ };
451
+ }
452
+ const config = loadAgentConfig();
453
+ const existing = config[agentId];
454
+ const entry = { runtime, model };
455
+ if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
456
+ if (mcps !== void 0) {
457
+ entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
458
+ } else if (existing?.mcps) {
459
+ entry.mcps = existing.mcps;
460
+ }
461
+ config[agentId] = entry;
462
+ saveAgentConfig(config);
463
+ return { ok: true };
464
+ }
465
+ function setAgentMcps(agentId, mcps) {
466
+ const config = loadAgentConfig();
467
+ const existing = config[agentId] ?? getAgentRuntime(agentId);
468
+ existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
469
+ config[agentId] = existing;
470
+ saveAgentConfig(config);
471
+ return { ok: true };
472
+ }
473
+ function clearAgentRuntime(agentId) {
474
+ const config = loadAgentConfig();
475
+ delete config[agentId];
476
+ saveAgentConfig(config);
477
+ }
478
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
479
+ var init_agent_config = __esm({
480
+ "src/lib/agent-config.ts"() {
481
+ "use strict";
482
+ init_config();
483
+ init_runtime_table();
484
+ init_secure_files();
485
+ AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
486
+ KNOWN_RUNTIMES = {
487
+ claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
488
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
489
+ opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
490
+ };
491
+ RUNTIME_LABELS = {
492
+ claude: "Claude Code (Anthropic)",
493
+ codex: "Codex (OpenAI)",
494
+ opencode: "OpenCode (open source)"
495
+ };
496
+ DEFAULT_MODELS = {
497
+ claude: "claude-opus-4.6",
498
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
499
+ opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
500
+ };
501
+ }
502
+ });
503
+
352
504
  // src/lib/employees.ts
505
+ var employees_exports = {};
506
+ __export(employees_exports, {
507
+ COORDINATOR_ROLE: () => COORDINATOR_ROLE,
508
+ DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
509
+ EMPLOYEES_PATH: () => EMPLOYEES_PATH,
510
+ addEmployee: () => addEmployee,
511
+ baseAgentName: () => baseAgentName,
512
+ canCoordinate: () => canCoordinate,
513
+ getCoordinatorEmployee: () => getCoordinatorEmployee,
514
+ getCoordinatorName: () => getCoordinatorName,
515
+ getEmployee: () => getEmployee,
516
+ getEmployeeByRole: () => getEmployeeByRole,
517
+ getEmployeeNamesByRole: () => getEmployeeNamesByRole,
518
+ hasRole: () => hasRole,
519
+ hireEmployee: () => hireEmployee,
520
+ isCoordinatorName: () => isCoordinatorName,
521
+ isCoordinatorRole: () => isCoordinatorRole,
522
+ isMultiInstance: () => isMultiInstance,
523
+ loadEmployees: () => loadEmployees,
524
+ loadEmployeesSync: () => loadEmployeesSync,
525
+ normalizeRole: () => normalizeRole,
526
+ normalizeRosterCase: () => normalizeRosterCase,
527
+ registerBinSymlinks: () => registerBinSymlinks,
528
+ saveEmployees: () => saveEmployees,
529
+ validateEmployeeName: () => validateEmployeeName
530
+ });
353
531
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
354
- import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
532
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
355
533
  import { execSync } from "child_process";
356
- import path2 from "path";
534
+ import path3 from "path";
357
535
  import os2 from "os";
358
536
  function normalizeRole(role) {
359
537
  return (role ?? "").trim().toLowerCase();
@@ -367,29 +545,222 @@ function getCoordinatorEmployee(employees) {
367
545
  function getCoordinatorName(employees = loadEmployeesSync()) {
368
546
  return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
369
547
  }
548
+ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
549
+ if (!agentName) return false;
550
+ return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
551
+ }
552
+ function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
553
+ return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
554
+ }
555
+ function validateEmployeeName(name) {
556
+ if (!name) {
557
+ return { valid: false, error: "Name is required" };
558
+ }
559
+ if (name.length > 32) {
560
+ return { valid: false, error: "Name must be 32 characters or fewer" };
561
+ }
562
+ if (!/^[a-z][a-z0-9]*$/.test(name)) {
563
+ return {
564
+ valid: false,
565
+ error: "Name must start with a letter and contain only lowercase alphanumeric characters"
566
+ };
567
+ }
568
+ return { valid: true };
569
+ }
570
+ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
571
+ if (!existsSync4(employeesPath)) {
572
+ return [];
573
+ }
574
+ const raw = await readFile2(employeesPath, "utf-8");
575
+ try {
576
+ return JSON.parse(raw);
577
+ } catch {
578
+ return [];
579
+ }
580
+ }
581
+ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
582
+ await mkdir2(path3.dirname(employeesPath), { recursive: true });
583
+ await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
584
+ }
370
585
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
371
- if (!existsSync3(employeesPath)) return [];
586
+ if (!existsSync4(employeesPath)) return [];
372
587
  try {
373
- return JSON.parse(readFileSync2(employeesPath, "utf-8"));
588
+ return JSON.parse(readFileSync3(employeesPath, "utf-8"));
374
589
  } catch {
375
590
  return [];
376
591
  }
377
592
  }
378
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
593
+ function getEmployee(employees, name) {
594
+ return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
595
+ }
596
+ function getEmployeeByRole(employees, role) {
597
+ const lower = role.toLowerCase();
598
+ return employees.find((e) => e.role.toLowerCase() === lower);
599
+ }
600
+ function getEmployeeNamesByRole(employees, role) {
601
+ const lower = role.toLowerCase();
602
+ return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
603
+ }
604
+ function hasRole(agentName, role) {
605
+ const employees = loadEmployeesSync();
606
+ const emp = getEmployee(employees, agentName);
607
+ return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
608
+ }
609
+ function baseAgentName(name, employees) {
610
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
611
+ if (!match) return name;
612
+ const base = match[1];
613
+ const roster = employees ?? loadEmployeesSync();
614
+ if (getEmployee(roster, base)) return base;
615
+ return name;
616
+ }
617
+ function isMultiInstance(agentName, employees) {
618
+ const roster = employees ?? loadEmployeesSync();
619
+ const emp = getEmployee(roster, agentName);
620
+ if (!emp) return false;
621
+ return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
622
+ }
623
+ function addEmployee(employees, employee) {
624
+ const { systemPrompt: _legacyPrompt, ...rest } = employee;
625
+ const normalized = { ...rest, name: employee.name.toLowerCase() };
626
+ if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
627
+ throw new Error(`Employee '${normalized.name}' already exists`);
628
+ }
629
+ return [...employees, normalized];
630
+ }
631
+ function appendToCoordinatorTeam(employee) {
632
+ const coordinator = getCoordinatorEmployee(loadEmployeesSync());
633
+ if (!coordinator) return;
634
+ const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
635
+ if (!existsSync4(idPath)) return;
636
+ const content = readFileSync3(idPath, "utf-8");
637
+ if (content.includes(`**${capitalize(employee.name)}`)) return;
638
+ const teamMatch = content.match(TEAM_SECTION_RE);
639
+ if (!teamMatch || teamMatch.index === void 0) return;
640
+ const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
641
+ const nextHeading = afterTeam.match(/\n## /);
642
+ const entry = `
643
+ **${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
644
+ `;
645
+ let updated;
646
+ if (nextHeading && nextHeading.index !== void 0) {
647
+ const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
648
+ updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
649
+ } else {
650
+ updated = content.trimEnd() + "\n" + entry;
651
+ }
652
+ writeFileSync2(idPath, updated, "utf-8");
653
+ }
654
+ function capitalize(s) {
655
+ return s.charAt(0).toUpperCase() + s.slice(1);
656
+ }
657
+ async function hireEmployee(employee) {
658
+ const employees = await loadEmployees();
659
+ const updated = addEmployee(employees, employee);
660
+ await saveEmployees(updated);
661
+ try {
662
+ appendToCoordinatorTeam(employee);
663
+ } catch {
664
+ }
665
+ try {
666
+ const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
667
+ const config = loadAgentConfig2();
668
+ const name = employee.name.toLowerCase();
669
+ if (!config[name] && config["default"]) {
670
+ config[name] = { ...config["default"] };
671
+ saveAgentConfig2(config);
672
+ }
673
+ } catch {
674
+ }
675
+ return updated;
676
+ }
677
+ async function normalizeRosterCase(rosterPath) {
678
+ const employees = await loadEmployees(rosterPath);
679
+ let changed = false;
680
+ for (const emp of employees) {
681
+ if (emp.name !== emp.name.toLowerCase()) {
682
+ const oldName = emp.name;
683
+ emp.name = emp.name.toLowerCase();
684
+ changed = true;
685
+ try {
686
+ const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
687
+ const oldPath = path3.join(identityDir, `${oldName}.md`);
688
+ const newPath = path3.join(identityDir, `${emp.name}.md`);
689
+ if (existsSync4(oldPath) && !existsSync4(newPath)) {
690
+ renameSync2(oldPath, newPath);
691
+ } else if (existsSync4(oldPath) && oldPath !== newPath) {
692
+ const content = readFileSync3(oldPath, "utf-8");
693
+ writeFileSync2(newPath, content, "utf-8");
694
+ if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
695
+ unlinkSync(oldPath);
696
+ }
697
+ }
698
+ } catch {
699
+ }
700
+ }
701
+ }
702
+ if (changed) {
703
+ await saveEmployees(employees, rosterPath);
704
+ }
705
+ return changed;
706
+ }
707
+ function findExeBin() {
708
+ try {
709
+ return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
710
+ } catch {
711
+ return null;
712
+ }
713
+ }
714
+ function registerBinSymlinks(name) {
715
+ const created = [];
716
+ const skipped = [];
717
+ const errors = [];
718
+ const exeBinPath = findExeBin();
719
+ if (!exeBinPath) {
720
+ errors.push("Could not find 'exe-os' in PATH");
721
+ return { created, skipped, errors };
722
+ }
723
+ const binDir = path3.dirname(exeBinPath);
724
+ let target;
725
+ try {
726
+ target = readlinkSync(exeBinPath);
727
+ } catch {
728
+ errors.push("Could not read 'exe' symlink");
729
+ return { created, skipped, errors };
730
+ }
731
+ for (const suffix of ["", "-opencode"]) {
732
+ const linkName = `${name}${suffix}`;
733
+ const linkPath = path3.join(binDir, linkName);
734
+ if (existsSync4(linkPath)) {
735
+ skipped.push(linkName);
736
+ continue;
737
+ }
738
+ try {
739
+ symlinkSync(target, linkPath);
740
+ created.push(linkName);
741
+ } catch (err) {
742
+ errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
743
+ }
744
+ }
745
+ return { created, skipped, errors };
746
+ }
747
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
379
748
  var init_employees = __esm({
380
749
  "src/lib/employees.ts"() {
381
750
  "use strict";
382
751
  init_config();
383
- EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
752
+ EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
384
753
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
385
754
  COORDINATOR_ROLE = "COO";
386
- IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
755
+ MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
756
+ IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
757
+ TEAM_SECTION_RE = /^## Team\b.*$/m;
387
758
  }
388
759
  });
389
760
 
390
761
  // src/lib/database-adapter.ts
391
762
  import os3 from "os";
392
- import path3 from "path";
763
+ import path4 from "path";
393
764
  import { createRequire } from "module";
394
765
  import { pathToFileURL } from "url";
395
766
  function quotedIdentifier(identifier) {
@@ -700,8 +1071,8 @@ async function loadPrismaClient() {
700
1071
  }
701
1072
  return new PrismaClient2();
702
1073
  }
703
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
704
- const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
1074
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
1075
+ const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
705
1076
  const prismaEntry = requireFromExeDb.resolve("@prisma/client");
706
1077
  const module = await import(pathToFileURL(prismaEntry).href);
707
1078
  const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
@@ -982,8 +1353,8 @@ var init_memory = __esm({
982
1353
 
983
1354
  // src/lib/daemon-auth.ts
984
1355
  import crypto from "crypto";
985
- import path4 from "path";
986
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
1356
+ import path5 from "path";
1357
+ import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
987
1358
  function normalizeToken(token) {
988
1359
  if (!token) return null;
989
1360
  const trimmed = token.trim();
@@ -991,8 +1362,8 @@ function normalizeToken(token) {
991
1362
  }
992
1363
  function readDaemonToken() {
993
1364
  try {
994
- if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
995
- return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
1365
+ if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
1366
+ return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
996
1367
  } catch {
997
1368
  return null;
998
1369
  }
@@ -1002,7 +1373,7 @@ function ensureDaemonToken(seed) {
1002
1373
  if (existing) return existing;
1003
1374
  const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
1004
1375
  ensurePrivateDirSync(EXE_AI_DIR);
1005
- writeFileSync2(DAEMON_TOKEN_PATH, `${token}
1376
+ writeFileSync3(DAEMON_TOKEN_PATH, `${token}
1006
1377
  `, "utf8");
1007
1378
  enforcePrivateFileSync(DAEMON_TOKEN_PATH);
1008
1379
  return token;
@@ -1013,7 +1384,7 @@ var init_daemon_auth = __esm({
1013
1384
  "use strict";
1014
1385
  init_config();
1015
1386
  init_secure_files();
1016
- DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
1387
+ DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
1017
1388
  }
1018
1389
  });
1019
1390
 
@@ -1033,8 +1404,8 @@ import net from "net";
1033
1404
  import os4 from "os";
1034
1405
  import { spawn, execSync as execSync2 } from "child_process";
1035
1406
  import { randomUUID } from "crypto";
1036
- import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
1037
- import path5 from "path";
1407
+ import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
1408
+ import path6 from "path";
1038
1409
  import { fileURLToPath } from "url";
1039
1410
  function handleData(chunk) {
1040
1411
  _buffer += chunk.toString();
@@ -1070,9 +1441,9 @@ function isZombie(pid) {
1070
1441
  }
1071
1442
  }
1072
1443
  function cleanupStaleFiles() {
1073
- if (existsSync5(PID_PATH)) {
1444
+ if (existsSync6(PID_PATH)) {
1074
1445
  try {
1075
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1446
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1076
1447
  if (pid > 0) {
1077
1448
  try {
1078
1449
  process.kill(pid, 0);
@@ -1097,11 +1468,11 @@ function cleanupStaleFiles() {
1097
1468
  }
1098
1469
  }
1099
1470
  function findPackageRoot() {
1100
- let dir = path5.dirname(fileURLToPath(import.meta.url));
1101
- const { root } = path5.parse(dir);
1471
+ let dir = path6.dirname(fileURLToPath(import.meta.url));
1472
+ const { root } = path6.parse(dir);
1102
1473
  while (dir !== root) {
1103
- if (existsSync5(path5.join(dir, "package.json"))) return dir;
1104
- dir = path5.dirname(dir);
1474
+ if (existsSync6(path6.join(dir, "package.json"))) return dir;
1475
+ dir = path6.dirname(dir);
1105
1476
  }
1106
1477
  return null;
1107
1478
  }
@@ -1119,8 +1490,8 @@ function spawnDaemon() {
1119
1490
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
1120
1491
  return;
1121
1492
  }
1122
- const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1123
- if (!existsSync5(daemonPath)) {
1493
+ const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1494
+ if (!existsSync6(daemonPath)) {
1124
1495
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1125
1496
  `);
1126
1497
  return;
@@ -1129,7 +1500,7 @@ function spawnDaemon() {
1129
1500
  const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
1130
1501
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1131
1502
  `);
1132
- const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
1503
+ const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
1133
1504
  let stderrFd = "ignore";
1134
1505
  try {
1135
1506
  stderrFd = openSync(logPath, "a");
@@ -1294,9 +1665,9 @@ function killAndRespawnDaemon() {
1294
1665
  }
1295
1666
  try {
1296
1667
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
1297
- if (existsSync5(PID_PATH)) {
1668
+ if (existsSync6(PID_PATH)) {
1298
1669
  try {
1299
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1670
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1300
1671
  if (pid > 0) {
1301
1672
  try {
1302
1673
  process.kill(pid, "SIGKILL");
@@ -1442,9 +1813,9 @@ var init_exe_daemon_client = __esm({
1442
1813
  "use strict";
1443
1814
  init_config();
1444
1815
  init_daemon_auth();
1445
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
1446
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
1447
- SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
1816
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
1817
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
1818
+ SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
1448
1819
  SPAWN_LOCK_STALE_MS = 3e4;
1449
1820
  CONNECT_TIMEOUT_MS = 15e3;
1450
1821
  REQUEST_TIMEOUT_MS = 3e4;
@@ -1712,7 +2083,7 @@ __export(database_exports, {
1712
2083
  isInitialized: () => isInitialized,
1713
2084
  setExternalClient: () => setExternalClient
1714
2085
  });
1715
- import { chmodSync as chmodSync2, existsSync as existsSync6, statSync as statSync2, copyFileSync, unlinkSync as unlinkSync3, openSync as openSync2, closeSync as closeSync2, mkdirSync as mkdirSync2 } from "fs";
2086
+ import { chmodSync as chmodSync2, existsSync as existsSync7, statSync as statSync2, copyFileSync, unlinkSync as unlinkSync3, openSync as openSync2, closeSync as closeSync2, mkdirSync as mkdirSync2 } from "fs";
1716
2087
  import { createClient } from "@libsql/client";
1717
2088
  import { homedir } from "os";
1718
2089
  import { join } from "path";
@@ -1765,11 +2136,11 @@ function releaseDbLock() {
1765
2136
  }
1766
2137
  async function initDatabase(config) {
1767
2138
  acquireDbLock();
1768
- if (existsSync6(config.dbPath)) {
2139
+ if (existsSync7(config.dbPath)) {
1769
2140
  const dbStat = statSync2(config.dbPath);
1770
2141
  if (dbStat.size === 0) {
1771
2142
  const walPath = config.dbPath + "-wal";
1772
- if (existsSync6(walPath) && statSync2(walPath).size > 0) {
2143
+ if (existsSync7(walPath) && statSync2(walPath).size > 0) {
1773
2144
  const backupPath = config.dbPath + ".zeroed-" + Date.now();
1774
2145
  copyFileSync(config.dbPath, backupPath);
1775
2146
  unlinkSync3(config.dbPath);
@@ -3355,16 +3726,16 @@ var init_database = __esm({
3355
3726
  });
3356
3727
 
3357
3728
  // src/lib/keychain.ts
3358
- import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3359
- import { existsSync as existsSync7, statSync as statSync3 } from "fs";
3729
+ import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
3730
+ import { existsSync as existsSync8, statSync as statSync3 } from "fs";
3360
3731
  import { execSync as execSync3 } from "child_process";
3361
- import path6 from "path";
3732
+ import path7 from "path";
3362
3733
  import os5 from "os";
3363
3734
  function getKeyDir() {
3364
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
3735
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
3365
3736
  }
3366
3737
  function getKeyPath() {
3367
- return path6.join(getKeyDir(), "master.key");
3738
+ return path7.join(getKeyDir(), "master.key");
3368
3739
  }
3369
3740
  function nativeKeychainAllowed() {
3370
3741
  return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
@@ -3390,12 +3761,14 @@ function linuxSecretAvailable() {
3390
3761
  function isRootOnlyTrustedServerKeyFile(keyPath) {
3391
3762
  if (process.platform !== "linux") return false;
3392
3763
  try {
3393
- const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
3394
3764
  const st = statSync3(keyPath);
3395
3765
  if (!st.isFile() || (st.mode & 63) !== 0) return false;
3766
+ const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
3396
3767
  if (uid === 0) return true;
3397
3768
  const exeOsDir = process.env.EXE_OS_DIR;
3398
- return Boolean(exeOsDir && path6.resolve(keyPath).startsWith(path6.resolve(exeOsDir) + path6.sep));
3769
+ if (exeOsDir && path7.resolve(keyPath).startsWith(path7.resolve(exeOsDir) + path7.sep)) return true;
3770
+ if (!linuxSecretAvailable()) return true;
3771
+ return false;
3399
3772
  } catch {
3400
3773
  return false;
3401
3774
  }
@@ -3545,15 +3918,25 @@ async function writeMachineBoundFileFallback(b64) {
3545
3918
  await mkdir3(dir, { recursive: true });
3546
3919
  const keyPath = getKeyPath();
3547
3920
  const machineKey = deriveMachineKey();
3548
- if (machineKey) {
3549
- const encrypted = encryptWithMachineKey(b64, machineKey);
3550
- await writeFile3(keyPath, encrypted + "\n", "utf-8");
3551
- await chmod2(keyPath, 384);
3552
- return "encrypted";
3921
+ const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
3922
+ const result = machineKey ? "encrypted" : "plaintext";
3923
+ const tmpPath = keyPath + ".tmp";
3924
+ try {
3925
+ if (existsSync8(keyPath)) {
3926
+ await copyFile(keyPath, keyPath + ".bak").catch(() => {
3927
+ });
3928
+ }
3929
+ await writeFile3(tmpPath, content, "utf-8");
3930
+ await chmod2(tmpPath, 384);
3931
+ await rename(tmpPath, keyPath);
3932
+ } catch (err) {
3933
+ try {
3934
+ await unlink(tmpPath);
3935
+ } catch {
3936
+ }
3937
+ throw err;
3553
3938
  }
3554
- await writeFile3(keyPath, b64 + "\n", "utf-8");
3555
- await chmod2(keyPath, 384);
3556
- return "plaintext";
3939
+ return result;
3557
3940
  }
3558
3941
  async function getMasterKey() {
3559
3942
  let nativeValue = macKeychainGet() ?? linuxSecretGet();
@@ -3592,7 +3975,7 @@ async function getMasterKey() {
3592
3975
  }
3593
3976
  }
3594
3977
  const keyPath = getKeyPath();
3595
- if (!existsSync7(keyPath)) {
3978
+ if (!existsSync8(keyPath)) {
3596
3979
  process.stderr.write(
3597
3980
  `[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
3598
3981
  `
@@ -3620,7 +4003,7 @@ async function getMasterKey() {
3620
4003
  b64Value = content;
3621
4004
  }
3622
4005
  const key = Buffer.from(b64Value, "base64");
3623
- if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
4006
+ if (isRootOnlyTrustedServerKeyFile(keyPath)) {
3624
4007
  return key;
3625
4008
  }
3626
4009
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
@@ -3926,14 +4309,14 @@ __export(shard_manager_exports, {
3926
4309
  listShards: () => listShards,
3927
4310
  shardExists: () => shardExists
3928
4311
  });
3929
- import path7 from "path";
3930
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readdirSync, renameSync as renameSync3, statSync as statSync4 } from "fs";
4312
+ import path8 from "path";
4313
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, readdirSync, renameSync as renameSync3, statSync as statSync4 } from "fs";
3931
4314
  import { createClient as createClient2 } from "@libsql/client";
3932
4315
  function initShardManager(encryptionKey) {
3933
4316
  _encryptionKey = encryptionKey;
3934
4317
  _keyValidated = false;
3935
4318
  _keyValidationPromise = null;
3936
- if (!existsSync8(SHARDS_DIR)) {
4319
+ if (!existsSync9(SHARDS_DIR)) {
3937
4320
  mkdirSync3(SHARDS_DIR, { recursive: true });
3938
4321
  }
3939
4322
  const existingShards = readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db"));
@@ -3954,7 +4337,7 @@ async function validateEncryptionKey() {
3954
4337
  return true;
3955
4338
  }
3956
4339
  for (const shardFile of existingShards.slice(0, 3)) {
3957
- const dbPath = path7.join(SHARDS_DIR, shardFile);
4340
+ const dbPath = path8.join(SHARDS_DIR, shardFile);
3958
4341
  const testClient = createClient2({ url: `file:${dbPath}`, encryptionKey: _encryptionKey });
3959
4342
  try {
3960
4343
  await testClient.execute("SELECT COUNT(*) FROM sqlite_schema");
@@ -3997,7 +4380,7 @@ function getShardClient(projectName) {
3997
4380
  while (_shards.size >= MAX_OPEN_SHARDS) {
3998
4381
  evictLRU();
3999
4382
  }
4000
- const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
4383
+ const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
4001
4384
  const client = createClient2({
4002
4385
  url: `file:${dbPath}`,
4003
4386
  encryptionKey: _encryptionKey
@@ -4008,13 +4391,13 @@ function getShardClient(projectName) {
4008
4391
  }
4009
4392
  function shardExists(projectName) {
4010
4393
  const safeName = safeShardName(projectName);
4011
- return existsSync8(path7.join(SHARDS_DIR, `${safeName}.db`));
4394
+ return existsSync9(path8.join(SHARDS_DIR, `${safeName}.db`));
4012
4395
  }
4013
4396
  function safeShardName(projectName) {
4014
4397
  return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
4015
4398
  }
4016
4399
  function listShards() {
4017
- if (!existsSync8(SHARDS_DIR)) return [];
4400
+ if (!existsSync9(SHARDS_DIR)) return [];
4018
4401
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
4019
4402
  }
4020
4403
  async function auditShardHealth(options = {}) {
@@ -4026,7 +4409,7 @@ async function auditShardHealth(options = {}) {
4026
4409
  const names = listShards();
4027
4410
  const shards = [];
4028
4411
  for (const name of names) {
4029
- const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
4412
+ const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
4030
4413
  const stat = statSync4(dbPath);
4031
4414
  const item = {
4032
4415
  name,
@@ -4061,7 +4444,7 @@ async function auditShardHealth(options = {}) {
4061
4444
  _shards.delete(name);
4062
4445
  _shardLastAccess.delete(name);
4063
4446
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4064
- const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
4447
+ const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
4065
4448
  renameSync3(dbPath, archivedPath);
4066
4449
  item.archivedPath = archivedPath;
4067
4450
  }
@@ -4289,11 +4672,11 @@ async function getReadyShardClient(projectName) {
4289
4672
  client.close();
4290
4673
  _shards.delete(safeName);
4291
4674
  _shardLastAccess.delete(safeName);
4292
- const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
4293
- if (existsSync8(dbPath)) {
4675
+ const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
4676
+ if (existsSync9(dbPath)) {
4294
4677
  const stat = statSync4(dbPath);
4295
4678
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4296
- const archivedPath = path7.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
4679
+ const archivedPath = path8.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
4297
4680
  renameSync3(dbPath, archivedPath);
4298
4681
  process.stderr.write(
4299
4682
  `[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
@@ -4361,7 +4744,7 @@ var init_shard_manager = __esm({
4361
4744
  "src/lib/shard-manager.ts"() {
4362
4745
  "use strict";
4363
4746
  init_config();
4364
- SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
4747
+ SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
4365
4748
  SHARD_IDLE_MS = 5 * 60 * 1e3;
4366
4749
  MAX_OPEN_SHARDS = 10;
4367
4750
  EVICTION_INTERVAL_MS = 60 * 1e3;
@@ -5855,13 +6238,13 @@ var init_store = __esm({
5855
6238
  });
5856
6239
 
5857
6240
  // src/lib/session-registry.ts
5858
- import path8 from "path";
6241
+ import path9 from "path";
5859
6242
  import os6 from "os";
5860
6243
  var REGISTRY_PATH;
5861
6244
  var init_session_registry = __esm({
5862
6245
  "src/lib/session-registry.ts"() {
5863
6246
  "use strict";
5864
- REGISTRY_PATH = path8.join(os6.homedir(), ".exe-os", "session-registry.json");
6247
+ REGISTRY_PATH = path9.join(os6.homedir(), ".exe-os", "session-registry.json");
5865
6248
  }
5866
6249
  });
5867
6250
 
@@ -6079,51 +6462,6 @@ var init_provider_table = __esm({
6079
6462
  }
6080
6463
  });
6081
6464
 
6082
- // src/lib/runtime-table.ts
6083
- var RUNTIME_TABLE;
6084
- var init_runtime_table = __esm({
6085
- "src/lib/runtime-table.ts"() {
6086
- "use strict";
6087
- RUNTIME_TABLE = {
6088
- codex: {
6089
- binary: "codex",
6090
- launchMode: "interactive",
6091
- autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
6092
- inlineFlag: "--no-alt-screen",
6093
- apiKeyEnv: "OPENAI_API_KEY",
6094
- defaultModel: "gpt-5.5"
6095
- },
6096
- opencode: {
6097
- binary: "opencode",
6098
- launchMode: "exec",
6099
- autoApproveFlag: "--dangerously-skip-permissions",
6100
- inlineFlag: "",
6101
- apiKeyEnv: "ANTHROPIC_API_KEY",
6102
- defaultModel: "anthropic/claude-sonnet-4-6"
6103
- }
6104
- };
6105
- }
6106
- });
6107
-
6108
- // src/lib/agent-config.ts
6109
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync9 } from "fs";
6110
- import path9 from "path";
6111
- var AGENT_CONFIG_PATH, DEFAULT_MODELS;
6112
- var init_agent_config = __esm({
6113
- "src/lib/agent-config.ts"() {
6114
- "use strict";
6115
- init_config();
6116
- init_runtime_table();
6117
- init_secure_files();
6118
- AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
6119
- DEFAULT_MODELS = {
6120
- claude: "claude-opus-4.6",
6121
- codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
6122
- opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
6123
- };
6124
- }
6125
- });
6126
-
6127
6465
  // src/lib/intercom-queue.ts
6128
6466
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync4, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
6129
6467
  import path10 from "path";
@@ -6204,6 +6542,21 @@ function isRootSession(name) {
6204
6542
  function extractRootExe(name) {
6205
6543
  if (!name) return null;
6206
6544
  if (!name.includes("-")) return name;
6545
+ try {
6546
+ const roster = (init_employees(), __toCommonJS(employees_exports)).loadEmployeesSync();
6547
+ if (roster.length > 0) {
6548
+ const sortedNames = roster.map((e) => e.name).sort((a, b) => b.length - a.length);
6549
+ for (const agentName of sortedNames) {
6550
+ const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6551
+ const regex = new RegExp(`^${escaped}\\d*-(.+)$`);
6552
+ const match = name.match(regex);
6553
+ if (match) {
6554
+ return extractRootExe(match[1]);
6555
+ }
6556
+ }
6557
+ }
6558
+ } catch {
6559
+ }
6207
6560
  const parts = name.split("-").filter(Boolean);
6208
6561
  return parts.length > 0 ? parts[parts.length - 1] : null;
6209
6562
  }
@@ -6222,6 +6575,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
6222
6575
  function getParentExe(sessionKey) {
6223
6576
  try {
6224
6577
  const data = JSON.parse(readFileSync9(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
6578
+ if (data.registeredAt) {
6579
+ const age = Date.now() - new Date(data.registeredAt).getTime();
6580
+ if (age > PARENT_EXE_CACHE_TTL_MS) return null;
6581
+ }
6225
6582
  return data.parentExe || null;
6226
6583
  } catch {
6227
6584
  return null;
@@ -6299,7 +6656,7 @@ function isExeSession(sessionName) {
6299
6656
  const coordinatorName = getCoordinatorName();
6300
6657
  return matchesBaseWithInstance(coordinatorName);
6301
6658
  }
6302
- var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
6659
+ var SPAWN_LOCK_DIR, SESSION_CACHE, PARENT_EXE_CACHE_TTL_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
6303
6660
  var init_tmux_routing = __esm({
6304
6661
  "src/lib/tmux-routing.ts"() {
6305
6662
  "use strict";
@@ -6317,6 +6674,7 @@ var init_tmux_routing = __esm({
6317
6674
  init_agent_symlinks();
6318
6675
  SPAWN_LOCK_DIR = path14.join(os10.homedir(), ".exe-os", "spawn-locks");
6319
6676
  SESSION_CACHE = path14.join(os10.homedir(), ".exe-os", "session-cache");
6677
+ PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
6320
6678
  INTERCOM_LOG2 = path14.join(os10.homedir(), ".exe-os", "intercom.log");
6321
6679
  DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
6322
6680
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;