@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
@@ -154,6 +154,17 @@ function normalizeOrchestration(raw) {
154
154
  const userOrg = raw.orchestration ?? {};
155
155
  raw.orchestration = { ...defaultOrg, ...userOrg };
156
156
  }
157
+ function normalizeCloudEndpoint(raw) {
158
+ const cloud = raw.cloud;
159
+ if (!cloud?.endpoint) return;
160
+ const ep = String(cloud.endpoint);
161
+ if (ep === "https://askexe.com/cloud" || ep === "https://askexe.com/cloud/") {
162
+ cloud.endpoint = "https://cloud.askexe.com";
163
+ process.stderr.write(
164
+ "[config] Auto-migrated cloud endpoint: askexe.com/cloud \u2192 cloud.askexe.com\n"
165
+ );
166
+ }
167
+ }
157
168
  async function loadConfig() {
158
169
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
159
170
  await ensurePrivateDir(dir);
@@ -179,6 +190,7 @@ async function loadConfig() {
179
190
  normalizeSessionLifecycle(migratedCfg);
180
191
  normalizeAutoUpdate(migratedCfg);
181
192
  normalizeOrchestration(migratedCfg);
193
+ normalizeCloudEndpoint(migratedCfg);
182
194
  const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
183
195
  if (config.dbPath.startsWith("~")) {
184
196
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
@@ -207,6 +219,7 @@ function loadConfigSync() {
207
219
  normalizeSessionLifecycle(migratedCfg);
208
220
  normalizeAutoUpdate(migratedCfg);
209
221
  normalizeOrchestration(migratedCfg);
222
+ normalizeCloudEndpoint(migratedCfg);
210
223
  const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
211
224
  if (config.dbPath.startsWith("~")) {
212
225
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
@@ -569,11 +582,176 @@ var init_db_retry = __esm({
569
582
  }
570
583
  });
571
584
 
585
+ // src/lib/runtime-table.ts
586
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
587
+ var init_runtime_table = __esm({
588
+ "src/lib/runtime-table.ts"() {
589
+ "use strict";
590
+ RUNTIME_TABLE = {
591
+ codex: {
592
+ binary: "codex",
593
+ launchMode: "interactive",
594
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
595
+ inlineFlag: "--no-alt-screen",
596
+ apiKeyEnv: "OPENAI_API_KEY",
597
+ defaultModel: "gpt-5.5"
598
+ },
599
+ opencode: {
600
+ binary: "opencode",
601
+ launchMode: "exec",
602
+ autoApproveFlag: "--dangerously-skip-permissions",
603
+ inlineFlag: "",
604
+ apiKeyEnv: "ANTHROPIC_API_KEY",
605
+ defaultModel: "anthropic/claude-sonnet-4-6"
606
+ }
607
+ };
608
+ DEFAULT_RUNTIME = "claude";
609
+ }
610
+ });
611
+
612
+ // src/lib/agent-config.ts
613
+ var agent_config_exports = {};
614
+ __export(agent_config_exports, {
615
+ AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
616
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
617
+ KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
618
+ RUNTIME_LABELS: () => RUNTIME_LABELS,
619
+ clearAgentRuntime: () => clearAgentRuntime,
620
+ getAgentRuntime: () => getAgentRuntime,
621
+ loadAgentConfig: () => loadAgentConfig,
622
+ normalizeCcModelName: () => normalizeCcModelName,
623
+ saveAgentConfig: () => saveAgentConfig,
624
+ setAgentMcps: () => setAgentMcps,
625
+ setAgentRuntime: () => setAgentRuntime
626
+ });
627
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
628
+ import path2 from "path";
629
+ function loadAgentConfig() {
630
+ if (!existsSync3(AGENT_CONFIG_PATH)) return {};
631
+ try {
632
+ return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
633
+ } catch {
634
+ return {};
635
+ }
636
+ }
637
+ function saveAgentConfig(config) {
638
+ const dir = path2.dirname(AGENT_CONFIG_PATH);
639
+ ensurePrivateDirSync(dir);
640
+ writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
641
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
642
+ }
643
+ function getAgentRuntime(agentId) {
644
+ const config = loadAgentConfig();
645
+ const entry = config[agentId];
646
+ if (entry) return entry;
647
+ const orgDefault = config["default"];
648
+ if (orgDefault) return orgDefault;
649
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
650
+ }
651
+ function normalizeCcModelName(model) {
652
+ let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
653
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
654
+ ccModel += "[1m]";
655
+ }
656
+ return ccModel;
657
+ }
658
+ function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
659
+ const knownModels = KNOWN_RUNTIMES[runtime];
660
+ if (!knownModels) {
661
+ return {
662
+ ok: false,
663
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
664
+ };
665
+ }
666
+ if (!knownModels.includes(model)) {
667
+ return {
668
+ ok: false,
669
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
670
+ };
671
+ }
672
+ const config = loadAgentConfig();
673
+ const existing = config[agentId];
674
+ const entry = { runtime, model };
675
+ if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
676
+ if (mcps !== void 0) {
677
+ entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
678
+ } else if (existing?.mcps) {
679
+ entry.mcps = existing.mcps;
680
+ }
681
+ config[agentId] = entry;
682
+ saveAgentConfig(config);
683
+ return { ok: true };
684
+ }
685
+ function setAgentMcps(agentId, mcps) {
686
+ const config = loadAgentConfig();
687
+ const existing = config[agentId] ?? getAgentRuntime(agentId);
688
+ existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
689
+ config[agentId] = existing;
690
+ saveAgentConfig(config);
691
+ return { ok: true };
692
+ }
693
+ function clearAgentRuntime(agentId) {
694
+ const config = loadAgentConfig();
695
+ delete config[agentId];
696
+ saveAgentConfig(config);
697
+ }
698
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
699
+ var init_agent_config = __esm({
700
+ "src/lib/agent-config.ts"() {
701
+ "use strict";
702
+ init_config();
703
+ init_runtime_table();
704
+ init_secure_files();
705
+ AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
706
+ KNOWN_RUNTIMES = {
707
+ claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
708
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
709
+ opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
710
+ };
711
+ RUNTIME_LABELS = {
712
+ claude: "Claude Code (Anthropic)",
713
+ codex: "Codex (OpenAI)",
714
+ opencode: "OpenCode (open source)"
715
+ };
716
+ DEFAULT_MODELS = {
717
+ claude: "claude-opus-4.6",
718
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
719
+ opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
720
+ };
721
+ }
722
+ });
723
+
572
724
  // src/lib/employees.ts
725
+ var employees_exports = {};
726
+ __export(employees_exports, {
727
+ COORDINATOR_ROLE: () => COORDINATOR_ROLE,
728
+ DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
729
+ EMPLOYEES_PATH: () => EMPLOYEES_PATH,
730
+ addEmployee: () => addEmployee,
731
+ baseAgentName: () => baseAgentName,
732
+ canCoordinate: () => canCoordinate,
733
+ getCoordinatorEmployee: () => getCoordinatorEmployee,
734
+ getCoordinatorName: () => getCoordinatorName,
735
+ getEmployee: () => getEmployee,
736
+ getEmployeeByRole: () => getEmployeeByRole,
737
+ getEmployeeNamesByRole: () => getEmployeeNamesByRole,
738
+ hasRole: () => hasRole,
739
+ hireEmployee: () => hireEmployee,
740
+ isCoordinatorName: () => isCoordinatorName,
741
+ isCoordinatorRole: () => isCoordinatorRole,
742
+ isMultiInstance: () => isMultiInstance,
743
+ loadEmployees: () => loadEmployees,
744
+ loadEmployeesSync: () => loadEmployeesSync,
745
+ normalizeRole: () => normalizeRole,
746
+ normalizeRosterCase: () => normalizeRosterCase,
747
+ registerBinSymlinks: () => registerBinSymlinks,
748
+ saveEmployees: () => saveEmployees,
749
+ validateEmployeeName: () => validateEmployeeName
750
+ });
573
751
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
574
- import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
752
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
575
753
  import { execSync } from "child_process";
576
- import path2 from "path";
754
+ import path3 from "path";
577
755
  import os2 from "os";
578
756
  function normalizeRole(role) {
579
757
  return (role ?? "").trim().toLowerCase();
@@ -587,10 +765,47 @@ function getCoordinatorEmployee(employees) {
587
765
  function getCoordinatorName(employees = loadEmployeesSync()) {
588
766
  return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
589
767
  }
768
+ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
769
+ if (!agentName) return false;
770
+ return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
771
+ }
772
+ function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
773
+ return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
774
+ }
775
+ function validateEmployeeName(name) {
776
+ if (!name) {
777
+ return { valid: false, error: "Name is required" };
778
+ }
779
+ if (name.length > 32) {
780
+ return { valid: false, error: "Name must be 32 characters or fewer" };
781
+ }
782
+ if (!/^[a-z][a-z0-9]*$/.test(name)) {
783
+ return {
784
+ valid: false,
785
+ error: "Name must start with a letter and contain only lowercase alphanumeric characters"
786
+ };
787
+ }
788
+ return { valid: true };
789
+ }
790
+ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
791
+ if (!existsSync4(employeesPath)) {
792
+ return [];
793
+ }
794
+ const raw = await readFile2(employeesPath, "utf-8");
795
+ try {
796
+ return JSON.parse(raw);
797
+ } catch {
798
+ return [];
799
+ }
800
+ }
801
+ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
802
+ await mkdir2(path3.dirname(employeesPath), { recursive: true });
803
+ await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
804
+ }
590
805
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
591
- if (!existsSync3(employeesPath)) return [];
806
+ if (!existsSync4(employeesPath)) return [];
592
807
  try {
593
- return JSON.parse(readFileSync2(employeesPath, "utf-8"));
808
+ return JSON.parse(readFileSync3(employeesPath, "utf-8"));
594
809
  } catch {
595
810
  return [];
596
811
  }
@@ -598,21 +813,174 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
598
813
  function getEmployee(employees, name) {
599
814
  return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
600
815
  }
601
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
816
+ function getEmployeeByRole(employees, role) {
817
+ const lower = role.toLowerCase();
818
+ return employees.find((e) => e.role.toLowerCase() === lower);
819
+ }
820
+ function getEmployeeNamesByRole(employees, role) {
821
+ const lower = role.toLowerCase();
822
+ return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
823
+ }
824
+ function hasRole(agentName, role) {
825
+ const employees = loadEmployeesSync();
826
+ const emp = getEmployee(employees, agentName);
827
+ return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
828
+ }
829
+ function baseAgentName(name, employees) {
830
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
831
+ if (!match) return name;
832
+ const base = match[1];
833
+ const roster = employees ?? loadEmployeesSync();
834
+ if (getEmployee(roster, base)) return base;
835
+ return name;
836
+ }
837
+ function isMultiInstance(agentName, employees) {
838
+ const roster = employees ?? loadEmployeesSync();
839
+ const emp = getEmployee(roster, agentName);
840
+ if (!emp) return false;
841
+ return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
842
+ }
843
+ function addEmployee(employees, employee) {
844
+ const { systemPrompt: _legacyPrompt, ...rest } = employee;
845
+ const normalized = { ...rest, name: employee.name.toLowerCase() };
846
+ if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
847
+ throw new Error(`Employee '${normalized.name}' already exists`);
848
+ }
849
+ return [...employees, normalized];
850
+ }
851
+ function appendToCoordinatorTeam(employee) {
852
+ const coordinator = getCoordinatorEmployee(loadEmployeesSync());
853
+ if (!coordinator) return;
854
+ const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
855
+ if (!existsSync4(idPath)) return;
856
+ const content = readFileSync3(idPath, "utf-8");
857
+ if (content.includes(`**${capitalize(employee.name)}`)) return;
858
+ const teamMatch = content.match(TEAM_SECTION_RE);
859
+ if (!teamMatch || teamMatch.index === void 0) return;
860
+ const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
861
+ const nextHeading = afterTeam.match(/\n## /);
862
+ const entry = `
863
+ **${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
864
+ `;
865
+ let updated;
866
+ if (nextHeading && nextHeading.index !== void 0) {
867
+ const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
868
+ updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
869
+ } else {
870
+ updated = content.trimEnd() + "\n" + entry;
871
+ }
872
+ writeFileSync2(idPath, updated, "utf-8");
873
+ }
874
+ function capitalize(s) {
875
+ return s.charAt(0).toUpperCase() + s.slice(1);
876
+ }
877
+ async function hireEmployee(employee) {
878
+ const employees = await loadEmployees();
879
+ const updated = addEmployee(employees, employee);
880
+ await saveEmployees(updated);
881
+ try {
882
+ appendToCoordinatorTeam(employee);
883
+ } catch {
884
+ }
885
+ try {
886
+ const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
887
+ const config = loadAgentConfig2();
888
+ const name = employee.name.toLowerCase();
889
+ if (!config[name] && config["default"]) {
890
+ config[name] = { ...config["default"] };
891
+ saveAgentConfig2(config);
892
+ }
893
+ } catch {
894
+ }
895
+ return updated;
896
+ }
897
+ async function normalizeRosterCase(rosterPath) {
898
+ const employees = await loadEmployees(rosterPath);
899
+ let changed = false;
900
+ for (const emp of employees) {
901
+ if (emp.name !== emp.name.toLowerCase()) {
902
+ const oldName = emp.name;
903
+ emp.name = emp.name.toLowerCase();
904
+ changed = true;
905
+ try {
906
+ const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
907
+ const oldPath = path3.join(identityDir, `${oldName}.md`);
908
+ const newPath = path3.join(identityDir, `${emp.name}.md`);
909
+ if (existsSync4(oldPath) && !existsSync4(newPath)) {
910
+ renameSync2(oldPath, newPath);
911
+ } else if (existsSync4(oldPath) && oldPath !== newPath) {
912
+ const content = readFileSync3(oldPath, "utf-8");
913
+ writeFileSync2(newPath, content, "utf-8");
914
+ if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
915
+ unlinkSync(oldPath);
916
+ }
917
+ }
918
+ } catch {
919
+ }
920
+ }
921
+ }
922
+ if (changed) {
923
+ await saveEmployees(employees, rosterPath);
924
+ }
925
+ return changed;
926
+ }
927
+ function findExeBin() {
928
+ try {
929
+ return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
930
+ } catch {
931
+ return null;
932
+ }
933
+ }
934
+ function registerBinSymlinks(name) {
935
+ const created = [];
936
+ const skipped = [];
937
+ const errors = [];
938
+ const exeBinPath = findExeBin();
939
+ if (!exeBinPath) {
940
+ errors.push("Could not find 'exe-os' in PATH");
941
+ return { created, skipped, errors };
942
+ }
943
+ const binDir = path3.dirname(exeBinPath);
944
+ let target;
945
+ try {
946
+ target = readlinkSync(exeBinPath);
947
+ } catch {
948
+ errors.push("Could not read 'exe' symlink");
949
+ return { created, skipped, errors };
950
+ }
951
+ for (const suffix of ["", "-opencode"]) {
952
+ const linkName = `${name}${suffix}`;
953
+ const linkPath = path3.join(binDir, linkName);
954
+ if (existsSync4(linkPath)) {
955
+ skipped.push(linkName);
956
+ continue;
957
+ }
958
+ try {
959
+ symlinkSync(target, linkPath);
960
+ created.push(linkName);
961
+ } catch (err) {
962
+ errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
963
+ }
964
+ }
965
+ return { created, skipped, errors };
966
+ }
967
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
602
968
  var init_employees = __esm({
603
969
  "src/lib/employees.ts"() {
604
970
  "use strict";
605
971
  init_config();
606
- EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
972
+ EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
607
973
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
608
974
  COORDINATOR_ROLE = "COO";
609
- IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
975
+ MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
976
+ IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
977
+ TEAM_SECTION_RE = /^## Team\b.*$/m;
610
978
  }
611
979
  });
612
980
 
613
981
  // src/lib/database-adapter.ts
614
982
  import os3 from "os";
615
- import path3 from "path";
983
+ import path4 from "path";
616
984
  import { createRequire } from "module";
617
985
  import { pathToFileURL } from "url";
618
986
  function quotedIdentifier(identifier) {
@@ -923,8 +1291,8 @@ async function loadPrismaClient() {
923
1291
  }
924
1292
  return new PrismaClient2();
925
1293
  }
926
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os3.homedir(), "exe-db");
927
- const requireFromExeDb = createRequire(path3.join(exeDbRoot, "package.json"));
1294
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
1295
+ const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
928
1296
  const prismaEntry = requireFromExeDb.resolve("@prisma/client");
929
1297
  const module = await import(pathToFileURL(prismaEntry).href);
930
1298
  const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
@@ -1196,8 +1564,8 @@ var init_database_adapter = __esm({
1196
1564
 
1197
1565
  // src/lib/daemon-auth.ts
1198
1566
  import crypto2 from "crypto";
1199
- import path4 from "path";
1200
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
1567
+ import path5 from "path";
1568
+ import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
1201
1569
  function normalizeToken(token) {
1202
1570
  if (!token) return null;
1203
1571
  const trimmed = token.trim();
@@ -1205,8 +1573,8 @@ function normalizeToken(token) {
1205
1573
  }
1206
1574
  function readDaemonToken() {
1207
1575
  try {
1208
- if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
1209
- return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
1576
+ if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
1577
+ return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
1210
1578
  } catch {
1211
1579
  return null;
1212
1580
  }
@@ -1216,7 +1584,7 @@ function ensureDaemonToken(seed) {
1216
1584
  if (existing) return existing;
1217
1585
  const token = normalizeToken(seed) ?? crypto2.randomBytes(32).toString("hex");
1218
1586
  ensurePrivateDirSync(EXE_AI_DIR);
1219
- writeFileSync2(DAEMON_TOKEN_PATH, `${token}
1587
+ writeFileSync3(DAEMON_TOKEN_PATH, `${token}
1220
1588
  `, "utf8");
1221
1589
  enforcePrivateFileSync(DAEMON_TOKEN_PATH);
1222
1590
  return token;
@@ -1227,7 +1595,7 @@ var init_daemon_auth = __esm({
1227
1595
  "use strict";
1228
1596
  init_config();
1229
1597
  init_secure_files();
1230
- DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
1598
+ DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
1231
1599
  }
1232
1600
  });
1233
1601
 
@@ -1247,8 +1615,8 @@ import net from "net";
1247
1615
  import os4 from "os";
1248
1616
  import { spawn, execSync as execSync2 } from "child_process";
1249
1617
  import { randomUUID } from "crypto";
1250
- import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
1251
- import path5 from "path";
1618
+ import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
1619
+ import path6 from "path";
1252
1620
  import { fileURLToPath } from "url";
1253
1621
  function handleData(chunk) {
1254
1622
  _buffer += chunk.toString();
@@ -1284,9 +1652,9 @@ function isZombie(pid) {
1284
1652
  }
1285
1653
  }
1286
1654
  function cleanupStaleFiles() {
1287
- if (existsSync5(PID_PATH)) {
1655
+ if (existsSync6(PID_PATH)) {
1288
1656
  try {
1289
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1657
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1290
1658
  if (pid > 0) {
1291
1659
  try {
1292
1660
  process.kill(pid, 0);
@@ -1311,11 +1679,11 @@ function cleanupStaleFiles() {
1311
1679
  }
1312
1680
  }
1313
1681
  function findPackageRoot() {
1314
- let dir = path5.dirname(fileURLToPath(import.meta.url));
1315
- const { root } = path5.parse(dir);
1682
+ let dir = path6.dirname(fileURLToPath(import.meta.url));
1683
+ const { root } = path6.parse(dir);
1316
1684
  while (dir !== root) {
1317
- if (existsSync5(path5.join(dir, "package.json"))) return dir;
1318
- dir = path5.dirname(dir);
1685
+ if (existsSync6(path6.join(dir, "package.json"))) return dir;
1686
+ dir = path6.dirname(dir);
1319
1687
  }
1320
1688
  return null;
1321
1689
  }
@@ -1333,8 +1701,8 @@ function spawnDaemon() {
1333
1701
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
1334
1702
  return;
1335
1703
  }
1336
- const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1337
- if (!existsSync5(daemonPath)) {
1704
+ const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1705
+ if (!existsSync6(daemonPath)) {
1338
1706
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1339
1707
  `);
1340
1708
  return;
@@ -1343,7 +1711,7 @@ function spawnDaemon() {
1343
1711
  const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
1344
1712
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1345
1713
  `);
1346
- const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
1714
+ const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
1347
1715
  let stderrFd = "ignore";
1348
1716
  try {
1349
1717
  stderrFd = openSync(logPath, "a");
@@ -1508,9 +1876,9 @@ function killAndRespawnDaemon() {
1508
1876
  }
1509
1877
  try {
1510
1878
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
1511
- if (existsSync5(PID_PATH)) {
1879
+ if (existsSync6(PID_PATH)) {
1512
1880
  try {
1513
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1881
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1514
1882
  if (pid > 0) {
1515
1883
  try {
1516
1884
  process.kill(pid, "SIGKILL");
@@ -1656,9 +2024,9 @@ var init_exe_daemon_client = __esm({
1656
2024
  "use strict";
1657
2025
  init_config();
1658
2026
  init_daemon_auth();
1659
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
1660
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
1661
- SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
2027
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
2028
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
2029
+ SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
1662
2030
  SPAWN_LOCK_STALE_MS = 3e4;
1663
2031
  CONNECT_TIMEOUT_MS = 15e3;
1664
2032
  REQUEST_TIMEOUT_MS = 3e4;
@@ -1926,7 +2294,7 @@ __export(database_exports, {
1926
2294
  isInitialized: () => isInitialized,
1927
2295
  setExternalClient: () => setExternalClient
1928
2296
  });
1929
- 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";
2297
+ 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";
1930
2298
  import { createClient } from "@libsql/client";
1931
2299
  import { homedir } from "os";
1932
2300
  import { join } from "path";
@@ -1979,11 +2347,11 @@ function releaseDbLock() {
1979
2347
  }
1980
2348
  async function initDatabase(config) {
1981
2349
  acquireDbLock();
1982
- if (existsSync6(config.dbPath)) {
2350
+ if (existsSync7(config.dbPath)) {
1983
2351
  const dbStat = statSync2(config.dbPath);
1984
2352
  if (dbStat.size === 0) {
1985
2353
  const walPath = config.dbPath + "-wal";
1986
- if (existsSync6(walPath) && statSync2(walPath).size > 0) {
2354
+ if (existsSync7(walPath) && statSync2(walPath).size > 0) {
1987
2355
  const backupPath = config.dbPath + ".zeroed-" + Date.now();
1988
2356
  copyFileSync(config.dbPath, backupPath);
1989
2357
  unlinkSync3(config.dbPath);
@@ -3569,16 +3937,16 @@ var init_database = __esm({
3569
3937
  });
3570
3938
 
3571
3939
  // src/lib/keychain.ts
3572
- import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3573
- import { existsSync as existsSync7, statSync as statSync3 } from "fs";
3940
+ import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
3941
+ import { existsSync as existsSync8, statSync as statSync3 } from "fs";
3574
3942
  import { execSync as execSync3 } from "child_process";
3575
- import path6 from "path";
3943
+ import path7 from "path";
3576
3944
  import os5 from "os";
3577
3945
  function getKeyDir() {
3578
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
3946
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
3579
3947
  }
3580
3948
  function getKeyPath() {
3581
- return path6.join(getKeyDir(), "master.key");
3949
+ return path7.join(getKeyDir(), "master.key");
3582
3950
  }
3583
3951
  function nativeKeychainAllowed() {
3584
3952
  return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
@@ -3604,12 +3972,14 @@ function linuxSecretAvailable() {
3604
3972
  function isRootOnlyTrustedServerKeyFile(keyPath) {
3605
3973
  if (process.platform !== "linux") return false;
3606
3974
  try {
3607
- const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
3608
3975
  const st = statSync3(keyPath);
3609
3976
  if (!st.isFile() || (st.mode & 63) !== 0) return false;
3977
+ const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
3610
3978
  if (uid === 0) return true;
3611
3979
  const exeOsDir = process.env.EXE_OS_DIR;
3612
- return Boolean(exeOsDir && path6.resolve(keyPath).startsWith(path6.resolve(exeOsDir) + path6.sep));
3980
+ if (exeOsDir && path7.resolve(keyPath).startsWith(path7.resolve(exeOsDir) + path7.sep)) return true;
3981
+ if (!linuxSecretAvailable()) return true;
3982
+ return false;
3613
3983
  } catch {
3614
3984
  return false;
3615
3985
  }
@@ -3759,15 +4129,25 @@ async function writeMachineBoundFileFallback(b64) {
3759
4129
  await mkdir3(dir, { recursive: true });
3760
4130
  const keyPath = getKeyPath();
3761
4131
  const machineKey = deriveMachineKey();
3762
- if (machineKey) {
3763
- const encrypted = encryptWithMachineKey(b64, machineKey);
3764
- await writeFile3(keyPath, encrypted + "\n", "utf-8");
3765
- await chmod2(keyPath, 384);
3766
- return "encrypted";
4132
+ const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
4133
+ const result = machineKey ? "encrypted" : "plaintext";
4134
+ const tmpPath = keyPath + ".tmp";
4135
+ try {
4136
+ if (existsSync8(keyPath)) {
4137
+ await copyFile(keyPath, keyPath + ".bak").catch(() => {
4138
+ });
4139
+ }
4140
+ await writeFile3(tmpPath, content, "utf-8");
4141
+ await chmod2(tmpPath, 384);
4142
+ await rename(tmpPath, keyPath);
4143
+ } catch (err) {
4144
+ try {
4145
+ await unlink(tmpPath);
4146
+ } catch {
4147
+ }
4148
+ throw err;
3767
4149
  }
3768
- await writeFile3(keyPath, b64 + "\n", "utf-8");
3769
- await chmod2(keyPath, 384);
3770
- return "plaintext";
4150
+ return result;
3771
4151
  }
3772
4152
  async function getMasterKey() {
3773
4153
  let nativeValue = macKeychainGet() ?? linuxSecretGet();
@@ -3806,7 +4186,7 @@ async function getMasterKey() {
3806
4186
  }
3807
4187
  }
3808
4188
  const keyPath = getKeyPath();
3809
- if (!existsSync7(keyPath)) {
4189
+ if (!existsSync8(keyPath)) {
3810
4190
  process.stderr.write(
3811
4191
  `[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
3812
4192
  `
@@ -3834,7 +4214,7 @@ async function getMasterKey() {
3834
4214
  b64Value = content;
3835
4215
  }
3836
4216
  const key = Buffer.from(b64Value, "base64");
3837
- if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
4217
+ if (isRootOnlyTrustedServerKeyFile(keyPath)) {
3838
4218
  return key;
3839
4219
  }
3840
4220
  const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
@@ -4140,14 +4520,14 @@ __export(shard_manager_exports, {
4140
4520
  listShards: () => listShards,
4141
4521
  shardExists: () => shardExists
4142
4522
  });
4143
- import path7 from "path";
4144
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readdirSync, renameSync as renameSync3, statSync as statSync4 } from "fs";
4523
+ import path8 from "path";
4524
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, readdirSync, renameSync as renameSync3, statSync as statSync4 } from "fs";
4145
4525
  import { createClient as createClient2 } from "@libsql/client";
4146
4526
  function initShardManager(encryptionKey) {
4147
4527
  _encryptionKey = encryptionKey;
4148
4528
  _keyValidated = false;
4149
4529
  _keyValidationPromise = null;
4150
- if (!existsSync8(SHARDS_DIR)) {
4530
+ if (!existsSync9(SHARDS_DIR)) {
4151
4531
  mkdirSync3(SHARDS_DIR, { recursive: true });
4152
4532
  }
4153
4533
  const existingShards = readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db"));
@@ -4168,7 +4548,7 @@ async function validateEncryptionKey() {
4168
4548
  return true;
4169
4549
  }
4170
4550
  for (const shardFile of existingShards.slice(0, 3)) {
4171
- const dbPath = path7.join(SHARDS_DIR, shardFile);
4551
+ const dbPath = path8.join(SHARDS_DIR, shardFile);
4172
4552
  const testClient = createClient2({ url: `file:${dbPath}`, encryptionKey: _encryptionKey });
4173
4553
  try {
4174
4554
  await testClient.execute("SELECT COUNT(*) FROM sqlite_schema");
@@ -4211,7 +4591,7 @@ function getShardClient(projectName) {
4211
4591
  while (_shards.size >= MAX_OPEN_SHARDS) {
4212
4592
  evictLRU();
4213
4593
  }
4214
- const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
4594
+ const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
4215
4595
  const client = createClient2({
4216
4596
  url: `file:${dbPath}`,
4217
4597
  encryptionKey: _encryptionKey
@@ -4222,13 +4602,13 @@ function getShardClient(projectName) {
4222
4602
  }
4223
4603
  function shardExists(projectName) {
4224
4604
  const safeName = safeShardName(projectName);
4225
- return existsSync8(path7.join(SHARDS_DIR, `${safeName}.db`));
4605
+ return existsSync9(path8.join(SHARDS_DIR, `${safeName}.db`));
4226
4606
  }
4227
4607
  function safeShardName(projectName) {
4228
4608
  return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
4229
4609
  }
4230
4610
  function listShards() {
4231
- if (!existsSync8(SHARDS_DIR)) return [];
4611
+ if (!existsSync9(SHARDS_DIR)) return [];
4232
4612
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
4233
4613
  }
4234
4614
  async function auditShardHealth(options = {}) {
@@ -4240,7 +4620,7 @@ async function auditShardHealth(options = {}) {
4240
4620
  const names = listShards();
4241
4621
  const shards = [];
4242
4622
  for (const name of names) {
4243
- const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
4623
+ const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
4244
4624
  const stat = statSync4(dbPath);
4245
4625
  const item = {
4246
4626
  name,
@@ -4275,7 +4655,7 @@ async function auditShardHealth(options = {}) {
4275
4655
  _shards.delete(name);
4276
4656
  _shardLastAccess.delete(name);
4277
4657
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4278
- const archivedPath = path7.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
4658
+ const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
4279
4659
  renameSync3(dbPath, archivedPath);
4280
4660
  item.archivedPath = archivedPath;
4281
4661
  }
@@ -4503,11 +4883,11 @@ async function getReadyShardClient(projectName) {
4503
4883
  client.close();
4504
4884
  _shards.delete(safeName);
4505
4885
  _shardLastAccess.delete(safeName);
4506
- const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
4507
- if (existsSync8(dbPath)) {
4886
+ const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
4887
+ if (existsSync9(dbPath)) {
4508
4888
  const stat = statSync4(dbPath);
4509
4889
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4510
- const archivedPath = path7.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
4890
+ const archivedPath = path8.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
4511
4891
  renameSync3(dbPath, archivedPath);
4512
4892
  process.stderr.write(
4513
4893
  `[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
@@ -4575,7 +4955,7 @@ var init_shard_manager = __esm({
4575
4955
  "src/lib/shard-manager.ts"() {
4576
4956
  "use strict";
4577
4957
  init_config();
4578
- SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
4958
+ SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
4579
4959
  SHARD_IDLE_MS = 5 * 60 * 1e3;
4580
4960
  MAX_OPEN_SHARDS = 10;
4581
4961
  EVICTION_INTERVAL_MS = 60 * 1e3;
@@ -6167,8 +6547,8 @@ __export(reranker_exports, {
6167
6547
  rerankWithContext: () => rerankWithContext,
6168
6548
  rerankWithScores: () => rerankWithScores
6169
6549
  });
6170
- import path8 from "path";
6171
- import { existsSync as existsSync9 } from "fs";
6550
+ import path9 from "path";
6551
+ import { existsSync as existsSync10 } from "fs";
6172
6552
  function resetIdleTimer() {
6173
6553
  if (_idleTimer) clearTimeout(_idleTimer);
6174
6554
  _idleTimer = setTimeout(() => {
@@ -6179,18 +6559,18 @@ function resetIdleTimer() {
6179
6559
  }
6180
6560
  }
6181
6561
  function isRerankerAvailable() {
6182
- return existsSync9(path8.join(MODELS_DIR, RERANKER_MODEL_FILE));
6562
+ return existsSync10(path9.join(MODELS_DIR, RERANKER_MODEL_FILE));
6183
6563
  }
6184
6564
  function getRerankerModelPath() {
6185
- return path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
6565
+ return path9.join(MODELS_DIR, RERANKER_MODEL_FILE);
6186
6566
  }
6187
6567
  async function ensureLoaded() {
6188
6568
  if (_rerankerContext) {
6189
6569
  resetIdleTimer();
6190
6570
  return;
6191
6571
  }
6192
- const modelPath = path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
6193
- if (!existsSync9(modelPath)) {
6572
+ const modelPath = path9.join(MODELS_DIR, RERANKER_MODEL_FILE);
6573
+ if (!existsSync10(modelPath)) {
6194
6574
  throw new Error(
6195
6575
  `Reranker model not found at ${modelPath}. Run /exe-setup to download it.`
6196
6576
  );
@@ -6361,7 +6741,7 @@ __export(project_name_exports, {
6361
6741
  getProjectName: () => getProjectName
6362
6742
  });
6363
6743
  import { execSync as execSync4 } from "child_process";
6364
- import path9 from "path";
6744
+ import path10 from "path";
6365
6745
  function getProjectName(cwd) {
6366
6746
  const dir = cwd ?? process.cwd();
6367
6747
  if (_cached && _cachedCwd === dir) return _cached;
@@ -6374,7 +6754,7 @@ function getProjectName(cwd) {
6374
6754
  timeout: 2e3,
6375
6755
  stdio: ["pipe", "pipe", "pipe"]
6376
6756
  }).trim();
6377
- repoRoot = path9.dirname(gitCommonDir);
6757
+ repoRoot = path10.dirname(gitCommonDir);
6378
6758
  } catch {
6379
6759
  repoRoot = execSync4("git rev-parse --show-toplevel", {
6380
6760
  cwd: dir,
@@ -6383,11 +6763,11 @@ function getProjectName(cwd) {
6383
6763
  stdio: ["pipe", "pipe", "pipe"]
6384
6764
  }).trim();
6385
6765
  }
6386
- _cached = path9.basename(repoRoot);
6766
+ _cached = path10.basename(repoRoot);
6387
6767
  _cachedCwd = dir;
6388
6768
  return _cached;
6389
6769
  } catch {
6390
- _cached = path9.basename(dir);
6770
+ _cached = path10.basename(dir);
6391
6771
  _cachedCwd = dir;
6392
6772
  return _cached;
6393
6773
  }
@@ -6411,8 +6791,8 @@ __export(file_grep_exports, {
6411
6791
  grepProjectFiles: () => grepProjectFiles
6412
6792
  });
6413
6793
  import { execSync as execSync5 } from "child_process";
6414
- import { readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync5, existsSync as existsSync10 } from "fs";
6415
- import path10 from "path";
6794
+ import { readFileSync as readFileSync6, readdirSync as readdirSync2, statSync as statSync5, existsSync as existsSync11 } from "fs";
6795
+ import path11 from "path";
6416
6796
  import crypto3 from "crypto";
6417
6797
  function hasRipgrep() {
6418
6798
  if (_hasRg === null) {
@@ -6452,7 +6832,7 @@ async function grepProjectFiles(query, projectRoot, options) {
6452
6832
  session_id: "file-grep",
6453
6833
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6454
6834
  tool_name: "file_grep",
6455
- project_name: path10.basename(projectRoot),
6835
+ project_name: path11.basename(projectRoot),
6456
6836
  has_error: false,
6457
6837
  raw_text: `${prefix} ${buildSnippet(hit, projectRoot)}`,
6458
6838
  vector: null,
@@ -6464,7 +6844,7 @@ function getChunkContext(filePath, lineNumber) {
6464
6844
  try {
6465
6845
  const ext = filePath.split(".").pop()?.toLowerCase();
6466
6846
  if (ext !== "ts" && ext !== "tsx" && ext !== "js" && ext !== "jsx") return "";
6467
- const source = readFileSync5(filePath, "utf8");
6847
+ const source = readFileSync6(filePath, "utf8");
6468
6848
  const lines = source.split("\n");
6469
6849
  for (let i = Math.min(lineNumber - 1, lines.length - 1); i >= 0; i--) {
6470
6850
  const line = lines[i];
@@ -6526,11 +6906,11 @@ function grepWithNodeFs(pattern, projectRoot, patterns) {
6526
6906
  const files = collectFiles(projectRoot, patterns ?? DEFAULT_PATTERNS);
6527
6907
  const hits = [];
6528
6908
  for (const filePath of files.slice(0, MAX_FILES)) {
6529
- const absPath = path10.join(projectRoot, filePath);
6909
+ const absPath = path11.join(projectRoot, filePath);
6530
6910
  try {
6531
6911
  const stat = statSync5(absPath);
6532
6912
  if (stat.size > MAX_FILE_SIZE) continue;
6533
- const content = readFileSync5(absPath, "utf8");
6913
+ const content = readFileSync6(absPath, "utf8");
6534
6914
  const lines = content.split("\n");
6535
6915
  const matches = content.match(regex);
6536
6916
  if (!matches || matches.length === 0) continue;
@@ -6553,15 +6933,15 @@ function collectFiles(root, patterns) {
6553
6933
  const files = [];
6554
6934
  function walk(dir, relative) {
6555
6935
  if (files.length >= MAX_FILES) return;
6556
- const basename = path10.basename(dir);
6936
+ const basename = path11.basename(dir);
6557
6937
  if (EXCLUDE_DIRS.includes(basename)) return;
6558
6938
  try {
6559
6939
  const entries = readdirSync2(dir, { withFileTypes: true });
6560
6940
  for (const entry of entries) {
6561
6941
  if (files.length >= MAX_FILES) return;
6562
- const rel = path10.join(relative, entry.name);
6942
+ const rel = path11.join(relative, entry.name);
6563
6943
  if (entry.isDirectory()) {
6564
- walk(path10.join(dir, entry.name), rel);
6944
+ walk(path11.join(dir, entry.name), rel);
6565
6945
  } else if (entry.isFile()) {
6566
6946
  for (const pat of patterns) {
6567
6947
  if (matchGlob(rel, pat)) {
@@ -6593,7 +6973,7 @@ function matchGlob(filePath, pattern) {
6593
6973
  if (slashIdx !== -1) {
6594
6974
  const dir = pattern.slice(0, slashIdx);
6595
6975
  const ext2 = pattern.slice(slashIdx + 1).replace("*", "");
6596
- const fileDir = path10.dirname(filePath);
6976
+ const fileDir = path11.dirname(filePath);
6597
6977
  return fileDir === dir && filePath.endsWith(ext2);
6598
6978
  }
6599
6979
  const ext = pattern.replace("*", "");
@@ -6601,9 +6981,9 @@ function matchGlob(filePath, pattern) {
6601
6981
  }
6602
6982
  function buildSnippet(hit, projectRoot) {
6603
6983
  try {
6604
- const absPath = path10.join(projectRoot, hit.filePath);
6605
- if (!existsSync10(absPath)) return hit.matchLine;
6606
- const lines = readFileSync5(absPath, "utf8").split("\n");
6984
+ const absPath = path11.join(projectRoot, hit.filePath);
6985
+ if (!existsSync11(absPath)) return hit.matchLine;
6986
+ const lines = readFileSync6(absPath, "utf8").split("\n");
6607
6987
  const start = Math.max(0, hit.lineNumber - 3);
6608
6988
  const end = Math.min(lines.length, hit.lineNumber + 2);
6609
6989
  return lines.slice(start, end).join("\n").slice(0, 500);
@@ -7989,9 +8369,9 @@ var init_agent_context = __esm({
7989
8369
  });
7990
8370
 
7991
8371
  // src/lib/active-agent.ts
7992
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, unlinkSync as unlinkSync4, readdirSync as readdirSync3 } from "fs";
8372
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, unlinkSync as unlinkSync4, readdirSync as readdirSync3 } from "fs";
7993
8373
  import { execSync as execSync7 } from "child_process";
7994
- import path11 from "path";
8374
+ import path12 from "path";
7995
8375
  function isNameWithOptionalInstance(candidate, baseName) {
7996
8376
  if (candidate === baseName) return true;
7997
8377
  if (!candidate.startsWith(baseName)) return false;
@@ -8035,12 +8415,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
8035
8415
  return null;
8036
8416
  }
8037
8417
  function getMarkerPath() {
8038
- return path11.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
8418
+ return path12.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
8039
8419
  }
8040
8420
  function writeActiveAgent(agentId, agentRole) {
8041
8421
  try {
8042
8422
  mkdirSync4(CACHE_DIR, { recursive: true });
8043
- writeFileSync3(
8423
+ writeFileSync4(
8044
8424
  getMarkerPath(),
8045
8425
  JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
8046
8426
  );
@@ -8058,7 +8438,7 @@ function getActiveAgent() {
8058
8438
  if (httpCtx) return httpCtx;
8059
8439
  try {
8060
8440
  const markerPath = getMarkerPath();
8061
- const raw = readFileSync6(markerPath, "utf8");
8441
+ const raw = readFileSync7(markerPath, "utf8");
8062
8442
  const data = JSON.parse(raw);
8063
8443
  if (data.agentId) {
8064
8444
  if (data.startedAt) {
@@ -8106,14 +8486,14 @@ function getAllActiveAgents() {
8106
8486
  const key = file.slice("active-agent-".length, -".json".length);
8107
8487
  if (key === "undefined") continue;
8108
8488
  try {
8109
- const raw = readFileSync6(path11.join(CACHE_DIR, file), "utf8");
8489
+ const raw = readFileSync7(path12.join(CACHE_DIR, file), "utf8");
8110
8490
  const data = JSON.parse(raw);
8111
8491
  if (!data.agentId) continue;
8112
8492
  if (data.startedAt) {
8113
8493
  const age = Date.now() - new Date(data.startedAt).getTime();
8114
8494
  if (age > STALE_MS) {
8115
8495
  try {
8116
- unlinkSync4(path11.join(CACHE_DIR, file));
8496
+ unlinkSync4(path12.join(CACHE_DIR, file));
8117
8497
  } catch {
8118
8498
  }
8119
8499
  continue;
@@ -8136,11 +8516,11 @@ function getAllActiveAgents() {
8136
8516
  function cleanupSessionMarkers() {
8137
8517
  const key = getSessionKey();
8138
8518
  try {
8139
- unlinkSync4(path11.join(CACHE_DIR, `active-agent-${key}.json`));
8519
+ unlinkSync4(path12.join(CACHE_DIR, `active-agent-${key}.json`));
8140
8520
  } catch {
8141
8521
  }
8142
8522
  try {
8143
- unlinkSync4(path11.join(CACHE_DIR, "active-agent-undefined.json"));
8523
+ unlinkSync4(path12.join(CACHE_DIR, "active-agent-undefined.json"));
8144
8524
  } catch {
8145
8525
  }
8146
8526
  }
@@ -8152,7 +8532,7 @@ var init_active_agent = __esm({
8152
8532
  init_session_key();
8153
8533
  init_agent_context();
8154
8534
  init_employees();
8155
- CACHE_DIR = path11.join(EXE_AI_DIR, "session-cache");
8535
+ CACHE_DIR = path12.join(EXE_AI_DIR, "session-cache");
8156
8536
  STALE_MS = 24 * 60 * 60 * 1e3;
8157
8537
  }
8158
8538
  });
@@ -8477,13 +8857,13 @@ var init_fast_db_init = __esm({
8477
8857
  });
8478
8858
 
8479
8859
  // src/lib/session-registry.ts
8480
- import path12 from "path";
8860
+ import path13 from "path";
8481
8861
  import os6 from "os";
8482
8862
  var REGISTRY_PATH;
8483
8863
  var init_session_registry = __esm({
8484
8864
  "src/lib/session-registry.ts"() {
8485
8865
  "use strict";
8486
- REGISTRY_PATH = path12.join(os6.homedir(), ".exe-os", "session-registry.json");
8866
+ REGISTRY_PATH = path13.join(os6.homedir(), ".exe-os", "session-registry.json");
8487
8867
  }
8488
8868
  });
8489
8869
 
@@ -8619,51 +8999,6 @@ var init_provider_table = __esm({
8619
8999
  }
8620
9000
  });
8621
9001
 
8622
- // src/lib/runtime-table.ts
8623
- var RUNTIME_TABLE;
8624
- var init_runtime_table = __esm({
8625
- "src/lib/runtime-table.ts"() {
8626
- "use strict";
8627
- RUNTIME_TABLE = {
8628
- codex: {
8629
- binary: "codex",
8630
- launchMode: "interactive",
8631
- autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
8632
- inlineFlag: "--no-alt-screen",
8633
- apiKeyEnv: "OPENAI_API_KEY",
8634
- defaultModel: "gpt-5.5"
8635
- },
8636
- opencode: {
8637
- binary: "opencode",
8638
- launchMode: "exec",
8639
- autoApproveFlag: "--dangerously-skip-permissions",
8640
- inlineFlag: "",
8641
- apiKeyEnv: "ANTHROPIC_API_KEY",
8642
- defaultModel: "anthropic/claude-sonnet-4-6"
8643
- }
8644
- };
8645
- }
8646
- });
8647
-
8648
- // src/lib/agent-config.ts
8649
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync11 } from "fs";
8650
- import path13 from "path";
8651
- var AGENT_CONFIG_PATH, DEFAULT_MODELS;
8652
- var init_agent_config = __esm({
8653
- "src/lib/agent-config.ts"() {
8654
- "use strict";
8655
- init_config();
8656
- init_runtime_table();
8657
- init_secure_files();
8658
- AGENT_CONFIG_PATH = path13.join(EXE_AI_DIR, "agent-config.json");
8659
- DEFAULT_MODELS = {
8660
- claude: "claude-opus-4.6",
8661
- codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
8662
- opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
8663
- };
8664
- }
8665
- });
8666
-
8667
9002
  // src/lib/intercom-queue.ts
8668
9003
  import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, renameSync as renameSync4, existsSync as existsSync12, mkdirSync as mkdirSync5 } from "fs";
8669
9004
  import path14 from "path";
@@ -8744,6 +9079,21 @@ function isRootSession(name) {
8744
9079
  function extractRootExe(name) {
8745
9080
  if (!name) return null;
8746
9081
  if (!name.includes("-")) return name;
9082
+ try {
9083
+ const roster = (init_employees(), __toCommonJS(employees_exports)).loadEmployeesSync();
9084
+ if (roster.length > 0) {
9085
+ const sortedNames = roster.map((e) => e.name).sort((a, b) => b.length - a.length);
9086
+ for (const agentName of sortedNames) {
9087
+ const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9088
+ const regex = new RegExp(`^${escaped}\\d*-(.+)$`);
9089
+ const match = name.match(regex);
9090
+ if (match) {
9091
+ return extractRootExe(match[1]);
9092
+ }
9093
+ }
9094
+ }
9095
+ } catch {
9096
+ }
8747
9097
  const parts = name.split("-").filter(Boolean);
8748
9098
  return parts.length > 0 ? parts[parts.length - 1] : null;
8749
9099
  }
@@ -8762,6 +9112,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
8762
9112
  function getParentExe(sessionKey) {
8763
9113
  try {
8764
9114
  const data = JSON.parse(readFileSync11(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
9115
+ if (data.registeredAt) {
9116
+ const age = Date.now() - new Date(data.registeredAt).getTime();
9117
+ if (age > PARENT_EXE_CACHE_TTL_MS) return null;
9118
+ }
8765
9119
  return data.parentExe || null;
8766
9120
  } catch {
8767
9121
  return null;
@@ -8834,7 +9188,7 @@ function resolveExeSession() {
8834
9188
  }
8835
9189
  return candidate;
8836
9190
  }
8837
- var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
9191
+ var SPAWN_LOCK_DIR, SESSION_CACHE, PARENT_EXE_CACHE_TTL_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
8838
9192
  var init_tmux_routing = __esm({
8839
9193
  "src/lib/tmux-routing.ts"() {
8840
9194
  "use strict";
@@ -8852,6 +9206,7 @@ var init_tmux_routing = __esm({
8852
9206
  init_agent_symlinks();
8853
9207
  SPAWN_LOCK_DIR = path18.join(os10.homedir(), ".exe-os", "spawn-locks");
8854
9208
  SESSION_CACHE = path18.join(os10.homedir(), ".exe-os", "session-cache");
9209
+ PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
8855
9210
  INTERCOM_LOG2 = path18.join(os10.homedir(), ".exe-os", "intercom.log");
8856
9211
  DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
8857
9212
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;