@askexenow/exe-os 0.9.6 → 0.9.8

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 (95) hide show
  1. package/dist/bin/backfill-conversations.js +754 -79
  2. package/dist/bin/backfill-responses.js +752 -77
  3. package/dist/bin/backfill-vectors.js +752 -77
  4. package/dist/bin/cleanup-stale-review-tasks.js +668 -37
  5. package/dist/bin/cli.js +1399 -607
  6. package/dist/bin/exe-agent-config.js +123 -95
  7. package/dist/bin/exe-agent.js +41 -25
  8. package/dist/bin/exe-assign.js +732 -57
  9. package/dist/bin/exe-boot.js +795 -155
  10. package/dist/bin/exe-call.js +209 -138
  11. package/dist/bin/exe-cloud.js +35 -12
  12. package/dist/bin/exe-dispatch.js +703 -72
  13. package/dist/bin/exe-doctor.js +648 -26
  14. package/dist/bin/exe-export-behaviors.js +650 -20
  15. package/dist/bin/exe-forget.js +635 -13
  16. package/dist/bin/exe-gateway.js +1064 -273
  17. package/dist/bin/exe-heartbeat.js +676 -45
  18. package/dist/bin/exe-kill.js +646 -16
  19. package/dist/bin/exe-launch-agent.js +887 -97
  20. package/dist/bin/exe-link.js +658 -43
  21. package/dist/bin/exe-new-employee.js +378 -177
  22. package/dist/bin/exe-pending-messages.js +656 -34
  23. package/dist/bin/exe-pending-notifications.js +635 -13
  24. package/dist/bin/exe-pending-reviews.js +659 -37
  25. package/dist/bin/exe-rename.js +645 -30
  26. package/dist/bin/exe-review.js +635 -13
  27. package/dist/bin/exe-search.js +771 -88
  28. package/dist/bin/exe-session-cleanup.js +845 -152
  29. package/dist/bin/exe-settings.js +127 -91
  30. package/dist/bin/exe-start-codex.js +729 -94
  31. package/dist/bin/exe-start-opencode.js +717 -82
  32. package/dist/bin/exe-status.js +668 -37
  33. package/dist/bin/exe-team.js +635 -13
  34. package/dist/bin/git-sweep.js +731 -91
  35. package/dist/bin/graph-backfill.js +643 -13
  36. package/dist/bin/graph-export.js +646 -16
  37. package/dist/bin/install.js +596 -193
  38. package/dist/bin/scan-tasks.js +735 -95
  39. package/dist/bin/setup.js +1038 -210
  40. package/dist/bin/shard-migrate.js +645 -15
  41. package/dist/bin/wiki-sync.js +646 -16
  42. package/dist/gateway/index.js +1038 -247
  43. package/dist/hooks/bug-report-worker.js +902 -172
  44. package/dist/hooks/commit-complete.js +729 -89
  45. package/dist/hooks/error-recall.js +776 -93
  46. package/dist/hooks/exe-heartbeat-hook.js +85 -71
  47. package/dist/hooks/ingest-worker.js +851 -158
  48. package/dist/hooks/ingest.js +90 -73
  49. package/dist/hooks/instructions-loaded.js +669 -38
  50. package/dist/hooks/notification.js +661 -30
  51. package/dist/hooks/post-compact.js +685 -45
  52. package/dist/hooks/pre-compact.js +729 -89
  53. package/dist/hooks/pre-tool-use.js +883 -127
  54. package/dist/hooks/prompt-ingest-worker.js +758 -83
  55. package/dist/hooks/prompt-submit.js +1071 -321
  56. package/dist/hooks/response-ingest-worker.js +758 -83
  57. package/dist/hooks/session-end.js +732 -92
  58. package/dist/hooks/session-start.js +1042 -209
  59. package/dist/hooks/stop.js +691 -51
  60. package/dist/hooks/subagent-stop.js +685 -45
  61. package/dist/hooks/summary-worker.js +827 -134
  62. package/dist/index.js +1026 -234
  63. package/dist/lib/cloud-sync.js +663 -48
  64. package/dist/lib/consolidation.js +26 -3
  65. package/dist/lib/database.js +626 -18
  66. package/dist/lib/db.js +2261 -0
  67. package/dist/lib/device-registry.js +640 -25
  68. package/dist/lib/embedder.js +96 -43
  69. package/dist/lib/employee-templates.js +16 -0
  70. package/dist/lib/employees.js +259 -83
  71. package/dist/lib/exe-daemon-client.js +101 -63
  72. package/dist/lib/exe-daemon.js +905 -164
  73. package/dist/lib/hybrid-search.js +771 -88
  74. package/dist/lib/identity.js +27 -7
  75. package/dist/lib/messaging.js +66 -30
  76. package/dist/lib/reminders.js +21 -1
  77. package/dist/lib/schedules.js +636 -14
  78. package/dist/lib/skill-learning.js +21 -1
  79. package/dist/lib/store.js +643 -13
  80. package/dist/lib/task-router.js +82 -71
  81. package/dist/lib/tasks.js +109 -73
  82. package/dist/lib/tmux-routing.js +98 -62
  83. package/dist/lib/token-spend.js +26 -6
  84. package/dist/mcp/server.js +1807 -472
  85. package/dist/mcp/tools/complete-reminder.js +21 -1
  86. package/dist/mcp/tools/create-reminder.js +21 -1
  87. package/dist/mcp/tools/create-task.js +301 -166
  88. package/dist/mcp/tools/deactivate-behavior.js +24 -4
  89. package/dist/mcp/tools/list-reminders.js +21 -1
  90. package/dist/mcp/tools/list-tasks.js +206 -40
  91. package/dist/mcp/tools/send-message.js +69 -33
  92. package/dist/mcp/tools/update-task.js +86 -50
  93. package/dist/runtime/index.js +731 -91
  94. package/dist/tui/App.js +864 -125
  95. package/package.json +3 -2
@@ -531,98 +531,132 @@ async function pingDaemon() {
531
531
  return null;
532
532
  }
533
533
  function killAndRespawnDaemon() {
534
- process.stderr.write("[exed-client] Killing daemon for restart...\n");
535
- if (existsSync2(PID_PATH)) {
536
- try {
537
- const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
538
- if (pid > 0) {
539
- try {
540
- process.kill(pid, "SIGKILL");
541
- } catch {
534
+ if (!acquireSpawnLock()) {
535
+ process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
536
+ if (_socket) {
537
+ _socket.destroy();
538
+ _socket = null;
539
+ }
540
+ _connected = false;
541
+ _buffer = "";
542
+ return;
543
+ }
544
+ try {
545
+ process.stderr.write("[exed-client] Killing daemon for restart...\n");
546
+ if (existsSync2(PID_PATH)) {
547
+ try {
548
+ const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
549
+ if (pid > 0) {
550
+ try {
551
+ process.kill(pid, "SIGKILL");
552
+ } catch {
553
+ }
542
554
  }
555
+ } catch {
543
556
  }
557
+ }
558
+ if (_socket) {
559
+ _socket.destroy();
560
+ _socket = null;
561
+ }
562
+ _connected = false;
563
+ _buffer = "";
564
+ try {
565
+ unlinkSync(PID_PATH);
544
566
  } catch {
545
567
  }
568
+ try {
569
+ unlinkSync(SOCKET_PATH);
570
+ } catch {
571
+ }
572
+ spawnDaemon();
573
+ } finally {
574
+ releaseSpawnLock();
546
575
  }
547
- if (_socket) {
548
- _socket.destroy();
549
- _socket = null;
550
- }
551
- _connected = false;
552
- _buffer = "";
576
+ }
577
+ function isDaemonTooYoung() {
553
578
  try {
554
- unlinkSync(PID_PATH);
579
+ const stat = statSync(PID_PATH);
580
+ return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
555
581
  } catch {
582
+ return false;
556
583
  }
557
- try {
558
- unlinkSync(SOCKET_PATH);
559
- } catch {
584
+ }
585
+ async function retryThenRestart(doRequest, label) {
586
+ const result = await doRequest();
587
+ if (!result.error) {
588
+ _consecutiveFailures = 0;
589
+ return result;
590
+ }
591
+ _consecutiveFailures++;
592
+ for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
593
+ const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
594
+ process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
595
+ `);
596
+ await new Promise((r) => setTimeout(r, delayMs));
597
+ if (!_connected) {
598
+ if (!await connectToSocket()) continue;
599
+ }
600
+ const retry = await doRequest();
601
+ if (!retry.error) {
602
+ _consecutiveFailures = 0;
603
+ return retry;
604
+ }
605
+ _consecutiveFailures++;
606
+ }
607
+ if (isDaemonTooYoung()) {
608
+ process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
609
+ `);
610
+ return { error: result.error };
560
611
  }
561
- spawnDaemon();
612
+ process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
613
+ `);
614
+ killAndRespawnDaemon();
615
+ const start = Date.now();
616
+ let delay2 = 200;
617
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
618
+ await new Promise((r) => setTimeout(r, delay2));
619
+ if (await connectToSocket()) break;
620
+ delay2 = Math.min(delay2 * 2, 3e3);
621
+ }
622
+ if (!_connected) return { error: "Daemon restart failed" };
623
+ const final = await doRequest();
624
+ if (!final.error) _consecutiveFailures = 0;
625
+ return final;
562
626
  }
563
627
  async function embedViaClient(text, priority = "high") {
564
628
  if (!_connected && !await connectEmbedDaemon()) return null;
565
629
  _requestCount++;
566
630
  if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
567
631
  const health = await pingDaemon();
568
- if (!health) {
632
+ if (!health && !isDaemonTooYoung()) {
569
633
  process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
570
634
  `);
571
635
  killAndRespawnDaemon();
572
636
  const start = Date.now();
573
- let delay2 = 200;
637
+ let d = 200;
574
638
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
575
- await new Promise((r) => setTimeout(r, delay2));
639
+ await new Promise((r) => setTimeout(r, d));
576
640
  if (await connectToSocket()) break;
577
- delay2 = Math.min(delay2 * 2, 3e3);
641
+ d = Math.min(d * 2, 3e3);
578
642
  }
579
643
  if (!_connected) return null;
580
644
  }
581
645
  }
582
- const result = await sendRequest([text], priority);
583
- if (!result.error && result.vectors?.[0]) return result.vectors[0];
584
- if (result.error) {
585
- process.stderr.write(`[exed-client] Embed failed (${result.error}) \u2014 attempting restart
586
- `);
587
- killAndRespawnDaemon();
588
- const start = Date.now();
589
- let delay2 = 200;
590
- while (Date.now() - start < CONNECT_TIMEOUT_MS) {
591
- await new Promise((r) => setTimeout(r, delay2));
592
- if (await connectToSocket()) break;
593
- delay2 = Math.min(delay2 * 2, 3e3);
594
- }
595
- if (!_connected) return null;
596
- const retry = await sendRequest([text], priority);
597
- if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
598
- process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
599
- `);
600
- }
601
- return null;
646
+ const result = await retryThenRestart(
647
+ () => sendRequest([text], priority),
648
+ "Embed"
649
+ );
650
+ return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
602
651
  }
603
652
  async function embedBatchViaClient(texts, priority = "high") {
604
653
  if (!_connected && !await connectEmbedDaemon()) return null;
605
654
  _requestCount++;
606
- const result = await sendRequest(texts, priority);
607
- if (!result.error && result.vectors) return result.vectors;
608
- if (result.error) {
609
- process.stderr.write(`[exed-client] Batch embed failed (${result.error}) \u2014 attempting restart
610
- `);
611
- killAndRespawnDaemon();
612
- const start = Date.now();
613
- let delay2 = 200;
614
- while (Date.now() - start < CONNECT_TIMEOUT_MS) {
615
- await new Promise((r) => setTimeout(r, delay2));
616
- if (await connectToSocket()) break;
617
- delay2 = Math.min(delay2 * 2, 3e3);
618
- }
619
- if (!_connected) return null;
620
- const retry = await sendRequest(texts, priority);
621
- if (!retry.error && retry.vectors) return retry.vectors;
622
- process.stderr.write(`[exed-client] Batch retry also failed: ${retry.error ?? "no vectors"}
623
- `);
624
- }
625
- return null;
655
+ const result = await retryThenRestart(
656
+ () => sendRequest(texts, priority),
657
+ "Batch embed"
658
+ );
659
+ return !result.error && result.vectors ? result.vectors : null;
626
660
  }
627
661
  function disconnectClient() {
628
662
  if (_socket) {
@@ -640,7 +674,7 @@ function disconnectClient() {
640
674
  function isClientConnected() {
641
675
  return _connected;
642
676
  }
643
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
677
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
644
678
  var init_exe_daemon_client = __esm({
645
679
  "src/lib/exe-daemon-client.ts"() {
646
680
  "use strict";
@@ -655,7 +689,11 @@ var init_exe_daemon_client = __esm({
655
689
  _connected = false;
656
690
  _buffer = "";
657
691
  _requestCount = 0;
692
+ _consecutiveFailures = 0;
658
693
  HEALTH_CHECK_INTERVAL = 100;
694
+ MAX_RETRIES_BEFORE_RESTART = 3;
695
+ RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
696
+ MIN_DAEMON_AGE_MS = 3e4;
659
697
  _pending = /* @__PURE__ */ new Map();
660
698
  MAX_BUFFER = 1e7;
661
699
  }
@@ -699,8 +737,8 @@ async function embedDirect(text) {
699
737
  const llamaCpp = await import("node-llama-cpp");
700
738
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
701
739
  const { existsSync: existsSync32 } = await import("fs");
702
- const path40 = await import("path");
703
- const modelPath = path40.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
740
+ const path41 = await import("path");
741
+ const modelPath = path41.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
704
742
  if (!existsSync32(modelPath)) {
705
743
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
706
744
  }
@@ -784,6 +822,118 @@ var init_db_retry = __esm({
784
822
  }
785
823
  });
786
824
 
825
+ // src/lib/runtime-table.ts
826
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
827
+ var init_runtime_table = __esm({
828
+ "src/lib/runtime-table.ts"() {
829
+ "use strict";
830
+ RUNTIME_TABLE = {
831
+ codex: {
832
+ binary: "codex",
833
+ launchMode: "interactive",
834
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
835
+ inlineFlag: "--no-alt-screen",
836
+ apiKeyEnv: "OPENAI_API_KEY",
837
+ defaultModel: "gpt-5.4"
838
+ },
839
+ opencode: {
840
+ binary: "opencode",
841
+ launchMode: "exec",
842
+ autoApproveFlag: "--dangerously-skip-permissions",
843
+ inlineFlag: "",
844
+ apiKeyEnv: "ANTHROPIC_API_KEY",
845
+ defaultModel: "anthropic/claude-sonnet-4-6"
846
+ }
847
+ };
848
+ DEFAULT_RUNTIME = "claude";
849
+ }
850
+ });
851
+
852
+ // src/lib/agent-config.ts
853
+ var agent_config_exports = {};
854
+ __export(agent_config_exports, {
855
+ AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
856
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
857
+ KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
858
+ RUNTIME_LABELS: () => RUNTIME_LABELS,
859
+ clearAgentRuntime: () => clearAgentRuntime,
860
+ getAgentRuntime: () => getAgentRuntime,
861
+ loadAgentConfig: () => loadAgentConfig,
862
+ saveAgentConfig: () => saveAgentConfig,
863
+ setAgentRuntime: () => setAgentRuntime
864
+ });
865
+ import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync3, mkdirSync } from "fs";
866
+ import path3 from "path";
867
+ function loadAgentConfig() {
868
+ if (!existsSync3(AGENT_CONFIG_PATH)) return {};
869
+ try {
870
+ return JSON.parse(readFileSync3(AGENT_CONFIG_PATH, "utf-8"));
871
+ } catch {
872
+ return {};
873
+ }
874
+ }
875
+ function saveAgentConfig(config2) {
876
+ const dir = path3.dirname(AGENT_CONFIG_PATH);
877
+ if (!existsSync3(dir)) mkdirSync(dir, { recursive: true });
878
+ writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
879
+ }
880
+ function getAgentRuntime(agentId) {
881
+ const config2 = loadAgentConfig();
882
+ const entry = config2[agentId];
883
+ if (entry) return entry;
884
+ const orgDefault = config2["default"];
885
+ if (orgDefault) return orgDefault;
886
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
887
+ }
888
+ function setAgentRuntime(agentId, runtime, model) {
889
+ const knownModels = KNOWN_RUNTIMES[runtime];
890
+ if (!knownModels) {
891
+ return {
892
+ ok: false,
893
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
894
+ };
895
+ }
896
+ if (!knownModels.includes(model)) {
897
+ return {
898
+ ok: false,
899
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
900
+ };
901
+ }
902
+ const config2 = loadAgentConfig();
903
+ config2[agentId] = { runtime, model };
904
+ saveAgentConfig(config2);
905
+ return { ok: true };
906
+ }
907
+ function clearAgentRuntime(agentId) {
908
+ const config2 = loadAgentConfig();
909
+ delete config2[agentId];
910
+ saveAgentConfig(config2);
911
+ }
912
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
913
+ var init_agent_config = __esm({
914
+ "src/lib/agent-config.ts"() {
915
+ "use strict";
916
+ init_config();
917
+ init_runtime_table();
918
+ AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
919
+ KNOWN_RUNTIMES = {
920
+ claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
921
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
922
+ opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
923
+ };
924
+ RUNTIME_LABELS = {
925
+ claude: "Claude Code (Anthropic)",
926
+ codex: "Codex (OpenAI)",
927
+ opencode: "OpenCode (open source)"
928
+ };
929
+ DEFAULT_MODELS = {
930
+ claude: "claude-opus-4",
931
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
932
+ opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
933
+ };
934
+ }
935
+ });
936
+
787
937
  // src/lib/employees.ts
788
938
  var employees_exports = {};
789
939
  __export(employees_exports, {
@@ -799,6 +949,7 @@ __export(employees_exports, {
799
949
  getEmployeeByRole: () => getEmployeeByRole,
800
950
  getEmployeeNamesByRole: () => getEmployeeNamesByRole,
801
951
  hasRole: () => hasRole,
952
+ hireEmployee: () => hireEmployee,
802
953
  isCoordinatorName: () => isCoordinatorName,
803
954
  isCoordinatorRole: () => isCoordinatorRole,
804
955
  isMultiInstance: () => isMultiInstance,
@@ -811,9 +962,9 @@ __export(employees_exports, {
811
962
  validateEmployeeName: () => validateEmployeeName
812
963
  });
813
964
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
814
- import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync } from "fs";
965
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync4, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
815
966
  import { execSync } from "child_process";
816
- import path3 from "path";
967
+ import path4 from "path";
817
968
  import os3 from "os";
818
969
  function normalizeRole(role) {
819
970
  return (role ?? "").trim().toLowerCase();
@@ -850,7 +1001,7 @@ function validateEmployeeName(name) {
850
1001
  return { valid: true };
851
1002
  }
852
1003
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
853
- if (!existsSync3(employeesPath)) {
1004
+ if (!existsSync4(employeesPath)) {
854
1005
  return [];
855
1006
  }
856
1007
  const raw = await readFile2(employeesPath, "utf-8");
@@ -861,13 +1012,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
861
1012
  }
862
1013
  }
863
1014
  async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
864
- await mkdir2(path3.dirname(employeesPath), { recursive: true });
1015
+ await mkdir2(path4.dirname(employeesPath), { recursive: true });
865
1016
  await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
866
1017
  }
867
1018
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
868
- if (!existsSync3(employeesPath)) return [];
1019
+ if (!existsSync4(employeesPath)) return [];
869
1020
  try {
870
- return JSON.parse(readFileSync3(employeesPath, "utf-8"));
1021
+ return JSON.parse(readFileSync4(employeesPath, "utf-8"));
871
1022
  } catch {
872
1023
  return [];
873
1024
  }
@@ -909,6 +1060,52 @@ function addEmployee(employees, employee) {
909
1060
  }
910
1061
  return [...employees, normalized];
911
1062
  }
1063
+ function appendToCoordinatorTeam(employee) {
1064
+ const coordinator = getCoordinatorEmployee(loadEmployeesSync());
1065
+ if (!coordinator) return;
1066
+ const idPath = path4.join(IDENTITY_DIR, `${coordinator.name}.md`);
1067
+ if (!existsSync4(idPath)) return;
1068
+ const content = readFileSync4(idPath, "utf-8");
1069
+ if (content.includes(`**${capitalize(employee.name)}`)) return;
1070
+ const teamMatch = content.match(TEAM_SECTION_RE);
1071
+ if (!teamMatch || teamMatch.index === void 0) return;
1072
+ const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
1073
+ const nextHeading = afterTeam.match(/\n## /);
1074
+ const entry = `
1075
+ **${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
1076
+ `;
1077
+ let updated;
1078
+ if (nextHeading && nextHeading.index !== void 0) {
1079
+ const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
1080
+ updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
1081
+ } else {
1082
+ updated = content.trimEnd() + "\n" + entry;
1083
+ }
1084
+ writeFileSync2(idPath, updated, "utf-8");
1085
+ }
1086
+ function capitalize(s) {
1087
+ return s.charAt(0).toUpperCase() + s.slice(1);
1088
+ }
1089
+ async function hireEmployee(employee) {
1090
+ const employees = await loadEmployees();
1091
+ const updated = addEmployee(employees, employee);
1092
+ await saveEmployees(updated);
1093
+ try {
1094
+ appendToCoordinatorTeam(employee);
1095
+ } catch {
1096
+ }
1097
+ try {
1098
+ const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
1099
+ const config2 = loadAgentConfig2();
1100
+ const name = employee.name.toLowerCase();
1101
+ if (!config2[name] && config2["default"]) {
1102
+ config2[name] = { ...config2["default"] };
1103
+ saveAgentConfig2(config2);
1104
+ }
1105
+ } catch {
1106
+ }
1107
+ return updated;
1108
+ }
912
1109
  async function normalizeRosterCase(rosterPath) {
913
1110
  const employees = await loadEmployees(rosterPath);
914
1111
  let changed = false;
@@ -918,14 +1115,14 @@ async function normalizeRosterCase(rosterPath) {
918
1115
  emp.name = emp.name.toLowerCase();
919
1116
  changed = true;
920
1117
  try {
921
- const identityDir = path3.join(os3.homedir(), ".exe-os", "identity");
922
- const oldPath = path3.join(identityDir, `${oldName}.md`);
923
- const newPath = path3.join(identityDir, `${emp.name}.md`);
924
- if (existsSync3(oldPath) && !existsSync3(newPath)) {
1118
+ const identityDir = path4.join(os3.homedir(), ".exe-os", "identity");
1119
+ const oldPath = path4.join(identityDir, `${oldName}.md`);
1120
+ const newPath = path4.join(identityDir, `${emp.name}.md`);
1121
+ if (existsSync4(oldPath) && !existsSync4(newPath)) {
925
1122
  renameSync2(oldPath, newPath);
926
- } else if (existsSync3(oldPath) && oldPath !== newPath) {
927
- const content = readFileSync3(oldPath, "utf-8");
928
- writeFileSync(newPath, content, "utf-8");
1123
+ } else if (existsSync4(oldPath) && oldPath !== newPath) {
1124
+ const content = readFileSync4(oldPath, "utf-8");
1125
+ writeFileSync2(newPath, content, "utf-8");
929
1126
  if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
930
1127
  unlinkSync2(oldPath);
931
1128
  }
@@ -955,7 +1152,7 @@ function registerBinSymlinks(name) {
955
1152
  errors.push("Could not find 'exe-os' in PATH");
956
1153
  return { created, skipped, errors };
957
1154
  }
958
- const binDir = path3.dirname(exeBinPath);
1155
+ const binDir = path4.dirname(exeBinPath);
959
1156
  let target;
960
1157
  try {
961
1158
  target = readlinkSync(exeBinPath);
@@ -965,29 +1162,615 @@ function registerBinSymlinks(name) {
965
1162
  }
966
1163
  for (const suffix of ["", "-opencode"]) {
967
1164
  const linkName = `${name}${suffix}`;
968
- const linkPath = path3.join(binDir, linkName);
969
- if (existsSync3(linkPath)) {
1165
+ const linkPath = path4.join(binDir, linkName);
1166
+ if (existsSync4(linkPath)) {
970
1167
  skipped.push(linkName);
971
1168
  continue;
972
1169
  }
973
- try {
974
- symlinkSync(target, linkPath);
975
- created.push(linkName);
976
- } catch (err) {
977
- errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
1170
+ try {
1171
+ symlinkSync(target, linkPath);
1172
+ created.push(linkName);
1173
+ } catch (err) {
1174
+ errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
1175
+ }
1176
+ }
1177
+ return { created, skipped, errors };
1178
+ }
1179
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
1180
+ var init_employees = __esm({
1181
+ "src/lib/employees.ts"() {
1182
+ "use strict";
1183
+ init_config();
1184
+ EMPLOYEES_PATH = path4.join(EXE_AI_DIR, "exe-employees.json");
1185
+ DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
1186
+ COORDINATOR_ROLE = "COO";
1187
+ MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
1188
+ IDENTITY_DIR = path4.join(EXE_AI_DIR, "identity");
1189
+ TEAM_SECTION_RE = /^## Team\b.*$/m;
1190
+ }
1191
+ });
1192
+
1193
+ // src/lib/database-adapter.ts
1194
+ import os4 from "os";
1195
+ import path5 from "path";
1196
+ import { createRequire } from "module";
1197
+ import { pathToFileURL } from "url";
1198
+ function quotedIdentifier(identifier) {
1199
+ return `"${identifier.replace(/"/g, '""')}"`;
1200
+ }
1201
+ function unqualifiedTableName(name) {
1202
+ const raw = name.trim().replace(/^"|"$/g, "");
1203
+ const parts = raw.split(".");
1204
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
1205
+ }
1206
+ function stripTrailingSemicolon(sql) {
1207
+ return sql.trim().replace(/;+\s*$/u, "");
1208
+ }
1209
+ function appendClause(sql, clause) {
1210
+ const trimmed = stripTrailingSemicolon(sql);
1211
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
1212
+ if (!returningMatch) {
1213
+ return `${trimmed}${clause}`;
1214
+ }
1215
+ const idx = returningMatch.index;
1216
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
1217
+ }
1218
+ function normalizeStatement(stmt) {
1219
+ if (typeof stmt === "string") {
1220
+ return { kind: "positional", sql: stmt, args: [] };
1221
+ }
1222
+ const sql = stmt.sql;
1223
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
1224
+ return { kind: "positional", sql, args: stmt.args ?? [] };
1225
+ }
1226
+ return { kind: "named", sql, args: stmt.args };
1227
+ }
1228
+ function rewriteBooleanLiterals(sql) {
1229
+ let out = sql;
1230
+ for (const column of BOOLEAN_COLUMN_NAMES) {
1231
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
1232
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
1233
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
1234
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
1235
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
1236
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
1237
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
1238
+ }
1239
+ return out;
1240
+ }
1241
+ function rewriteInsertOrIgnore(sql) {
1242
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
1243
+ return sql;
1244
+ }
1245
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
1246
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
1247
+ }
1248
+ function rewriteInsertOrReplace(sql) {
1249
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
1250
+ if (!match) {
1251
+ return sql;
1252
+ }
1253
+ const rawTable = match[1];
1254
+ const rawColumns = match[2];
1255
+ const remainder = match[3];
1256
+ const tableName = unqualifiedTableName(rawTable);
1257
+ const conflictKeys = UPSERT_KEYS[tableName];
1258
+ if (!conflictKeys?.length) {
1259
+ return sql;
1260
+ }
1261
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
1262
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
1263
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
1264
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
1265
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
1266
+ }
1267
+ function rewriteSql(sql) {
1268
+ let out = sql;
1269
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
1270
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
1271
+ out = rewriteBooleanLiterals(out);
1272
+ out = rewriteInsertOrReplace(out);
1273
+ out = rewriteInsertOrIgnore(out);
1274
+ return stripTrailingSemicolon(out);
1275
+ }
1276
+ function toBoolean(value) {
1277
+ if (value === null || value === void 0) return value;
1278
+ if (typeof value === "boolean") return value;
1279
+ if (typeof value === "number") return value !== 0;
1280
+ if (typeof value === "bigint") return value !== 0n;
1281
+ if (typeof value === "string") {
1282
+ const normalized = value.trim().toLowerCase();
1283
+ if (normalized === "0" || normalized === "false") return false;
1284
+ if (normalized === "1" || normalized === "true") return true;
1285
+ }
1286
+ return Boolean(value);
1287
+ }
1288
+ function countQuestionMarks(sql, end) {
1289
+ let count = 0;
1290
+ let inSingle = false;
1291
+ let inDouble = false;
1292
+ let inLineComment = false;
1293
+ let inBlockComment = false;
1294
+ for (let i = 0; i < end; i++) {
1295
+ const ch = sql[i];
1296
+ const next = sql[i + 1];
1297
+ if (inLineComment) {
1298
+ if (ch === "\n") inLineComment = false;
1299
+ continue;
1300
+ }
1301
+ if (inBlockComment) {
1302
+ if (ch === "*" && next === "/") {
1303
+ inBlockComment = false;
1304
+ i += 1;
1305
+ }
1306
+ continue;
1307
+ }
1308
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1309
+ inLineComment = true;
1310
+ i += 1;
1311
+ continue;
1312
+ }
1313
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1314
+ inBlockComment = true;
1315
+ i += 1;
1316
+ continue;
1317
+ }
1318
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1319
+ inSingle = !inSingle;
1320
+ continue;
1321
+ }
1322
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1323
+ inDouble = !inDouble;
1324
+ continue;
1325
+ }
1326
+ if (!inSingle && !inDouble && ch === "?") {
1327
+ count += 1;
1328
+ }
1329
+ }
1330
+ return count;
1331
+ }
1332
+ function findBooleanPlaceholderIndexes(sql) {
1333
+ const indexes = /* @__PURE__ */ new Set();
1334
+ for (const column of BOOLEAN_COLUMN_NAMES) {
1335
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
1336
+ for (const match of sql.matchAll(pattern)) {
1337
+ const matchText = match[0];
1338
+ const qIndex = match.index + matchText.lastIndexOf("?");
1339
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
1340
+ }
1341
+ }
1342
+ return indexes;
1343
+ }
1344
+ function coerceInsertBooleanArgs(sql, args) {
1345
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
1346
+ if (!match) return;
1347
+ const rawTable = match[1];
1348
+ const rawColumns = match[2];
1349
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
1350
+ if (!boolColumns?.size) return;
1351
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
1352
+ for (const [index, column] of columns.entries()) {
1353
+ if (boolColumns.has(column) && index < args.length) {
1354
+ args[index] = toBoolean(args[index]);
1355
+ }
1356
+ }
1357
+ }
1358
+ function coerceUpdateBooleanArgs(sql, args) {
1359
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
1360
+ if (!match) return;
1361
+ const rawTable = match[1];
1362
+ const setClause = match[2];
1363
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
1364
+ if (!boolColumns?.size) return;
1365
+ const assignments = setClause.split(",");
1366
+ let placeholderIndex = 0;
1367
+ for (const assignment of assignments) {
1368
+ if (!assignment.includes("?")) continue;
1369
+ placeholderIndex += 1;
1370
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
1371
+ if (colMatch && boolColumns.has(colMatch[1])) {
1372
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
1373
+ }
1374
+ }
1375
+ }
1376
+ function coerceBooleanArgs(sql, args) {
1377
+ const nextArgs = [...args];
1378
+ coerceInsertBooleanArgs(sql, nextArgs);
1379
+ coerceUpdateBooleanArgs(sql, nextArgs);
1380
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
1381
+ for (const index of placeholderIndexes) {
1382
+ if (index > 0 && index <= nextArgs.length) {
1383
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
1384
+ }
1385
+ }
1386
+ return nextArgs;
1387
+ }
1388
+ function convertQuestionMarksToDollarParams(sql) {
1389
+ let out = "";
1390
+ let placeholder = 0;
1391
+ let inSingle = false;
1392
+ let inDouble = false;
1393
+ let inLineComment = false;
1394
+ let inBlockComment = false;
1395
+ for (let i = 0; i < sql.length; i++) {
1396
+ const ch = sql[i];
1397
+ const next = sql[i + 1];
1398
+ if (inLineComment) {
1399
+ out += ch;
1400
+ if (ch === "\n") inLineComment = false;
1401
+ continue;
1402
+ }
1403
+ if (inBlockComment) {
1404
+ out += ch;
1405
+ if (ch === "*" && next === "/") {
1406
+ out += next;
1407
+ inBlockComment = false;
1408
+ i += 1;
1409
+ }
1410
+ continue;
1411
+ }
1412
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1413
+ out += ch + next;
1414
+ inLineComment = true;
1415
+ i += 1;
1416
+ continue;
1417
+ }
1418
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1419
+ out += ch + next;
1420
+ inBlockComment = true;
1421
+ i += 1;
1422
+ continue;
1423
+ }
1424
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1425
+ inSingle = !inSingle;
1426
+ out += ch;
1427
+ continue;
1428
+ }
1429
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1430
+ inDouble = !inDouble;
1431
+ out += ch;
1432
+ continue;
1433
+ }
1434
+ if (!inSingle && !inDouble && ch === "?") {
1435
+ placeholder += 1;
1436
+ out += `$${placeholder}`;
1437
+ continue;
1438
+ }
1439
+ out += ch;
1440
+ }
1441
+ return out;
1442
+ }
1443
+ function translateStatementForPostgres(stmt) {
1444
+ const normalized = normalizeStatement(stmt);
1445
+ if (normalized.kind === "named") {
1446
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
1447
+ }
1448
+ const rewrittenSql = rewriteSql(normalized.sql);
1449
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
1450
+ return {
1451
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
1452
+ args: coercedArgs
1453
+ };
1454
+ }
1455
+ function shouldBypassPostgres(stmt) {
1456
+ const normalized = normalizeStatement(stmt);
1457
+ if (normalized.kind === "named") {
1458
+ return true;
1459
+ }
1460
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
1461
+ }
1462
+ function shouldFallbackOnError(error) {
1463
+ const message = error instanceof Error ? error.message : String(error);
1464
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
1465
+ }
1466
+ function isReadQuery(sql) {
1467
+ const trimmed = sql.trimStart();
1468
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
1469
+ }
1470
+ function buildRow(row, columns) {
1471
+ const values = columns.map((column) => row[column]);
1472
+ return Object.assign(values, row);
1473
+ }
1474
+ function buildResultSet(rows, rowsAffected = 0) {
1475
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
1476
+ const resultRows = rows.map((row) => buildRow(row, columns));
1477
+ return {
1478
+ columns,
1479
+ columnTypes: columns.map(() => ""),
1480
+ rows: resultRows,
1481
+ rowsAffected,
1482
+ lastInsertRowid: void 0,
1483
+ toJSON() {
1484
+ return {
1485
+ columns,
1486
+ columnTypes: columns.map(() => ""),
1487
+ rows,
1488
+ rowsAffected,
1489
+ lastInsertRowid: void 0
1490
+ };
1491
+ }
1492
+ };
1493
+ }
1494
+ async function loadPrismaClient() {
1495
+ if (!prismaClientPromise) {
1496
+ prismaClientPromise = (async () => {
1497
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
1498
+ if (explicitPath) {
1499
+ const module2 = await import(pathToFileURL(explicitPath).href);
1500
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
1501
+ if (!PrismaClient2) {
1502
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
1503
+ }
1504
+ return new PrismaClient2();
1505
+ }
1506
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path5.join(os4.homedir(), "exe-db");
1507
+ const requireFromExeDb = createRequire(path5.join(exeDbRoot, "package.json"));
1508
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
1509
+ const module = await import(pathToFileURL(prismaEntry).href);
1510
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
1511
+ if (!PrismaClient) {
1512
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
1513
+ }
1514
+ return new PrismaClient();
1515
+ })();
1516
+ }
1517
+ return prismaClientPromise;
1518
+ }
1519
+ async function ensureCompatibilityViews(prisma) {
1520
+ if (!compatibilityBootstrapPromise) {
1521
+ compatibilityBootstrapPromise = (async () => {
1522
+ for (const mapping of VIEW_MAPPINGS) {
1523
+ const relation = mapping.source.replace(/"/g, "");
1524
+ const rows = await prisma.$queryRawUnsafe(
1525
+ "SELECT to_regclass($1) AS regclass",
1526
+ relation
1527
+ );
1528
+ if (!rows[0]?.regclass) {
1529
+ continue;
1530
+ }
1531
+ await prisma.$executeRawUnsafe(
1532
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
1533
+ );
1534
+ }
1535
+ })();
1536
+ }
1537
+ return compatibilityBootstrapPromise;
1538
+ }
1539
+ async function executeOnPrisma(executor, stmt) {
1540
+ const translated = translateStatementForPostgres(stmt);
1541
+ if (isReadQuery(translated.sql)) {
1542
+ const rows = await executor.$queryRawUnsafe(
1543
+ translated.sql,
1544
+ ...translated.args
1545
+ );
1546
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
1547
+ }
1548
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
1549
+ return buildResultSet([], rowsAffected);
1550
+ }
1551
+ function splitSqlStatements(sql) {
1552
+ const parts = [];
1553
+ let current = "";
1554
+ let inSingle = false;
1555
+ let inDouble = false;
1556
+ let inLineComment = false;
1557
+ let inBlockComment = false;
1558
+ for (let i = 0; i < sql.length; i++) {
1559
+ const ch = sql[i];
1560
+ const next = sql[i + 1];
1561
+ if (inLineComment) {
1562
+ current += ch;
1563
+ if (ch === "\n") inLineComment = false;
1564
+ continue;
1565
+ }
1566
+ if (inBlockComment) {
1567
+ current += ch;
1568
+ if (ch === "*" && next === "/") {
1569
+ current += next;
1570
+ inBlockComment = false;
1571
+ i += 1;
1572
+ }
1573
+ continue;
1574
+ }
1575
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1576
+ current += ch + next;
1577
+ inLineComment = true;
1578
+ i += 1;
1579
+ continue;
1580
+ }
1581
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1582
+ current += ch + next;
1583
+ inBlockComment = true;
1584
+ i += 1;
1585
+ continue;
1586
+ }
1587
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1588
+ inSingle = !inSingle;
1589
+ current += ch;
1590
+ continue;
1591
+ }
1592
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1593
+ inDouble = !inDouble;
1594
+ current += ch;
1595
+ continue;
1596
+ }
1597
+ if (!inSingle && !inDouble && ch === ";") {
1598
+ if (current.trim()) {
1599
+ parts.push(current.trim());
1600
+ }
1601
+ current = "";
1602
+ continue;
978
1603
  }
1604
+ current += ch;
979
1605
  }
980
- return { created, skipped, errors };
1606
+ if (current.trim()) {
1607
+ parts.push(current.trim());
1608
+ }
1609
+ return parts;
981
1610
  }
982
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
983
- var init_employees = __esm({
984
- "src/lib/employees.ts"() {
1611
+ async function createPrismaDbAdapter(fallbackClient) {
1612
+ const prisma = await loadPrismaClient();
1613
+ await ensureCompatibilityViews(prisma);
1614
+ let closed = false;
1615
+ let adapter;
1616
+ const fallbackExecute = async (stmt, error) => {
1617
+ if (!fallbackClient) {
1618
+ if (error) throw error;
1619
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
1620
+ }
1621
+ if (error) {
1622
+ process.stderr.write(
1623
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
1624
+ `
1625
+ );
1626
+ }
1627
+ return fallbackClient.execute(stmt);
1628
+ };
1629
+ adapter = {
1630
+ async execute(stmt) {
1631
+ if (shouldBypassPostgres(stmt)) {
1632
+ return fallbackExecute(stmt);
1633
+ }
1634
+ try {
1635
+ return await executeOnPrisma(prisma, stmt);
1636
+ } catch (error) {
1637
+ if (shouldFallbackOnError(error)) {
1638
+ return fallbackExecute(stmt, error);
1639
+ }
1640
+ throw error;
1641
+ }
1642
+ },
1643
+ async batch(stmts, mode) {
1644
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
1645
+ if (!fallbackClient) {
1646
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
1647
+ }
1648
+ return fallbackClient.batch(stmts, mode);
1649
+ }
1650
+ try {
1651
+ if (prisma.$transaction) {
1652
+ return await prisma.$transaction(async (tx) => {
1653
+ const results2 = [];
1654
+ for (const stmt of stmts) {
1655
+ results2.push(await executeOnPrisma(tx, stmt));
1656
+ }
1657
+ return results2;
1658
+ });
1659
+ }
1660
+ const results = [];
1661
+ for (const stmt of stmts) {
1662
+ results.push(await executeOnPrisma(prisma, stmt));
1663
+ }
1664
+ return results;
1665
+ } catch (error) {
1666
+ if (fallbackClient && shouldFallbackOnError(error)) {
1667
+ process.stderr.write(
1668
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
1669
+ `
1670
+ );
1671
+ return fallbackClient.batch(stmts, mode);
1672
+ }
1673
+ throw error;
1674
+ }
1675
+ },
1676
+ async migrate(stmts) {
1677
+ if (fallbackClient) {
1678
+ return fallbackClient.migrate(stmts);
1679
+ }
1680
+ return adapter.batch(stmts, "deferred");
1681
+ },
1682
+ async transaction(mode) {
1683
+ if (!fallbackClient) {
1684
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
1685
+ }
1686
+ return fallbackClient.transaction(mode);
1687
+ },
1688
+ async executeMultiple(sql) {
1689
+ if (fallbackClient && shouldBypassPostgres(sql)) {
1690
+ return fallbackClient.executeMultiple(sql);
1691
+ }
1692
+ for (const statement of splitSqlStatements(sql)) {
1693
+ await adapter.execute(statement);
1694
+ }
1695
+ },
1696
+ async sync() {
1697
+ if (fallbackClient) {
1698
+ return fallbackClient.sync();
1699
+ }
1700
+ return { frame_no: 0, frames_synced: 0 };
1701
+ },
1702
+ close() {
1703
+ closed = true;
1704
+ prismaClientPromise = null;
1705
+ compatibilityBootstrapPromise = null;
1706
+ void prisma.$disconnect?.();
1707
+ },
1708
+ get closed() {
1709
+ return closed;
1710
+ },
1711
+ get protocol() {
1712
+ return "prisma-postgres";
1713
+ }
1714
+ };
1715
+ return adapter;
1716
+ }
1717
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
1718
+ var init_database_adapter = __esm({
1719
+ "src/lib/database-adapter.ts"() {
985
1720
  "use strict";
986
- init_config();
987
- EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
988
- DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
989
- COORDINATOR_ROLE = "COO";
990
- MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
1721
+ VIEW_MAPPINGS = [
1722
+ { view: "memories", source: "memory.memory_records" },
1723
+ { view: "tasks", source: "memory.tasks" },
1724
+ { view: "behaviors", source: "memory.behaviors" },
1725
+ { view: "entities", source: "memory.entities" },
1726
+ { view: "relationships", source: "memory.relationships" },
1727
+ { view: "entity_memories", source: "memory.entity_memories" },
1728
+ { view: "entity_aliases", source: "memory.entity_aliases" },
1729
+ { view: "notifications", source: "memory.notifications" },
1730
+ { view: "messages", source: "memory.messages" },
1731
+ { view: "users", source: "wiki.users" },
1732
+ { view: "workspaces", source: "wiki.workspaces" },
1733
+ { view: "workspace_users", source: "wiki.workspace_users" },
1734
+ { view: "documents", source: "wiki.workspace_documents" },
1735
+ { view: "chats", source: "wiki.workspace_chats" }
1736
+ ];
1737
+ UPSERT_KEYS = {
1738
+ memories: ["id"],
1739
+ tasks: ["id"],
1740
+ behaviors: ["id"],
1741
+ entities: ["id"],
1742
+ relationships: ["id"],
1743
+ entity_aliases: ["alias"],
1744
+ notifications: ["id"],
1745
+ messages: ["id"],
1746
+ users: ["id"],
1747
+ workspaces: ["id"],
1748
+ workspace_users: ["id"],
1749
+ documents: ["id"],
1750
+ chats: ["id"]
1751
+ };
1752
+ BOOLEAN_COLUMNS_BY_TABLE = {
1753
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
1754
+ behaviors: /* @__PURE__ */ new Set(["active"]),
1755
+ notifications: /* @__PURE__ */ new Set(["read"]),
1756
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
1757
+ };
1758
+ BOOLEAN_COLUMN_NAMES = new Set(
1759
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
1760
+ );
1761
+ IMMEDIATE_FALLBACK_PATTERNS = [
1762
+ /\bPRAGMA\b/i,
1763
+ /\bsqlite_master\b/i,
1764
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
1765
+ /\bMATCH\b/i,
1766
+ /\bvector_distance_cos\s*\(/i,
1767
+ /\bjson_extract\s*\(/i,
1768
+ /\bjulianday\s*\(/i,
1769
+ /\bstrftime\s*\(/i,
1770
+ /\blast_insert_rowid\s*\(/i
1771
+ ];
1772
+ prismaClientPromise = null;
1773
+ compatibilityBootstrapPromise = null;
991
1774
  }
992
1775
  });
993
1776
 
@@ -1061,7 +1844,7 @@ __export(db_daemon_client_exports, {
1061
1844
  createDaemonDbClient: () => createDaemonDbClient,
1062
1845
  initDaemonDbClient: () => initDaemonDbClient
1063
1846
  });
1064
- function normalizeStatement(stmt) {
1847
+ function normalizeStatement2(stmt) {
1065
1848
  if (typeof stmt === "string") {
1066
1849
  return { sql: stmt, args: [] };
1067
1850
  }
@@ -1085,7 +1868,7 @@ function createDaemonDbClient(fallbackClient) {
1085
1868
  if (!_useDaemon || !isClientConnected()) {
1086
1869
  return fallbackClient.execute(stmt);
1087
1870
  }
1088
- const { sql, args } = normalizeStatement(stmt);
1871
+ const { sql, args } = normalizeStatement2(stmt);
1089
1872
  const response = await sendDaemonRequest({
1090
1873
  type: "db-execute",
1091
1874
  sql,
@@ -1110,7 +1893,7 @@ function createDaemonDbClient(fallbackClient) {
1110
1893
  if (!_useDaemon || !isClientConnected()) {
1111
1894
  return fallbackClient.batch(stmts, mode);
1112
1895
  }
1113
- const statements = stmts.map(normalizeStatement);
1896
+ const statements = stmts.map(normalizeStatement2);
1114
1897
  const response = await sendDaemonRequest({
1115
1898
  type: "db-batch",
1116
1899
  statements,
@@ -1205,6 +1988,18 @@ __export(database_exports, {
1205
1988
  });
1206
1989
  import { createClient } from "@libsql/client";
1207
1990
  async function initDatabase(config2) {
1991
+ if (_walCheckpointTimer) {
1992
+ clearInterval(_walCheckpointTimer);
1993
+ _walCheckpointTimer = null;
1994
+ }
1995
+ if (_daemonClient) {
1996
+ _daemonClient.close();
1997
+ _daemonClient = null;
1998
+ }
1999
+ if (_adapterClient && _adapterClient !== _resilientClient) {
2000
+ _adapterClient.close();
2001
+ }
2002
+ _adapterClient = null;
1208
2003
  if (_client) {
1209
2004
  _client.close();
1210
2005
  _client = null;
@@ -1218,6 +2013,7 @@ async function initDatabase(config2) {
1218
2013
  }
1219
2014
  _client = createClient(opts);
1220
2015
  _resilientClient = wrapWithRetry(_client);
2016
+ _adapterClient = _resilientClient;
1221
2017
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
1222
2018
  });
1223
2019
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -1228,14 +2024,20 @@ async function initDatabase(config2) {
1228
2024
  });
1229
2025
  }, 3e4);
1230
2026
  _walCheckpointTimer.unref();
2027
+ if (process.env.DATABASE_URL) {
2028
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
2029
+ }
1231
2030
  }
1232
2031
  function isInitialized() {
1233
- return _client !== null;
2032
+ return _adapterClient !== null || _client !== null;
1234
2033
  }
1235
2034
  function getClient() {
1236
- if (!_resilientClient) {
2035
+ if (!_adapterClient) {
1237
2036
  throw new Error("Database client not initialized. Call initDatabase() first.");
1238
2037
  }
2038
+ if (process.env.DATABASE_URL) {
2039
+ return _adapterClient;
2040
+ }
1239
2041
  if (process.env.EXE_IS_DAEMON === "1") {
1240
2042
  return _resilientClient;
1241
2043
  }
@@ -1245,6 +2047,7 @@ function getClient() {
1245
2047
  return _resilientClient;
1246
2048
  }
1247
2049
  async function initDaemonClient() {
2050
+ if (process.env.DATABASE_URL) return;
1248
2051
  if (process.env.EXE_IS_DAEMON === "1") return;
1249
2052
  if (!_resilientClient) return;
1250
2053
  try {
@@ -2189,26 +2992,36 @@ async function ensureSchema() {
2189
2992
  }
2190
2993
  }
2191
2994
  async function disposeDatabase() {
2995
+ if (_walCheckpointTimer) {
2996
+ clearInterval(_walCheckpointTimer);
2997
+ _walCheckpointTimer = null;
2998
+ }
2192
2999
  if (_daemonClient) {
2193
3000
  _daemonClient.close();
2194
3001
  _daemonClient = null;
2195
3002
  }
3003
+ if (_adapterClient && _adapterClient !== _resilientClient) {
3004
+ _adapterClient.close();
3005
+ }
3006
+ _adapterClient = null;
2196
3007
  if (_client) {
2197
3008
  _client.close();
2198
3009
  _client = null;
2199
3010
  _resilientClient = null;
2200
3011
  }
2201
3012
  }
2202
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
3013
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
2203
3014
  var init_database = __esm({
2204
3015
  "src/lib/database.ts"() {
2205
3016
  "use strict";
2206
3017
  init_db_retry();
2207
3018
  init_employees();
3019
+ init_database_adapter();
2208
3020
  _client = null;
2209
3021
  _resilientClient = null;
2210
3022
  _walCheckpointTimer = null;
2211
3023
  _daemonClient = null;
3024
+ _adapterClient = null;
2212
3025
  initTurso = initDatabase;
2213
3026
  disposeTurso = disposeDatabase;
2214
3027
  }
@@ -2224,14 +3037,14 @@ __export(keychain_exports, {
2224
3037
  setMasterKey: () => setMasterKey
2225
3038
  });
2226
3039
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
2227
- import { existsSync as existsSync4 } from "fs";
2228
- import path4 from "path";
2229
- import os4 from "os";
3040
+ import { existsSync as existsSync5 } from "fs";
3041
+ import path6 from "path";
3042
+ import os5 from "os";
2230
3043
  function getKeyDir() {
2231
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path4.join(os4.homedir(), ".exe-os");
3044
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
2232
3045
  }
2233
3046
  function getKeyPath() {
2234
- return path4.join(getKeyDir(), "master.key");
3047
+ return path6.join(getKeyDir(), "master.key");
2235
3048
  }
2236
3049
  async function tryKeytar() {
2237
3050
  try {
@@ -2252,9 +3065,9 @@ async function getMasterKey() {
2252
3065
  }
2253
3066
  }
2254
3067
  const keyPath = getKeyPath();
2255
- if (!existsSync4(keyPath)) {
3068
+ if (!existsSync5(keyPath)) {
2256
3069
  process.stderr.write(
2257
- `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
3070
+ `[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2258
3071
  `
2259
3072
  );
2260
3073
  return null;
@@ -2295,7 +3108,7 @@ async function deleteMasterKey() {
2295
3108
  }
2296
3109
  }
2297
3110
  const keyPath = getKeyPath();
2298
- if (existsSync4(keyPath)) {
3111
+ if (existsSync5(keyPath)) {
2299
3112
  await unlink(keyPath);
2300
3113
  }
2301
3114
  }
@@ -2405,13 +3218,13 @@ __export(shard_manager_exports, {
2405
3218
  listShards: () => listShards,
2406
3219
  shardExists: () => shardExists
2407
3220
  });
2408
- import path5 from "path";
2409
- import { existsSync as existsSync5, mkdirSync, readdirSync } from "fs";
3221
+ import path7 from "path";
3222
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync } from "fs";
2410
3223
  import { createClient as createClient2 } from "@libsql/client";
2411
3224
  function initShardManager(encryptionKey) {
2412
3225
  _encryptionKey = encryptionKey;
2413
- if (!existsSync5(SHARDS_DIR)) {
2414
- mkdirSync(SHARDS_DIR, { recursive: true });
3226
+ if (!existsSync6(SHARDS_DIR)) {
3227
+ mkdirSync2(SHARDS_DIR, { recursive: true });
2415
3228
  }
2416
3229
  _shardingEnabled = true;
2417
3230
  }
@@ -2431,7 +3244,7 @@ function getShardClient(projectName) {
2431
3244
  }
2432
3245
  const cached = _shards.get(safeName);
2433
3246
  if (cached) return cached;
2434
- const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
3247
+ const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
2435
3248
  const client = createClient2({
2436
3249
  url: `file:${dbPath}`,
2437
3250
  encryptionKey: _encryptionKey
@@ -2441,10 +3254,10 @@ function getShardClient(projectName) {
2441
3254
  }
2442
3255
  function shardExists(projectName) {
2443
3256
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
2444
- return existsSync5(path5.join(SHARDS_DIR, `${safeName}.db`));
3257
+ return existsSync6(path7.join(SHARDS_DIR, `${safeName}.db`));
2445
3258
  }
2446
3259
  function listShards() {
2447
- if (!existsSync5(SHARDS_DIR)) return [];
3260
+ if (!existsSync6(SHARDS_DIR)) return [];
2448
3261
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
2449
3262
  }
2450
3263
  async function ensureShardSchema(client) {
@@ -2518,7 +3331,23 @@ async function ensureShardSchema(client) {
2518
3331
  // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
2519
3332
  "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
2520
3333
  "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
2521
- "ALTER TABLE memories ADD COLUMN trajectory TEXT"
3334
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT",
3335
+ // Metadata enrichment columns (must match database.ts)
3336
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
3337
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
3338
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
3339
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
3340
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
3341
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
3342
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
3343
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
3344
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
3345
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
3346
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
3347
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
3348
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
3349
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
3350
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
2522
3351
  ]) {
2523
3352
  try {
2524
3353
  await client.execute(col);
@@ -2630,7 +3459,7 @@ var init_shard_manager = __esm({
2630
3459
  "src/lib/shard-manager.ts"() {
2631
3460
  "use strict";
2632
3461
  init_config();
2633
- SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
3462
+ SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
2634
3463
  _shards = /* @__PURE__ */ new Map();
2635
3464
  _encryptionKey = null;
2636
3465
  _shardingEnabled = false;
@@ -3493,8 +4322,8 @@ __export(reranker_exports, {
3493
4322
  rerankWithContext: () => rerankWithContext,
3494
4323
  rerankWithScores: () => rerankWithScores
3495
4324
  });
3496
- import path6 from "path";
3497
- import { existsSync as existsSync6 } from "fs";
4325
+ import path8 from "path";
4326
+ import { existsSync as existsSync7 } from "fs";
3498
4327
  function resetIdleTimer() {
3499
4328
  if (_idleTimer) clearTimeout(_idleTimer);
3500
4329
  _idleTimer = setTimeout(() => {
@@ -3505,18 +4334,18 @@ function resetIdleTimer() {
3505
4334
  }
3506
4335
  }
3507
4336
  function isRerankerAvailable() {
3508
- return existsSync6(path6.join(MODELS_DIR, RERANKER_MODEL_FILE));
4337
+ return existsSync7(path8.join(MODELS_DIR, RERANKER_MODEL_FILE));
3509
4338
  }
3510
4339
  function getRerankerModelPath() {
3511
- return path6.join(MODELS_DIR, RERANKER_MODEL_FILE);
4340
+ return path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
3512
4341
  }
3513
4342
  async function ensureLoaded() {
3514
4343
  if (_rerankerContext) {
3515
4344
  resetIdleTimer();
3516
4345
  return;
3517
4346
  }
3518
- const modelPath = path6.join(MODELS_DIR, RERANKER_MODEL_FILE);
3519
- if (!existsSync6(modelPath)) {
4347
+ const modelPath = path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
4348
+ if (!existsSync7(modelPath)) {
3520
4349
  throw new Error(
3521
4350
  `Reranker model not found at ${modelPath}. Run /exe-setup to download it.`
3522
4351
  );
@@ -3619,7 +4448,7 @@ __export(project_name_exports, {
3619
4448
  getProjectName: () => getProjectName
3620
4449
  });
3621
4450
  import { execSync as execSync2 } from "child_process";
3622
- import path7 from "path";
4451
+ import path9 from "path";
3623
4452
  function getProjectName(cwd) {
3624
4453
  const dir = cwd ?? process.cwd();
3625
4454
  if (_cached && _cachedCwd === dir) return _cached;
@@ -3632,7 +4461,7 @@ function getProjectName(cwd) {
3632
4461
  timeout: 2e3,
3633
4462
  stdio: ["pipe", "pipe", "pipe"]
3634
4463
  }).trim();
3635
- repoRoot = path7.dirname(gitCommonDir);
4464
+ repoRoot = path9.dirname(gitCommonDir);
3636
4465
  } catch {
3637
4466
  repoRoot = execSync2("git rev-parse --show-toplevel", {
3638
4467
  cwd: dir,
@@ -3641,11 +4470,11 @@ function getProjectName(cwd) {
3641
4470
  stdio: ["pipe", "pipe", "pipe"]
3642
4471
  }).trim();
3643
4472
  }
3644
- _cached = path7.basename(repoRoot);
4473
+ _cached = path9.basename(repoRoot);
3645
4474
  _cachedCwd = dir;
3646
4475
  return _cached;
3647
4476
  } catch {
3648
- _cached = path7.basename(dir);
4477
+ _cached = path9.basename(dir);
3649
4478
  _cachedCwd = dir;
3650
4479
  return _cached;
3651
4480
  }
@@ -3669,8 +4498,8 @@ __export(file_grep_exports, {
3669
4498
  grepProjectFiles: () => grepProjectFiles
3670
4499
  });
3671
4500
  import { execSync as execSync3 } from "child_process";
3672
- import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync7 } from "fs";
3673
- import path8 from "path";
4501
+ import { readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync8 } from "fs";
4502
+ import path10 from "path";
3674
4503
  import crypto from "crypto";
3675
4504
  function hasRipgrep() {
3676
4505
  if (_hasRg === null) {
@@ -3710,7 +4539,7 @@ async function grepProjectFiles(query, projectRoot, options) {
3710
4539
  session_id: "file-grep",
3711
4540
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3712
4541
  tool_name: "file_grep",
3713
- project_name: path8.basename(projectRoot),
4542
+ project_name: path10.basename(projectRoot),
3714
4543
  has_error: false,
3715
4544
  raw_text: `${prefix} ${buildSnippet(hit, projectRoot)}`,
3716
4545
  vector: null,
@@ -3722,7 +4551,7 @@ function getChunkContext(filePath, lineNumber) {
3722
4551
  try {
3723
4552
  const ext = filePath.split(".").pop()?.toLowerCase();
3724
4553
  if (ext !== "ts" && ext !== "tsx" && ext !== "js" && ext !== "jsx") return "";
3725
- const source = readFileSync4(filePath, "utf8");
4554
+ const source = readFileSync5(filePath, "utf8");
3726
4555
  const lines = source.split("\n");
3727
4556
  for (let i = Math.min(lineNumber - 1, lines.length - 1); i >= 0; i--) {
3728
4557
  const line = lines[i];
@@ -3784,11 +4613,11 @@ function grepWithNodeFs(pattern, projectRoot, patterns) {
3784
4613
  const files = collectFiles(projectRoot, patterns ?? DEFAULT_PATTERNS);
3785
4614
  const hits = [];
3786
4615
  for (const filePath of files.slice(0, MAX_FILES)) {
3787
- const absPath = path8.join(projectRoot, filePath);
4616
+ const absPath = path10.join(projectRoot, filePath);
3788
4617
  try {
3789
4618
  const stat = statSync2(absPath);
3790
4619
  if (stat.size > MAX_FILE_SIZE) continue;
3791
- const content = readFileSync4(absPath, "utf8");
4620
+ const content = readFileSync5(absPath, "utf8");
3792
4621
  const lines = content.split("\n");
3793
4622
  const matches = content.match(regex);
3794
4623
  if (!matches || matches.length === 0) continue;
@@ -3811,15 +4640,15 @@ function collectFiles(root, patterns) {
3811
4640
  const files = [];
3812
4641
  function walk(dir, relative) {
3813
4642
  if (files.length >= MAX_FILES) return;
3814
- const basename = path8.basename(dir);
4643
+ const basename = path10.basename(dir);
3815
4644
  if (EXCLUDE_DIRS.includes(basename)) return;
3816
4645
  try {
3817
4646
  const entries = readdirSync2(dir, { withFileTypes: true });
3818
4647
  for (const entry of entries) {
3819
4648
  if (files.length >= MAX_FILES) return;
3820
- const rel = path8.join(relative, entry.name);
4649
+ const rel = path10.join(relative, entry.name);
3821
4650
  if (entry.isDirectory()) {
3822
- walk(path8.join(dir, entry.name), rel);
4651
+ walk(path10.join(dir, entry.name), rel);
3823
4652
  } else if (entry.isFile()) {
3824
4653
  for (const pat of patterns) {
3825
4654
  if (matchGlob(rel, pat)) {
@@ -3851,7 +4680,7 @@ function matchGlob(filePath, pattern) {
3851
4680
  if (slashIdx !== -1) {
3852
4681
  const dir = pattern.slice(0, slashIdx);
3853
4682
  const ext2 = pattern.slice(slashIdx + 1).replace("*", "");
3854
- const fileDir = path8.dirname(filePath);
4683
+ const fileDir = path10.dirname(filePath);
3855
4684
  return fileDir === dir && filePath.endsWith(ext2);
3856
4685
  }
3857
4686
  const ext = pattern.replace("*", "");
@@ -3859,9 +4688,9 @@ function matchGlob(filePath, pattern) {
3859
4688
  }
3860
4689
  function buildSnippet(hit, projectRoot) {
3861
4690
  try {
3862
- const absPath = path8.join(projectRoot, hit.filePath);
3863
- if (!existsSync7(absPath)) return hit.matchLine;
3864
- const lines = readFileSync4(absPath, "utf8").split("\n");
4691
+ const absPath = path10.join(projectRoot, hit.filePath);
4692
+ if (!existsSync8(absPath)) return hit.matchLine;
4693
+ const lines = readFileSync5(absPath, "utf8").split("\n");
3865
4694
  const start = Math.max(0, hit.lineNumber - 3);
3866
4695
  const end = Math.min(lines.length, hit.lineNumber + 2);
3867
4696
  return lines.slice(start, end).join("\n").slice(0, 500);
@@ -5086,9 +5915,9 @@ __export(active_agent_exports, {
5086
5915
  resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
5087
5916
  writeActiveAgent: () => writeActiveAgent
5088
5917
  });
5089
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
5918
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
5090
5919
  import { execSync as execSync5 } from "child_process";
5091
- import path9 from "path";
5920
+ import path11 from "path";
5092
5921
  function isNameWithOptionalInstance(candidate, baseName) {
5093
5922
  if (candidate === baseName) return true;
5094
5923
  if (!candidate.startsWith(baseName)) return false;
@@ -5132,12 +5961,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
5132
5961
  return null;
5133
5962
  }
5134
5963
  function getMarkerPath() {
5135
- return path9.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
5964
+ return path11.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
5136
5965
  }
5137
5966
  function writeActiveAgent(agentId, agentRole) {
5138
5967
  try {
5139
- mkdirSync2(CACHE_DIR, { recursive: true });
5140
- writeFileSync2(
5968
+ mkdirSync3(CACHE_DIR, { recursive: true });
5969
+ writeFileSync3(
5141
5970
  getMarkerPath(),
5142
5971
  JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
5143
5972
  );
@@ -5153,7 +5982,7 @@ function clearActiveAgent() {
5153
5982
  function getActiveAgent() {
5154
5983
  try {
5155
5984
  const markerPath = getMarkerPath();
5156
- const raw = readFileSync5(markerPath, "utf8");
5985
+ const raw = readFileSync6(markerPath, "utf8");
5157
5986
  const data = JSON.parse(raw);
5158
5987
  if (data.agentId) {
5159
5988
  if (data.startedAt) {
@@ -5201,14 +6030,14 @@ function getAllActiveAgents() {
5201
6030
  const key = file.slice("active-agent-".length, -".json".length);
5202
6031
  if (key === "undefined") continue;
5203
6032
  try {
5204
- const raw = readFileSync5(path9.join(CACHE_DIR, file), "utf8");
6033
+ const raw = readFileSync6(path11.join(CACHE_DIR, file), "utf8");
5205
6034
  const data = JSON.parse(raw);
5206
6035
  if (!data.agentId) continue;
5207
6036
  if (data.startedAt) {
5208
6037
  const age = Date.now() - new Date(data.startedAt).getTime();
5209
6038
  if (age > STALE_MS) {
5210
6039
  try {
5211
- unlinkSync3(path9.join(CACHE_DIR, file));
6040
+ unlinkSync3(path11.join(CACHE_DIR, file));
5212
6041
  } catch {
5213
6042
  }
5214
6043
  continue;
@@ -5231,11 +6060,11 @@ function getAllActiveAgents() {
5231
6060
  function cleanupSessionMarkers() {
5232
6061
  const key = getSessionKey();
5233
6062
  try {
5234
- unlinkSync3(path9.join(CACHE_DIR, `active-agent-${key}.json`));
6063
+ unlinkSync3(path11.join(CACHE_DIR, `active-agent-${key}.json`));
5235
6064
  } catch {
5236
6065
  }
5237
6066
  try {
5238
- unlinkSync3(path9.join(CACHE_DIR, "active-agent-undefined.json"));
6067
+ unlinkSync3(path11.join(CACHE_DIR, "active-agent-undefined.json"));
5239
6068
  } catch {
5240
6069
  }
5241
6070
  }
@@ -5246,7 +6075,7 @@ var init_active_agent = __esm({
5246
6075
  init_config();
5247
6076
  init_session_key();
5248
6077
  init_employees();
5249
- CACHE_DIR = path9.join(EXE_AI_DIR, "session-cache");
6078
+ CACHE_DIR = path11.join(EXE_AI_DIR, "session-cache");
5250
6079
  STALE_MS = 24 * 60 * 60 * 1e3;
5251
6080
  }
5252
6081
  });
@@ -5268,9 +6097,9 @@ __export(license_exports, {
5268
6097
  stopLicenseRevalidation: () => stopLicenseRevalidation,
5269
6098
  validateLicense: () => validateLicense
5270
6099
  });
5271
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
6100
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
5272
6101
  import { randomUUID as randomUUID3 } from "crypto";
5273
- import path10 from "path";
6102
+ import path12 from "path";
5274
6103
  import { jwtVerify, importSPKI } from "jose";
5275
6104
  async function fetchRetry(url, init) {
5276
6105
  try {
@@ -5281,37 +6110,37 @@ async function fetchRetry(url, init) {
5281
6110
  }
5282
6111
  }
5283
6112
  function loadDeviceId() {
5284
- const deviceJsonPath = path10.join(EXE_AI_DIR, "device.json");
6113
+ const deviceJsonPath = path12.join(EXE_AI_DIR, "device.json");
5285
6114
  try {
5286
- if (existsSync8(deviceJsonPath)) {
5287
- const data = JSON.parse(readFileSync6(deviceJsonPath, "utf8"));
6115
+ if (existsSync9(deviceJsonPath)) {
6116
+ const data = JSON.parse(readFileSync7(deviceJsonPath, "utf8"));
5288
6117
  if (data.deviceId) return data.deviceId;
5289
6118
  }
5290
6119
  } catch {
5291
6120
  }
5292
6121
  try {
5293
- if (existsSync8(DEVICE_ID_PATH)) {
5294
- const id2 = readFileSync6(DEVICE_ID_PATH, "utf8").trim();
6122
+ if (existsSync9(DEVICE_ID_PATH)) {
6123
+ const id2 = readFileSync7(DEVICE_ID_PATH, "utf8").trim();
5295
6124
  if (id2) return id2;
5296
6125
  }
5297
6126
  } catch {
5298
6127
  }
5299
6128
  const id = randomUUID3();
5300
- mkdirSync3(EXE_AI_DIR, { recursive: true });
5301
- writeFileSync3(DEVICE_ID_PATH, id, "utf8");
6129
+ mkdirSync4(EXE_AI_DIR, { recursive: true });
6130
+ writeFileSync4(DEVICE_ID_PATH, id, "utf8");
5302
6131
  return id;
5303
6132
  }
5304
6133
  function loadLicense() {
5305
6134
  try {
5306
- if (!existsSync8(LICENSE_PATH)) return null;
5307
- return readFileSync6(LICENSE_PATH, "utf8").trim();
6135
+ if (!existsSync9(LICENSE_PATH)) return null;
6136
+ return readFileSync7(LICENSE_PATH, "utf8").trim();
5308
6137
  } catch {
5309
6138
  return null;
5310
6139
  }
5311
6140
  }
5312
6141
  function saveLicense(apiKey) {
5313
- mkdirSync3(EXE_AI_DIR, { recursive: true });
5314
- writeFileSync3(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
6142
+ mkdirSync4(EXE_AI_DIR, { recursive: true });
6143
+ writeFileSync4(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
5315
6144
  }
5316
6145
  async function verifyLicenseJwt(token) {
5317
6146
  try {
@@ -5337,8 +6166,8 @@ async function verifyLicenseJwt(token) {
5337
6166
  }
5338
6167
  async function getCachedLicense() {
5339
6168
  try {
5340
- if (!existsSync8(CACHE_PATH)) return null;
5341
- const raw = JSON.parse(readFileSync6(CACHE_PATH, "utf8"));
6169
+ if (!existsSync9(CACHE_PATH)) return null;
6170
+ const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
5342
6171
  if (!raw.token || typeof raw.token !== "string") return null;
5343
6172
  return await verifyLicenseJwt(raw.token);
5344
6173
  } catch {
@@ -5347,8 +6176,8 @@ async function getCachedLicense() {
5347
6176
  }
5348
6177
  function readCachedToken() {
5349
6178
  try {
5350
- if (!existsSync8(CACHE_PATH)) return null;
5351
- const raw = JSON.parse(readFileSync6(CACHE_PATH, "utf8"));
6179
+ if (!existsSync9(CACHE_PATH)) return null;
6180
+ const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
5352
6181
  return typeof raw.token === "string" ? raw.token : null;
5353
6182
  } catch {
5354
6183
  return null;
@@ -5382,7 +6211,7 @@ function getRawCachedPlan() {
5382
6211
  }
5383
6212
  function cacheResponse(token) {
5384
6213
  try {
5385
- writeFileSync3(CACHE_PATH, JSON.stringify({ token }), "utf8");
6214
+ writeFileSync4(CACHE_PATH, JSON.stringify({ token }), "utf8");
5386
6215
  } catch {
5387
6216
  }
5388
6217
  }
@@ -5446,9 +6275,9 @@ async function checkLicense() {
5446
6275
  let key = loadLicense();
5447
6276
  if (!key) {
5448
6277
  try {
5449
- const configPath = path10.join(EXE_AI_DIR, "config.json");
5450
- if (existsSync8(configPath)) {
5451
- const raw = JSON.parse(readFileSync6(configPath, "utf8"));
6278
+ const configPath = path12.join(EXE_AI_DIR, "config.json");
6279
+ if (existsSync9(configPath)) {
6280
+ const raw = JSON.parse(readFileSync7(configPath, "utf8"));
5452
6281
  const cloud = raw.cloud;
5453
6282
  if (cloud?.apiKey) {
5454
6283
  key = cloud.apiKey;
@@ -5607,9 +6436,9 @@ var init_license = __esm({
5607
6436
  "src/lib/license.ts"() {
5608
6437
  "use strict";
5609
6438
  init_config();
5610
- LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
5611
- CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
5612
- DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
6439
+ LICENSE_PATH = path12.join(EXE_AI_DIR, "license.key");
6440
+ CACHE_PATH = path12.join(EXE_AI_DIR, "license-cache.json");
6441
+ DEVICE_ID_PATH = path12.join(EXE_AI_DIR, "device-id");
5613
6442
  API_BASE = "https://askexe.com/cloud";
5614
6443
  RETRY_DELAY_MS = 500;
5615
6444
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
@@ -5649,12 +6478,12 @@ __export(plan_limits_exports, {
5649
6478
  countActiveMemories: () => countActiveMemories,
5650
6479
  getLicenseSync: () => getLicenseSync
5651
6480
  });
5652
- import { readFileSync as readFileSync7, existsSync as existsSync9 } from "fs";
5653
- import path11 from "path";
6481
+ import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
6482
+ import path13 from "path";
5654
6483
  function getLicenseSync() {
5655
6484
  try {
5656
- if (!existsSync9(CACHE_PATH2)) return freeLicense();
5657
- const raw = JSON.parse(readFileSync7(CACHE_PATH2, "utf8"));
6485
+ if (!existsSync10(CACHE_PATH2)) return freeLicense();
6486
+ const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
5658
6487
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
5659
6488
  const parts = raw.token.split(".");
5660
6489
  if (parts.length !== 3) return freeLicense();
@@ -5721,8 +6550,8 @@ function assertEmployeeLimitSync(rosterPath) {
5721
6550
  const filePath = rosterPath ?? EMPLOYEES_PATH;
5722
6551
  let count = 0;
5723
6552
  try {
5724
- if (existsSync9(filePath)) {
5725
- const raw = readFileSync7(filePath, "utf8");
6553
+ if (existsSync10(filePath)) {
6554
+ const raw = readFileSync8(filePath, "utf8");
5726
6555
  const employees = JSON.parse(raw);
5727
6556
  count = Array.isArray(employees) ? employees.length : 0;
5728
6557
  }
@@ -5759,19 +6588,19 @@ var init_plan_limits = __esm({
5759
6588
  this.name = "PlanLimitError";
5760
6589
  }
5761
6590
  };
5762
- CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
6591
+ CACHE_PATH2 = path13.join(EXE_AI_DIR, "license-cache.json");
5763
6592
  }
5764
6593
  });
5765
6594
 
5766
6595
  // src/lib/notifications.ts
5767
6596
  import crypto4 from "crypto";
5768
- import path14 from "path";
5769
- import os5 from "os";
6597
+ import path16 from "path";
6598
+ import os6 from "os";
5770
6599
  import {
5771
- readFileSync as readFileSync8,
6600
+ readFileSync as readFileSync9,
5772
6601
  readdirSync as readdirSync4,
5773
6602
  unlinkSync as unlinkSync4,
5774
- existsSync as existsSync10,
6603
+ existsSync as existsSync11,
5775
6604
  rmdirSync
5776
6605
  } from "fs";
5777
6606
  async function writeNotification(notification) {
@@ -5816,13 +6645,13 @@ var init_notifications = __esm({
5816
6645
  });
5817
6646
 
5818
6647
  // src/lib/session-registry.ts
5819
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync4, existsSync as existsSync11 } from "fs";
5820
- import path15 from "path";
5821
- import os6 from "os";
6648
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync5, existsSync as existsSync12 } from "fs";
6649
+ import path17 from "path";
6650
+ import os7 from "os";
5822
6651
  function registerSession(entry) {
5823
- const dir = path15.dirname(REGISTRY_PATH);
5824
- if (!existsSync11(dir)) {
5825
- mkdirSync4(dir, { recursive: true });
6652
+ const dir = path17.dirname(REGISTRY_PATH);
6653
+ if (!existsSync12(dir)) {
6654
+ mkdirSync5(dir, { recursive: true });
5826
6655
  }
5827
6656
  const sessions = listSessions();
5828
6657
  const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
@@ -5831,11 +6660,11 @@ function registerSession(entry) {
5831
6660
  } else {
5832
6661
  sessions.push(entry);
5833
6662
  }
5834
- writeFileSync6(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
6663
+ writeFileSync7(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
5835
6664
  }
5836
6665
  function listSessions() {
5837
6666
  try {
5838
- const raw = readFileSync9(REGISTRY_PATH, "utf8");
6667
+ const raw = readFileSync10(REGISTRY_PATH, "utf8");
5839
6668
  return JSON.parse(raw);
5840
6669
  } catch {
5841
6670
  return [];
@@ -5845,7 +6674,7 @@ var REGISTRY_PATH;
5845
6674
  var init_session_registry = __esm({
5846
6675
  "src/lib/session-registry.ts"() {
5847
6676
  "use strict";
5848
- REGISTRY_PATH = path15.join(os6.homedir(), ".exe-os", "session-registry.json");
6677
+ REGISTRY_PATH = path17.join(os7.homedir(), ".exe-os", "session-registry.json");
5849
6678
  }
5850
6679
  });
5851
6680
 
@@ -6029,101 +6858,6 @@ var init_provider_table = __esm({
6029
6858
  }
6030
6859
  });
6031
6860
 
6032
- // src/lib/runtime-table.ts
6033
- var RUNTIME_TABLE, DEFAULT_RUNTIME;
6034
- var init_runtime_table = __esm({
6035
- "src/lib/runtime-table.ts"() {
6036
- "use strict";
6037
- RUNTIME_TABLE = {
6038
- codex: {
6039
- binary: "codex",
6040
- launchMode: "interactive",
6041
- autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
6042
- inlineFlag: "--no-alt-screen",
6043
- apiKeyEnv: "OPENAI_API_KEY",
6044
- defaultModel: "gpt-5.4"
6045
- },
6046
- opencode: {
6047
- binary: "opencode",
6048
- launchMode: "exec",
6049
- autoApproveFlag: "--dangerously-skip-permissions",
6050
- inlineFlag: "",
6051
- apiKeyEnv: "ANTHROPIC_API_KEY",
6052
- defaultModel: "anthropic/claude-sonnet-4-6"
6053
- }
6054
- };
6055
- DEFAULT_RUNTIME = "claude";
6056
- }
6057
- });
6058
-
6059
- // src/lib/agent-config.ts
6060
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, existsSync as existsSync12, mkdirSync as mkdirSync5 } from "fs";
6061
- import path16 from "path";
6062
- function loadAgentConfig() {
6063
- if (!existsSync12(AGENT_CONFIG_PATH)) return {};
6064
- try {
6065
- return JSON.parse(readFileSync10(AGENT_CONFIG_PATH, "utf-8"));
6066
- } catch {
6067
- return {};
6068
- }
6069
- }
6070
- function saveAgentConfig(config2) {
6071
- const dir = path16.dirname(AGENT_CONFIG_PATH);
6072
- if (!existsSync12(dir)) mkdirSync5(dir, { recursive: true });
6073
- writeFileSync7(AGENT_CONFIG_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
6074
- }
6075
- function getAgentRuntime(agentId) {
6076
- const config2 = loadAgentConfig();
6077
- const entry = config2[agentId];
6078
- if (entry) return entry;
6079
- const orgDefault = config2["default"];
6080
- if (orgDefault) return orgDefault;
6081
- return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
6082
- }
6083
- function setAgentRuntime(agentId, runtime, model) {
6084
- const knownModels = KNOWN_RUNTIMES[runtime];
6085
- if (!knownModels) {
6086
- return {
6087
- ok: false,
6088
- error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
6089
- };
6090
- }
6091
- if (!knownModels.includes(model)) {
6092
- return {
6093
- ok: false,
6094
- error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
6095
- };
6096
- }
6097
- const config2 = loadAgentConfig();
6098
- config2[agentId] = { runtime, model };
6099
- saveAgentConfig(config2);
6100
- return { ok: true };
6101
- }
6102
- var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
6103
- var init_agent_config = __esm({
6104
- "src/lib/agent-config.ts"() {
6105
- "use strict";
6106
- init_config();
6107
- init_runtime_table();
6108
- AGENT_CONFIG_PATH = path16.join(EXE_AI_DIR, "agent-config.json");
6109
- KNOWN_RUNTIMES = {
6110
- claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
6111
- codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
6112
- opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
6113
- };
6114
- RUNTIME_LABELS = {
6115
- claude: "Claude Code (Anthropic)",
6116
- codex: "Codex (OpenAI)",
6117
- opencode: "OpenCode (open source)"
6118
- };
6119
- DEFAULT_MODELS = {
6120
- claude: "claude-opus-4",
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
6861
  // src/lib/intercom-queue.ts
6128
6862
  var intercom_queue_exports = {};
6129
6863
  __export(intercom_queue_exports, {
@@ -6134,10 +6868,10 @@ __export(intercom_queue_exports, {
6134
6868
  readQueue: () => readQueue
6135
6869
  });
6136
6870
  import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, renameSync as renameSync3, existsSync as existsSync13, mkdirSync as mkdirSync6 } from "fs";
6137
- import path17 from "path";
6138
- import os7 from "os";
6871
+ import path18 from "path";
6872
+ import os8 from "os";
6139
6873
  function ensureDir() {
6140
- const dir = path17.dirname(QUEUE_PATH);
6874
+ const dir = path18.dirname(QUEUE_PATH);
6141
6875
  if (!existsSync13(dir)) mkdirSync6(dir, { recursive: true });
6142
6876
  }
6143
6877
  function readQueue() {
@@ -6243,10 +6977,10 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
6243
6977
  var init_intercom_queue = __esm({
6244
6978
  "src/lib/intercom-queue.ts"() {
6245
6979
  "use strict";
6246
- QUEUE_PATH = path17.join(os7.homedir(), ".exe-os", "intercom-queue.json");
6980
+ QUEUE_PATH = path18.join(os8.homedir(), ".exe-os", "intercom-queue.json");
6247
6981
  MAX_RETRIES2 = 5;
6248
6982
  TTL_MS = 60 * 60 * 1e3;
6249
- INTERCOM_LOG = path17.join(os7.homedir(), ".exe-os", "intercom.log");
6983
+ INTERCOM_LOG = path18.join(os8.homedir(), ".exe-os", "intercom.log");
6250
6984
  }
6251
6985
  });
6252
6986
 
@@ -6625,12 +7359,12 @@ __export(tmux_routing_exports, {
6625
7359
  });
6626
7360
  import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
6627
7361
  import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, mkdirSync as mkdirSync7, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync5 } from "fs";
6628
- import path18 from "path";
6629
- import os8 from "os";
7362
+ import path19 from "path";
7363
+ import os9 from "os";
6630
7364
  import { fileURLToPath as fileURLToPath2 } from "url";
6631
7365
  import { unlinkSync as unlinkSync5 } from "fs";
6632
7366
  function spawnLockPath(sessionName) {
6633
- return path18.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
7367
+ return path19.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
6634
7368
  }
6635
7369
  function isProcessAlive(pid) {
6636
7370
  try {
@@ -6667,8 +7401,8 @@ function releaseSpawnLock2(sessionName) {
6667
7401
  function resolveBehaviorsExporterScript() {
6668
7402
  try {
6669
7403
  const thisFile = fileURLToPath2(import.meta.url);
6670
- const scriptPath = path18.join(
6671
- path18.dirname(thisFile),
7404
+ const scriptPath = path19.join(
7405
+ path19.dirname(thisFile),
6672
7406
  "..",
6673
7407
  "bin",
6674
7408
  "exe-export-behaviors.js"
@@ -6743,7 +7477,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
6743
7477
  mkdirSync7(SESSION_CACHE, { recursive: true });
6744
7478
  }
6745
7479
  const rootExe = extractRootExe(parentExe) ?? parentExe;
6746
- const filePath = path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
7480
+ const filePath = path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
6747
7481
  writeFileSync9(filePath, JSON.stringify({
6748
7482
  parentExe: rootExe,
6749
7483
  dispatchedBy: dispatchedBy || rootExe,
@@ -6752,7 +7486,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
6752
7486
  }
6753
7487
  function getParentExe(sessionKey) {
6754
7488
  try {
6755
- const data = JSON.parse(readFileSync12(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
7489
+ const data = JSON.parse(readFileSync12(path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
6756
7490
  return data.parentExe || null;
6757
7491
  } catch {
6758
7492
  return null;
@@ -6761,7 +7495,7 @@ function getParentExe(sessionKey) {
6761
7495
  function getDispatchedBy(sessionKey) {
6762
7496
  try {
6763
7497
  const data = JSON.parse(readFileSync12(
6764
- path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
7498
+ path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
6765
7499
  "utf8"
6766
7500
  ));
6767
7501
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -6772,15 +7506,24 @@ function getDispatchedBy(sessionKey) {
6772
7506
  function resolveExeSession() {
6773
7507
  const mySession = getMySession();
6774
7508
  if (!mySession) return null;
7509
+ const fromSessionName = extractRootExe(mySession);
6775
7510
  try {
6776
7511
  const key = getSessionKey();
6777
7512
  const parentExe = getParentExe(key);
6778
7513
  if (parentExe) {
6779
- return extractRootExe(parentExe) ?? parentExe;
7514
+ const fromCache = extractRootExe(parentExe) ?? parentExe;
7515
+ if (fromSessionName && fromCache !== fromSessionName) {
7516
+ process.stderr.write(
7517
+ `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
7518
+ `
7519
+ );
7520
+ return fromSessionName;
7521
+ }
7522
+ return fromCache;
6780
7523
  }
6781
7524
  } catch {
6782
7525
  }
6783
- return extractRootExe(mySession) ?? mySession;
7526
+ return fromSessionName ?? mySession;
6784
7527
  }
6785
7528
  function isEmployeeAlive(sessionName) {
6786
7529
  return getTransport().isAlive(sessionName);
@@ -6938,7 +7681,7 @@ function sendIntercom(targetSession) {
6938
7681
  try {
6939
7682
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
6940
7683
  const agent = baseAgentName(rawAgent);
6941
- const markerPath = path18.join(SESSION_CACHE, `current-task-${agent}.json`);
7684
+ const markerPath = path19.join(SESSION_CACHE, `current-task-${agent}.json`);
6942
7685
  if (existsSync14(markerPath)) {
6943
7686
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
6944
7687
  return "debounced";
@@ -6948,7 +7691,7 @@ function sendIntercom(targetSession) {
6948
7691
  try {
6949
7692
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
6950
7693
  const agent = baseAgentName(rawAgent);
6951
- const taskDir = path18.join(process.cwd(), "exe", agent);
7694
+ const taskDir = path19.join(process.cwd(), "exe", agent);
6952
7695
  if (existsSync14(taskDir)) {
6953
7696
  const files = readdirSync5(taskDir).filter(
6954
7697
  (f) => f.endsWith(".md") && f !== "DONE.txt"
@@ -7082,8 +7825,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7082
7825
  const transport = getTransport();
7083
7826
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
7084
7827
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
7085
- const logDir = path18.join(os8.homedir(), ".exe-os", "session-logs");
7086
- const logFile = path18.join(logDir, `${instanceLabel}-${Date.now()}.log`);
7828
+ const logDir = path19.join(os9.homedir(), ".exe-os", "session-logs");
7829
+ const logFile = path19.join(logDir, `${instanceLabel}-${Date.now()}.log`);
7087
7830
  if (!existsSync14(logDir)) {
7088
7831
  mkdirSync7(logDir, { recursive: true });
7089
7832
  }
@@ -7091,14 +7834,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7091
7834
  let cleanupSuffix = "";
7092
7835
  try {
7093
7836
  const thisFile = fileURLToPath2(import.meta.url);
7094
- const cleanupScript = path18.join(path18.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
7837
+ const cleanupScript = path19.join(path19.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
7095
7838
  if (existsSync14(cleanupScript)) {
7096
7839
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
7097
7840
  }
7098
7841
  } catch {
7099
7842
  }
7100
7843
  try {
7101
- const claudeJsonPath = path18.join(os8.homedir(), ".claude.json");
7844
+ const claudeJsonPath = path19.join(os9.homedir(), ".claude.json");
7102
7845
  let claudeJson = {};
7103
7846
  try {
7104
7847
  claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
@@ -7113,10 +7856,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7113
7856
  } catch {
7114
7857
  }
7115
7858
  try {
7116
- const settingsDir = path18.join(os8.homedir(), ".claude", "projects");
7859
+ const settingsDir = path19.join(os9.homedir(), ".claude", "projects");
7117
7860
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
7118
- const projSettingsDir = path18.join(settingsDir, normalizedKey);
7119
- const settingsPath = path18.join(projSettingsDir, "settings.json");
7861
+ const projSettingsDir = path19.join(settingsDir, normalizedKey);
7862
+ const settingsPath = path19.join(projSettingsDir, "settings.json");
7120
7863
  let settings = {};
7121
7864
  try {
7122
7865
  settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
@@ -7163,8 +7906,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7163
7906
  let behaviorsFlag = "";
7164
7907
  let legacyFallbackWarned = false;
7165
7908
  if (!useExeAgent && !useBinSymlink) {
7166
- const identityPath2 = path18.join(
7167
- os8.homedir(),
7909
+ const identityPath2 = path19.join(
7910
+ os9.homedir(),
7168
7911
  ".exe-os",
7169
7912
  "identity",
7170
7913
  `${employeeName}.md`
@@ -7179,7 +7922,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7179
7922
  }
7180
7923
  const behaviorsFile = exportBehaviorsSync(
7181
7924
  employeeName,
7182
- path18.basename(spawnCwd),
7925
+ path19.basename(spawnCwd),
7183
7926
  sessionName
7184
7927
  );
7185
7928
  if (behaviorsFile) {
@@ -7194,9 +7937,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7194
7937
  }
7195
7938
  let sessionContextFlag = "";
7196
7939
  try {
7197
- const ctxDir = path18.join(os8.homedir(), ".exe-os", "session-cache");
7940
+ const ctxDir = path19.join(os9.homedir(), ".exe-os", "session-cache");
7198
7941
  mkdirSync7(ctxDir, { recursive: true });
7199
- const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
7942
+ const ctxFile = path19.join(ctxDir, `session-context-${sessionName}.md`);
7200
7943
  const ctxContent = [
7201
7944
  `## Session Context`,
7202
7945
  `You are running in tmux session: ${sessionName}.`,
@@ -7280,7 +8023,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7280
8023
  transport.pipeLog(sessionName, logFile);
7281
8024
  try {
7282
8025
  const mySession = getMySession();
7283
- const dispatchInfo = path18.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
8026
+ const dispatchInfo = path19.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
7284
8027
  writeFileSync9(dispatchInfo, JSON.stringify({
7285
8028
  dispatchedBy: mySession,
7286
8029
  rootExe: exeSession,
@@ -7355,15 +8098,15 @@ var init_tmux_routing = __esm({
7355
8098
  init_intercom_queue();
7356
8099
  init_plan_limits();
7357
8100
  init_employees();
7358
- SPAWN_LOCK_DIR = path18.join(os8.homedir(), ".exe-os", "spawn-locks");
7359
- SESSION_CACHE = path18.join(os8.homedir(), ".exe-os", "session-cache");
8101
+ SPAWN_LOCK_DIR = path19.join(os9.homedir(), ".exe-os", "spawn-locks");
8102
+ SESSION_CACHE = path19.join(os9.homedir(), ".exe-os", "session-cache");
7360
8103
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
7361
8104
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
7362
8105
  VERIFY_PANE_LINES = 200;
7363
8106
  INTERCOM_DEBOUNCE_MS = 3e4;
7364
8107
  CODEX_DEBOUNCE_MS = 12e4;
7365
- INTERCOM_LOG2 = path18.join(os8.homedir(), ".exe-os", "intercom.log");
7366
- DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
8108
+ INTERCOM_LOG2 = path19.join(os9.homedir(), ".exe-os", "intercom.log");
8109
+ DEBOUNCE_FILE = path19.join(SESSION_CACHE, "intercom-debounce.json");
7367
8110
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
7368
8111
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
7369
8112
  }
@@ -7411,8 +8154,8 @@ __export(tasks_crud_exports, {
7411
8154
  writeCheckpoint: () => writeCheckpoint
7412
8155
  });
7413
8156
  import crypto6 from "crypto";
7414
- import path19 from "path";
7415
- import os9 from "os";
8157
+ import path20 from "path";
8158
+ import os10 from "os";
7416
8159
  import { execSync as execSync8 } from "child_process";
7417
8160
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
7418
8161
  import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
@@ -7590,8 +8333,8 @@ ${laneWarning}` : laneWarning;
7590
8333
  }
7591
8334
  if (input.baseDir) {
7592
8335
  try {
7593
- await mkdir4(path19.join(input.baseDir, "exe", "output"), { recursive: true });
7594
- await mkdir4(path19.join(input.baseDir, "exe", "research"), { recursive: true });
8336
+ await mkdir4(path20.join(input.baseDir, "exe", "output"), { recursive: true });
8337
+ await mkdir4(path20.join(input.baseDir, "exe", "research"), { recursive: true });
7595
8338
  await ensureArchitectureDoc(input.baseDir, input.projectName);
7596
8339
  await ensureGitignoreExe(input.baseDir);
7597
8340
  } catch {
@@ -7627,9 +8370,9 @@ ${laneWarning}` : laneWarning;
7627
8370
  });
7628
8371
  if (input.baseDir) {
7629
8372
  try {
7630
- const EXE_OS_DIR = path19.join(os9.homedir(), ".exe-os");
7631
- const mdPath = path19.join(EXE_OS_DIR, taskFile);
7632
- const mdDir = path19.dirname(mdPath);
8373
+ const EXE_OS_DIR = path20.join(os10.homedir(), ".exe-os");
8374
+ const mdPath = path20.join(EXE_OS_DIR, taskFile);
8375
+ const mdDir = path20.dirname(mdPath);
7633
8376
  if (!existsSync15(mdDir)) await mkdir4(mdDir, { recursive: true });
7634
8377
  const reviewer = input.reviewer ?? input.assignedBy;
7635
8378
  const mdContent = `# ${input.title}
@@ -7930,7 +8673,7 @@ async function deleteTaskCore(taskId, _baseDir) {
7930
8673
  return { taskFile, assignedTo, assignedBy, taskSlug };
7931
8674
  }
7932
8675
  async function ensureArchitectureDoc(baseDir, projectName) {
7933
- const archPath = path19.join(baseDir, "exe", "ARCHITECTURE.md");
8676
+ const archPath = path20.join(baseDir, "exe", "ARCHITECTURE.md");
7934
8677
  try {
7935
8678
  if (existsSync15(archPath)) return;
7936
8679
  const template = [
@@ -7965,7 +8708,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
7965
8708
  }
7966
8709
  }
7967
8710
  async function ensureGitignoreExe(baseDir) {
7968
- const gitignorePath = path19.join(baseDir, ".gitignore");
8711
+ const gitignorePath = path20.join(baseDir, ".gitignore");
7969
8712
  try {
7970
8713
  if (existsSync15(gitignorePath)) {
7971
8714
  const content = readFileSync13(gitignorePath, "utf-8");
@@ -7999,13 +8742,13 @@ var init_tasks_crud = __esm({
7999
8742
  });
8000
8743
 
8001
8744
  // src/lib/tasks-review.ts
8002
- import path20 from "path";
8745
+ import path21 from "path";
8003
8746
  import { existsSync as existsSync16, readdirSync as readdirSync6, unlinkSync as unlinkSync6 } from "fs";
8004
8747
  async function countPendingReviews(sessionScope) {
8005
8748
  const client = getClient();
8006
8749
  if (sessionScope) {
8007
8750
  const result2 = await client.execute({
8008
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND (session_scope = ? OR session_scope IS NULL)",
8751
+ sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
8009
8752
  args: [sessionScope]
8010
8753
  });
8011
8754
  return Number(result2.rows[0]?.cnt) || 0;
@@ -8181,11 +8924,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
8181
8924
  );
8182
8925
  }
8183
8926
  try {
8184
- const cacheDir = path20.join(EXE_AI_DIR, "session-cache");
8927
+ const cacheDir = path21.join(EXE_AI_DIR, "session-cache");
8185
8928
  if (existsSync16(cacheDir)) {
8186
8929
  for (const f of readdirSync6(cacheDir)) {
8187
8930
  if (f.startsWith("review-notified-")) {
8188
- unlinkSync6(path20.join(cacheDir, f));
8931
+ unlinkSync6(path21.join(cacheDir, f));
8189
8932
  }
8190
8933
  }
8191
8934
  }
@@ -8206,7 +8949,7 @@ var init_tasks_review = __esm({
8206
8949
  });
8207
8950
 
8208
8951
  // src/lib/tasks-chain.ts
8209
- import path21 from "path";
8952
+ import path22 from "path";
8210
8953
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
8211
8954
  async function cascadeUnblock(taskId, baseDir, now) {
8212
8955
  const client = getClient();
@@ -8223,7 +8966,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
8223
8966
  });
8224
8967
  for (const ur of unblockedRows.rows) {
8225
8968
  try {
8226
- const ubFile = path21.join(baseDir, String(ur.task_file));
8969
+ const ubFile = path22.join(baseDir, String(ur.task_file));
8227
8970
  let ubContent = await readFile4(ubFile, "utf-8");
8228
8971
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
8229
8972
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -8777,7 +9520,7 @@ __export(tasks_exports, {
8777
9520
  updateTaskStatus: () => updateTaskStatus,
8778
9521
  writeCheckpoint: () => writeCheckpoint
8779
9522
  });
8780
- import path22 from "path";
9523
+ import path23 from "path";
8781
9524
  import { writeFileSync as writeFileSync10, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
8782
9525
  async function createTask(input) {
8783
9526
  const result = await createTaskCore(input);
@@ -8797,8 +9540,8 @@ async function updateTask(input) {
8797
9540
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
8798
9541
  try {
8799
9542
  const agent = String(row.assigned_to);
8800
- const cacheDir = path22.join(EXE_AI_DIR, "session-cache");
8801
- const cachePath = path22.join(cacheDir, `current-task-${agent}.json`);
9543
+ const cacheDir = path23.join(EXE_AI_DIR, "session-cache");
9544
+ const cachePath = path23.join(cacheDir, `current-task-${agent}.json`);
8802
9545
  if (input.status === "in_progress") {
8803
9546
  mkdirSync8(cacheDir, { recursive: true });
8804
9547
  writeFileSync10(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
@@ -8969,15 +9712,15 @@ __export(identity_exports, {
8969
9712
  });
8970
9713
  import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "fs";
8971
9714
  import { readdirSync as readdirSync7 } from "fs";
8972
- import path23 from "path";
9715
+ import path24 from "path";
8973
9716
  import { createHash as createHash2 } from "crypto";
8974
9717
  function ensureDir2() {
8975
- if (!existsSync17(IDENTITY_DIR)) {
8976
- mkdirSync9(IDENTITY_DIR, { recursive: true });
9718
+ if (!existsSync17(IDENTITY_DIR2)) {
9719
+ mkdirSync9(IDENTITY_DIR2, { recursive: true });
8977
9720
  }
8978
9721
  }
8979
9722
  function identityPath(agentId) {
8980
- return path23.join(IDENTITY_DIR, `${agentId}.md`);
9723
+ return path24.join(IDENTITY_DIR2, `${agentId}.md`);
8981
9724
  }
8982
9725
  function parseFrontmatter(raw) {
8983
9726
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
@@ -9050,7 +9793,7 @@ async function updateIdentity(agentId, content, updatedBy) {
9050
9793
  }
9051
9794
  function listIdentities() {
9052
9795
  ensureDir2();
9053
- const files = readdirSync7(IDENTITY_DIR).filter((f) => f.endsWith(".md"));
9796
+ const files = readdirSync7(IDENTITY_DIR2).filter((f) => f.endsWith(".md"));
9054
9797
  const results = [];
9055
9798
  for (const file of files) {
9056
9799
  const agentId = file.replace(".md", "");
@@ -9083,13 +9826,13 @@ ${teamLines.join("\n")}`);
9083
9826
  }
9084
9827
  return parts.join("\n\n");
9085
9828
  }
9086
- var IDENTITY_DIR;
9829
+ var IDENTITY_DIR2;
9087
9830
  var init_identity = __esm({
9088
9831
  "src/lib/identity.ts"() {
9089
9832
  "use strict";
9090
9833
  init_config();
9091
9834
  init_database();
9092
- IDENTITY_DIR = path23.join(EXE_AI_DIR, "identity");
9835
+ IDENTITY_DIR2 = path24.join(EXE_AI_DIR, "identity");
9093
9836
  }
9094
9837
  });
9095
9838
 
@@ -10202,8 +10945,8 @@ __export(wiki_client_exports, {
10202
10945
  listDocuments: () => listDocuments,
10203
10946
  listWorkspaces: () => listWorkspaces
10204
10947
  });
10205
- async function wikiFetch(config2, path40, method = "GET", body) {
10206
- const url = `${config2.baseUrl}/api/v1${path40}`;
10948
+ async function wikiFetch(config2, path41, method = "GET", body) {
10949
+ const url = `${config2.baseUrl}/api/v1${path41}`;
10207
10950
  const headers = {
10208
10951
  Authorization: `Bearer ${config2.apiKey}`,
10209
10952
  "Content-Type": "application/json"
@@ -10236,7 +10979,7 @@ async function wikiFetch(config2, path40, method = "GET", body) {
10236
10979
  }
10237
10980
  }
10238
10981
  if (!response.ok) {
10239
- throw new Error(`Wiki API ${method} ${path40}: ${response.status} ${response.statusText}`);
10982
+ throw new Error(`Wiki API ${method} ${path41}: ${response.status} ${response.statusText}`);
10240
10983
  }
10241
10984
  return response.json();
10242
10985
  } finally {
@@ -10346,12 +11089,12 @@ __export(worker_gate_exports, {
10346
11089
  tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
10347
11090
  });
10348
11091
  import { readdirSync as readdirSync10, writeFileSync as writeFileSync17, unlinkSync as unlinkSync8, mkdirSync as mkdirSync14, existsSync as existsSync25 } from "fs";
10349
- import path33 from "path";
11092
+ import path34 from "path";
10350
11093
  function tryAcquireWorkerSlot() {
10351
11094
  try {
10352
11095
  mkdirSync14(WORKER_PID_DIR, { recursive: true });
10353
11096
  const reservationId = `res-${process.pid}-${Date.now()}`;
10354
- const reservationPath = path33.join(WORKER_PID_DIR, `${reservationId}.pid`);
11097
+ const reservationPath = path34.join(WORKER_PID_DIR, `${reservationId}.pid`);
10355
11098
  writeFileSync17(reservationPath, String(process.pid));
10356
11099
  const files = readdirSync10(WORKER_PID_DIR);
10357
11100
  let alive = 0;
@@ -10369,7 +11112,7 @@ function tryAcquireWorkerSlot() {
10369
11112
  alive++;
10370
11113
  } catch {
10371
11114
  try {
10372
- unlinkSync8(path33.join(WORKER_PID_DIR, f));
11115
+ unlinkSync8(path34.join(WORKER_PID_DIR, f));
10373
11116
  } catch {
10374
11117
  }
10375
11118
  }
@@ -10393,13 +11136,13 @@ function tryAcquireWorkerSlot() {
10393
11136
  function registerWorkerPid(pid) {
10394
11137
  try {
10395
11138
  mkdirSync14(WORKER_PID_DIR, { recursive: true });
10396
- writeFileSync17(path33.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
11139
+ writeFileSync17(path34.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
10397
11140
  } catch {
10398
11141
  }
10399
11142
  }
10400
11143
  function cleanupWorkerPid() {
10401
11144
  try {
10402
- unlinkSync8(path33.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
11145
+ unlinkSync8(path34.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
10403
11146
  } catch {
10404
11147
  }
10405
11148
  }
@@ -10439,9 +11182,9 @@ var init_worker_gate = __esm({
10439
11182
  "src/lib/worker-gate.ts"() {
10440
11183
  "use strict";
10441
11184
  init_config();
10442
- WORKER_PID_DIR = path33.join(EXE_AI_DIR, "worker-pids");
11185
+ WORKER_PID_DIR = path34.join(EXE_AI_DIR, "worker-pids");
10443
11186
  MAX_CONCURRENT_WORKERS = 3;
10444
- BACKFILL_LOCK = path33.join(WORKER_PID_DIR, "backfill.lock");
11187
+ BACKFILL_LOCK = path34.join(WORKER_PID_DIR, "backfill.lock");
10445
11188
  }
10446
11189
  });
10447
11190
 
@@ -10465,7 +11208,7 @@ __export(crdt_sync_exports, {
10465
11208
  });
10466
11209
  import * as Y from "yjs";
10467
11210
  import { readFileSync as readFileSync24, writeFileSync as writeFileSync18, existsSync as existsSync28, mkdirSync as mkdirSync15, unlinkSync as unlinkSync9 } from "fs";
10468
- import path36 from "path";
11211
+ import path37 from "path";
10469
11212
  import { homedir as homedir5 } from "os";
10470
11213
  function getStatePath() {
10471
11214
  return _statePathOverride ?? DEFAULT_STATE_PATH;
@@ -10621,7 +11364,7 @@ function persistState() {
10621
11364
  if (!doc) return;
10622
11365
  try {
10623
11366
  const sp = getStatePath();
10624
- const dir = path36.dirname(sp);
11367
+ const dir = path37.dirname(sp);
10625
11368
  if (!existsSync28(dir)) mkdirSync15(dir, { recursive: true });
10626
11369
  const state = Y.encodeStateAsUpdate(doc);
10627
11370
  writeFileSync18(sp, Buffer.from(state));
@@ -10665,7 +11408,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
10665
11408
  var init_crdt_sync = __esm({
10666
11409
  "src/lib/crdt-sync.ts"() {
10667
11410
  "use strict";
10668
- DEFAULT_STATE_PATH = path36.join(homedir5(), ".exe-os", "crdt-state.bin");
11411
+ DEFAULT_STATE_PATH = path37.join(homedir5(), ".exe-os", "crdt-state.bin");
10669
11412
  _statePathOverride = null;
10670
11413
  doc = null;
10671
11414
  }
@@ -10677,9 +11420,9 @@ init_store();
10677
11420
  init_database();
10678
11421
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10679
11422
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
10680
- import { spawn as spawn3 } from "child_process";
11423
+ import { spawn as spawn4 } from "child_process";
10681
11424
  import { existsSync as existsSync31, openSync as openSync3, mkdirSync as mkdirSync17, closeSync as closeSync3 } from "fs";
10682
- import path39 from "path";
11425
+ import path40 from "path";
10683
11426
  import { fileURLToPath as fileURLToPath5 } from "url";
10684
11427
 
10685
11428
  // src/mcp/tools/recall-my-memory.ts
@@ -10987,8 +11730,8 @@ init_active_agent();
10987
11730
  init_plan_limits();
10988
11731
  import { z as z4 } from "zod";
10989
11732
  import crypto2 from "crypto";
10990
- import { writeFileSync as writeFileSync4 } from "fs";
10991
- import path12 from "path";
11733
+ import { writeFileSync as writeFileSync5 } from "fs";
11734
+ import path14 from "path";
10992
11735
  function registerStoreMemory(server2) {
10993
11736
  server2.registerTool(
10994
11737
  "store_memory",
@@ -11056,8 +11799,8 @@ function registerStoreMemory(server2) {
11056
11799
  if (needsBackfill) {
11057
11800
  try {
11058
11801
  const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
11059
- const flagPath = path12.join(exeDir, "session-cache", "needs-backfill");
11060
- writeFileSync4(flagPath, "1");
11802
+ const flagPath = path14.join(exeDir, "session-cache", "needs-backfill");
11803
+ writeFileSync5(flagPath, "1");
11061
11804
  } catch {
11062
11805
  }
11063
11806
  }
@@ -11081,8 +11824,8 @@ init_active_agent();
11081
11824
  init_employees();
11082
11825
  import { z as z5 } from "zod";
11083
11826
  import crypto3 from "crypto";
11084
- import { writeFileSync as writeFileSync5 } from "fs";
11085
- import path13 from "path";
11827
+ import { writeFileSync as writeFileSync6 } from "fs";
11828
+ import path15 from "path";
11086
11829
  function registerCommitMemory(server2) {
11087
11830
  server2.registerTool(
11088
11831
  "commit_to_long_term_memory",
@@ -11172,8 +11915,8 @@ function registerCommitMemory(server2) {
11172
11915
  if (needsBackfill) {
11173
11916
  try {
11174
11917
  const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
11175
- const flagPath = path13.join(exeDir, "session-cache", "needs-backfill");
11176
- writeFileSync5(flagPath, "1");
11918
+ const flagPath = path15.join(exeDir, "session-cache", "needs-backfill");
11919
+ writeFileSync6(flagPath, "1");
11177
11920
  } catch {
11178
11921
  }
11179
11922
  }
@@ -11439,13 +12182,14 @@ Warning: ${task.warning}`;
11439
12182
  // src/mcp/tools/list-tasks.ts
11440
12183
  init_tasks();
11441
12184
  init_project_name();
12185
+ init_active_agent();
11442
12186
  import { z as z8 } from "zod";
11443
12187
  function registerListTasks(server2) {
11444
12188
  server2.registerTool(
11445
12189
  "list_tasks",
11446
12190
  {
11447
12191
  title: "List Tasks",
11448
- description: "Query tasks by assignee, status, project, or priority. Defaults to current project. Pass project_name='all' to see all projects.",
12192
+ description: "Query tasks by assignee, status, project, or priority. Defaults to current project. Pass project_name='all' for all projects. When querying your own tasks, project filter is skipped automatically.",
11449
12193
  inputSchema: {
11450
12194
  assigned_to: z8.string().optional().describe("Filter by employee name"),
11451
12195
  status: z8.enum(["open", "in_progress", "done", "blocked", "cancelled"]).optional().describe("Filter by status"),
@@ -11455,7 +12199,16 @@ function registerListTasks(server2) {
11455
12199
  },
11456
12200
  async ({ assigned_to, status, project_name, priority }) => {
11457
12201
  try {
11458
- const resolvedProject = project_name === "all" ? void 0 : project_name ?? getProjectName();
12202
+ let resolvedProject;
12203
+ if (project_name === "all") {
12204
+ resolvedProject = void 0;
12205
+ } else if (project_name) {
12206
+ resolvedProject = project_name;
12207
+ } else {
12208
+ const { agentId } = getActiveAgent();
12209
+ const queryingSelf = !assigned_to || assigned_to === agentId;
12210
+ resolvedProject = queryingSelf ? void 0 : getProjectName();
12211
+ }
11459
12212
  const tasks = await listTasks({
11460
12213
  assignedTo: assigned_to,
11461
12214
  status,
@@ -13210,9 +13963,9 @@ import { z as z29 } from "zod";
13210
13963
  // src/automation/trigger-engine.ts
13211
13964
  import { readFileSync as readFileSync16, writeFileSync as writeFileSync12, existsSync as existsSync18, mkdirSync as mkdirSync10 } from "fs";
13212
13965
  import { randomUUID as randomUUID4 } from "crypto";
13213
- import path24 from "path";
13214
- import os10 from "os";
13215
- var TRIGGERS_PATH = path24.join(os10.homedir(), ".exe-os", "triggers.json");
13966
+ import path25 from "path";
13967
+ import os11 from "os";
13968
+ var TRIGGERS_PATH = path25.join(os11.homedir(), ".exe-os", "triggers.json");
13216
13969
  function loadTriggers(project) {
13217
13970
  if (!existsSync18(TRIGGERS_PATH)) return [];
13218
13971
  try {
@@ -13228,7 +13981,7 @@ function loadTriggers(project) {
13228
13981
  }
13229
13982
  }
13230
13983
  function saveTriggers(triggers) {
13231
- const dir = path24.dirname(TRIGGERS_PATH);
13984
+ const dir = path25.dirname(TRIGGERS_PATH);
13232
13985
  if (!existsSync18(dir)) mkdirSync10(dir, { recursive: true });
13233
13986
  writeFileSync12(TRIGGERS_PATH, JSON.stringify(triggers, null, 2), "utf-8");
13234
13987
  }
@@ -13515,23 +14268,23 @@ import { z as z31 } from "zod";
13515
14268
 
13516
14269
  // src/automation/starter-packs/index.ts
13517
14270
  import { readFileSync as readFileSync17, readdirSync as readdirSync8, existsSync as existsSync19 } from "fs";
13518
- import path25 from "path";
14271
+ import path26 from "path";
13519
14272
  import { fileURLToPath as fileURLToPath3 } from "url";
13520
- var __dirname = path25.dirname(fileURLToPath3(import.meta.url));
14273
+ var __dirname = path26.dirname(fileURLToPath3(import.meta.url));
13521
14274
  function listPacks() {
13522
- const packsDir = path25.join(__dirname, ".");
14275
+ const packsDir = path26.join(__dirname, ".");
13523
14276
  if (!existsSync19(packsDir)) return [];
13524
14277
  return readdirSync8(packsDir, { withFileTypes: true }).filter(
13525
- (d) => d.isDirectory() && existsSync19(path25.join(packsDir, d.name, "custom-objects.json"))
14278
+ (d) => d.isDirectory() && existsSync19(path26.join(packsDir, d.name, "custom-objects.json"))
13526
14279
  ).map((d) => d.name);
13527
14280
  }
13528
14281
  function loadPack(industry) {
13529
- const packDir = path25.join(__dirname, industry);
13530
- const objectsPath = path25.join(packDir, "custom-objects.json");
13531
- const triggersPath = path25.join(packDir, "triggers.json");
13532
- const wikiDir = path25.join(packDir, "wiki-seeds");
13533
- const manifestPath = path25.join(packDir, "pack.json");
13534
- const identityContextPath = path25.join(packDir, "identity-context.md");
14282
+ const packDir = path26.join(__dirname, industry);
14283
+ const objectsPath = path26.join(packDir, "custom-objects.json");
14284
+ const triggersPath = path26.join(packDir, "triggers.json");
14285
+ const wikiDir = path26.join(packDir, "wiki-seeds");
14286
+ const manifestPath = path26.join(packDir, "pack.json");
14287
+ const identityContextPath = path26.join(packDir, "identity-context.md");
13535
14288
  if (!existsSync19(objectsPath)) return null;
13536
14289
  let customObjects = [];
13537
14290
  try {
@@ -13555,7 +14308,7 @@ function loadPack(industry) {
13555
14308
  if (existsSync19(wikiDir)) {
13556
14309
  const files = readdirSync8(wikiDir).filter((f) => f.endsWith(".md"));
13557
14310
  for (const file of files) {
13558
- const content = readFileSync17(path25.join(wikiDir, file), "utf-8");
14311
+ const content = readFileSync17(path26.join(wikiDir, file), "utf-8");
13559
14312
  const titleMatch = content.match(/^#\s+(.+)/m);
13560
14313
  wikiSeeds.push({
13561
14314
  filename: file,
@@ -13633,7 +14386,7 @@ function applyPack(industry, project) {
13633
14386
  // src/lib/client-coo.ts
13634
14387
  init_config();
13635
14388
  import { existsSync as existsSync20, mkdirSync as mkdirSync11, writeFileSync as writeFileSync13 } from "fs";
13636
- import path26 from "path";
14389
+ import path27 from "path";
13637
14390
 
13638
14391
  // src/lib/employee-templates.ts
13639
14392
  init_global_procedures();
@@ -13771,10 +14524,10 @@ var ClientCOOClobberError = class extends Error {
13771
14524
  var COO_ROLE = "Chief Operating Officer";
13772
14525
  var FEEDBACK_BEHAVIOR_CONTENT = "Tag exe-os issues with needs_improvement \u2014 this is the feedback loop that surfaces bugs, gaps, and friction to the founder each Monday.";
13773
14526
  async function provisionClientCOO(vars, opts = {}) {
13774
- const identityDir = opts.identityDir ?? path26.join(EXE_AI_DIR, "identity");
14527
+ const identityDir = opts.identityDir ?? path27.join(EXE_AI_DIR, "identity");
13775
14528
  const rosterPath = opts.employeesPath ?? EMPLOYEES_PATH;
13776
14529
  const storeFeedbackBehavior = opts.storeFeedbackBehavior ?? true;
13777
- const identityPath2 = path26.join(identityDir, `${vars.agent_name}.md`);
14530
+ const identityPath2 = path27.join(identityDir, `${vars.agent_name}.md`);
13778
14531
  if (existsSync20(identityPath2)) {
13779
14532
  throw new ClientCOOClobberError(vars.agent_name, identityPath2);
13780
14533
  }
@@ -14672,7 +15425,7 @@ function registerUpdateWikiPage(server2) {
14672
15425
  import { z as z37 } from "zod";
14673
15426
  import { execFile } from "child_process";
14674
15427
  import { promisify } from "util";
14675
- import path27 from "path";
15428
+ import path28 from "path";
14676
15429
  import { existsSync as existsSync21 } from "fs";
14677
15430
 
14678
15431
  // src/lib/hostinger-api.ts
@@ -14721,9 +15474,9 @@ var HostingerApiClient = class {
14721
15474
  }
14722
15475
  this.lastRequestTime = Date.now();
14723
15476
  }
14724
- async request(method, path40, body) {
15477
+ async request(method, path41, body) {
14725
15478
  await this.rateLimit();
14726
- const url = `${this.baseUrl}${path40}`;
15479
+ const url = `${this.baseUrl}${path41}`;
14727
15480
  const headers = {
14728
15481
  Authorization: `Bearer ${this.apiKey}`,
14729
15482
  "Content-Type": "application/json",
@@ -14796,8 +15549,8 @@ async function requestCloudflare(cfApiToken, zoneId, options) {
14796
15549
  }
14797
15550
  return envelope.result;
14798
15551
  }
14799
- function buildUrl(zoneId, path40 = "/dns_records", query) {
14800
- const normalizedPath = path40.startsWith("/") ? path40 : `/${path40}`;
15552
+ function buildUrl(zoneId, path41 = "/dns_records", query) {
15553
+ const normalizedPath = path41.startsWith("/") ? path41 : `/${path41}`;
14801
15554
  const url = new URL(
14802
15555
  `${CLOUDFLARE_API_BASE_URL}/zones/${zoneId}${normalizedPath}`
14803
15556
  );
@@ -15010,12 +15763,12 @@ async function waitForReady(client, vpsId) {
15010
15763
  }
15011
15764
  async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
15012
15765
  const safeClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "-");
15013
- const playbookDir = path27.resolve(process.cwd(), "infrastructure", "ansible");
15014
- const playbookPath = path27.join(playbookDir, "deploy.yml");
15015
- const inventoryPath = path27.join(playbookDir, "inventory", "hosts.yml");
15016
- const clientVarsPath = path27.join(playbookDir, "vars", `${safeClientName}.yml`);
15017
- const varsDir = path27.join(playbookDir, "vars");
15018
- if (!path27.resolve(clientVarsPath).startsWith(path27.resolve(varsDir))) {
15766
+ const playbookDir = path28.resolve(process.cwd(), "infrastructure", "ansible");
15767
+ const playbookPath = path28.join(playbookDir, "deploy.yml");
15768
+ const inventoryPath = path28.join(playbookDir, "inventory", "hosts.yml");
15769
+ const clientVarsPath = path28.join(playbookDir, "vars", `${safeClientName}.yml`);
15770
+ const varsDir = path28.join(playbookDir, "vars");
15771
+ if (!path28.resolve(clientVarsPath).startsWith(path28.resolve(varsDir))) {
15019
15772
  throw new Error(`Invalid client name for vars path: ${clientName}`);
15020
15773
  }
15021
15774
  const args = [
@@ -15105,7 +15858,7 @@ function buildInventoryRecord(params) {
15105
15858
  // src/mcp/tools/export-orchestration.ts
15106
15859
  init_active_agent();
15107
15860
  import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync15 } from "fs";
15108
- import path29 from "path";
15861
+ import path30 from "path";
15109
15862
  import { z as z38 } from "zod";
15110
15863
 
15111
15864
  // src/lib/orchestration-package.ts
@@ -15114,8 +15867,8 @@ init_identity();
15114
15867
  init_platform_procedures();
15115
15868
  import { randomUUID as randomUUID5 } from "crypto";
15116
15869
  import { copyFileSync, existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "fs";
15117
- import os11 from "os";
15118
- import path28 from "path";
15870
+ import os12 from "os";
15871
+ import path29 from "path";
15119
15872
  var PACKAGE_VERSION = "1.0";
15120
15873
  var ROSTER_FILENAME = "exe-employees.json";
15121
15874
  var ROSTER_BACKUP_FILENAME = "exe-employees.json.bak";
@@ -15181,10 +15934,10 @@ function validateProcedureEntry(value, index) {
15181
15934
  };
15182
15935
  }
15183
15936
  function getRosterPath() {
15184
- return path28.join(os11.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
15937
+ return path29.join(os12.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
15185
15938
  }
15186
15939
  function getBackupPath() {
15187
- return path28.join(os11.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
15940
+ return path29.join(os12.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
15188
15941
  }
15189
15942
  function readRosterFile() {
15190
15943
  const rosterPath = getRosterPath();
@@ -15201,7 +15954,7 @@ function writeRosterFile(roster) {
15201
15954
  throw new Error("Refusing to write empty roster \u2014 this would delete all employees");
15202
15955
  }
15203
15956
  const rosterPath = getRosterPath();
15204
- mkdirSync12(path28.dirname(rosterPath), { recursive: true });
15957
+ mkdirSync12(path29.dirname(rosterPath), { recursive: true });
15205
15958
  if (existsSync22(rosterPath)) {
15206
15959
  const currentRoster = readRosterFile();
15207
15960
  if (roster.length < currentRoster.length) {
@@ -15473,7 +16226,7 @@ function registerExportOrchestration(server2) {
15473
16226
  try {
15474
16227
  await initStore();
15475
16228
  const pkg = await exportOrchestration(getActiveAgent().agentId);
15476
- mkdirSync13(path29.dirname(output_path), { recursive: true });
16229
+ mkdirSync13(path30.dirname(output_path), { recursive: true });
15477
16230
  writeFileSync15(output_path, `${JSON.stringify(pkg, null, 2)}
15478
16231
  `, "utf-8");
15479
16232
  return {
@@ -15674,17 +16427,17 @@ function registerQueryConversations(server2) {
15674
16427
  // src/mcp/tools/load-skill.ts
15675
16428
  import { z as z41 } from "zod";
15676
16429
  import { readFileSync as readFileSync20, readdirSync as readdirSync9, statSync as statSync3 } from "fs";
15677
- import path30 from "path";
16430
+ import path31 from "path";
15678
16431
  import { homedir as homedir2 } from "os";
15679
- var SKILLS_DIR = path30.join(homedir2(), ".claude", "skills");
16432
+ var SKILLS_DIR = path31.join(homedir2(), ".claude", "skills");
15680
16433
  function listAvailableSkills() {
15681
16434
  try {
15682
16435
  const entries = readdirSync9(SKILLS_DIR);
15683
16436
  return entries.filter((entry) => {
15684
16437
  try {
15685
- const entryPath = path30.join(SKILLS_DIR, entry);
16438
+ const entryPath = path31.join(SKILLS_DIR, entry);
15686
16439
  if (!statSync3(entryPath).isDirectory()) return false;
15687
- const skillFile = path30.join(entryPath, "SKILL.md");
16440
+ const skillFile = path31.join(entryPath, "SKILL.md");
15688
16441
  statSync3(skillFile);
15689
16442
  return true;
15690
16443
  } catch {
@@ -15727,8 +16480,8 @@ ${skills.map((s) => `- ${s}`).join("\n")}`
15727
16480
  }]
15728
16481
  };
15729
16482
  }
15730
- const sanitized = path30.basename(skill_name);
15731
- const skillFile = path30.join(SKILLS_DIR, sanitized, "SKILL.md");
16483
+ const sanitized = path31.basename(skill_name);
16484
+ const skillFile = path31.join(SKILLS_DIR, sanitized, "SKILL.md");
15732
16485
  try {
15733
16486
  const content = readFileSync20(skillFile, "utf-8");
15734
16487
  return {
@@ -16369,8 +17122,8 @@ init_database();
16369
17122
  import { readdir } from "fs/promises";
16370
17123
  import { createReadStream } from "fs";
16371
17124
  import { createInterface } from "readline";
16372
- import path31 from "path";
16373
- import os12 from "os";
17125
+ import path32 from "path";
17126
+ import os13 from "os";
16374
17127
  var MODEL_PRICING = {
16375
17128
  // Opus 4.5+ ($5/$25 — Anthropic price drop from original Opus 4)
16376
17129
  "claude-opus-4-7": { input: 5 / 1e6, output: 25 / 1e6, cacheRead: 0.5 / 1e6, cacheWrite: 6.25 / 1e6 },
@@ -16418,18 +17171,18 @@ async function getAgentSpend(period = "7d") {
16418
17171
  for (const row of result.rows) {
16419
17172
  sessionAgent.set(row.session_uuid, row.agent_id);
16420
17173
  }
16421
- const claudeDir = path31.join(os12.homedir(), ".claude", "projects");
17174
+ const claudeDir = path32.join(os13.homedir(), ".claude", "projects");
16422
17175
  let projectDirs = [];
16423
17176
  try {
16424
17177
  const entries = await readdir(claudeDir);
16425
- projectDirs = entries.map((e) => path31.join(claudeDir, e));
17178
+ projectDirs = entries.map((e) => path32.join(claudeDir, e));
16426
17179
  } catch {
16427
17180
  return [];
16428
17181
  }
16429
17182
  const agentTotals = /* @__PURE__ */ new Map();
16430
17183
  for (const [sessionUuid, agentId] of sessionAgent) {
16431
17184
  for (const dir of projectDirs) {
16432
- const jsonlPath = path31.join(dir, `${sessionUuid}.jsonl`);
17185
+ const jsonlPath = path32.join(dir, `${sessionUuid}.jsonl`);
16433
17186
  try {
16434
17187
  const usage = await extractSessionUsage(jsonlPath);
16435
17188
  if (usage.input === 0 && usage.output === 0) continue;
@@ -17007,12 +17760,12 @@ function registerExportGraph(server2) {
17007
17760
  }
17008
17761
  const html = await exportGraphHTML(client);
17009
17762
  const fs = await import("fs");
17010
- const path40 = await import("path");
17011
- const os14 = await import("os");
17012
- const outDir = path40.join(os14.homedir(), ".exe-os", "exports");
17763
+ const path41 = await import("path");
17764
+ const os15 = await import("os");
17765
+ const outDir = path41.join(os15.homedir(), ".exe-os", "exports");
17013
17766
  fs.mkdirSync(outDir, { recursive: true });
17014
17767
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
17015
- const filePath = path40.join(outDir, `graph-${timestamp}.html`);
17768
+ const filePath = path41.join(outDir, `graph-${timestamp}.html`);
17016
17769
  fs.writeFileSync(filePath, html, "utf-8");
17017
17770
  return {
17018
17771
  content: [
@@ -17234,9 +17987,9 @@ function registerListAgentSessions(server2) {
17234
17987
  // src/mcp/tools/get-daemon-health.ts
17235
17988
  import { z as z56 } from "zod";
17236
17989
  import { existsSync as existsSync23, readFileSync as readFileSync21 } from "fs";
17237
- import path32 from "path";
17990
+ import path33 from "path";
17238
17991
  import { homedir as homedir3 } from "os";
17239
- var PID_PATH2 = path32.join(homedir3(), ".exe-os", "exed.pid");
17992
+ var PID_PATH2 = path33.join(homedir3(), ".exe-os", "exed.pid");
17240
17993
  function formatUptime(seconds) {
17241
17994
  const h = Math.floor(seconds / 3600);
17242
17995
  const m = Math.floor(seconds % 3600 / 60);
@@ -17426,8 +18179,8 @@ init_worker_gate();
17426
18179
  init_config();
17427
18180
  import { z as z58 } from "zod";
17428
18181
  import { readdirSync as readdirSync11, existsSync as existsSync26 } from "fs";
17429
- import path34 from "path";
17430
- var WORKER_PID_DIR2 = path34.join(EXE_AI_DIR, "worker-pids");
18182
+ import path35 from "path";
18183
+ var WORKER_PID_DIR2 = path35.join(EXE_AI_DIR, "worker-pids");
17431
18184
  function countAliveWorkers() {
17432
18185
  let alive = 0;
17433
18186
  let stale = 0;
@@ -17503,7 +18256,7 @@ import { z as z59 } from "zod";
17503
18256
  // src/bin/exe-doctor.ts
17504
18257
  init_store();
17505
18258
  init_database();
17506
- import os13 from "os";
18259
+ import os14 from "os";
17507
18260
 
17508
18261
  // src/lib/is-main.ts
17509
18262
  import { realpathSync } from "fs";
@@ -17523,7 +18276,7 @@ function isMainModule(importMetaUrl) {
17523
18276
  // src/bin/exe-doctor.ts
17524
18277
  import { existsSync as existsSync27, readFileSync as readFileSync23 } from "fs";
17525
18278
  import { spawn as spawn2 } from "child_process";
17526
- import path35 from "path";
18279
+ import path36 from "path";
17527
18280
  import { randomUUID as randomUUID7 } from "crypto";
17528
18281
 
17529
18282
  // src/lib/conflict-detector.ts
@@ -17926,7 +18679,7 @@ async function auditOrphanedProjects(client) {
17926
18679
  for (const row of result.rows) {
17927
18680
  const name = row.project_name;
17928
18681
  const count = Number(row.cnt);
17929
- const exists = existsSync27(path35.join(home, name)) || existsSync27(path35.join(home, "..", name)) || existsSync27(path35.join(process.cwd(), "..", name));
18682
+ const exists = existsSync27(path36.join(home, name)) || existsSync27(path36.join(home, "..", name)) || existsSync27(path36.join(process.cwd(), "..", name));
17930
18683
  if (!exists) {
17931
18684
  orphans.push({ project_name: name, count });
17932
18685
  }
@@ -17934,7 +18687,7 @@ async function auditOrphanedProjects(client) {
17934
18687
  return orphans;
17935
18688
  }
17936
18689
  function auditHookHealth() {
17937
- const logPath = path35.join(
18690
+ const logPath = path36.join(
17938
18691
  process.env.HOME ?? process.env.USERPROFILE ?? "",
17939
18692
  ".exe-os",
17940
18693
  "logs",
@@ -18020,7 +18773,7 @@ function formatReport(report, flags) {
18020
18773
  }
18021
18774
  lines.push("");
18022
18775
  }
18023
- const totalMemGB = os13.totalmem() / (1024 * 1024 * 1024);
18776
+ const totalMemGB = os14.totalmem() / (1024 * 1024 * 1024);
18024
18777
  const isLowMemSystem = totalMemGB <= 8;
18025
18778
  if (isLowMemSystem && report.nullVectors > 0) {
18026
18779
  lines.push(`\u{1F7E2} Null vectors: ${fmtNum(report.nullVectors)} / ${fmtNum(s.total)} (expected \u2014 8GB system, keyword search mode)`);
@@ -18142,7 +18895,7 @@ async function fixNullVectors() {
18142
18895
  }
18143
18896
  }
18144
18897
  const npmRoot = (await import("child_process")).execSync("npm root -g", { encoding: "utf8" }).trim();
18145
- const backfillPath = path35.join(npmRoot, "exe-os", "dist", "bin", "backfill-vectors.js");
18898
+ const backfillPath = path36.join(npmRoot, "exe-os", "dist", "bin", "backfill-vectors.js");
18146
18899
  return new Promise((resolve, reject) => {
18147
18900
  const child = spawn2("node", [backfillPath], { stdio: "inherit" });
18148
18901
  if (child.pid) registerWorkerPid2(child.pid);
@@ -18342,7 +19095,7 @@ import { z as z60 } from "zod";
18342
19095
  init_database();
18343
19096
  import { readFileSync as readFileSync25, writeFileSync as writeFileSync19, existsSync as existsSync29, readdirSync as readdirSync12, mkdirSync as mkdirSync16, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
18344
19097
  import crypto16 from "crypto";
18345
- import path37 from "path";
19098
+ import path38 from "path";
18346
19099
  import { homedir as homedir6 } from "os";
18347
19100
 
18348
19101
  // src/lib/crypto.ts
@@ -18416,7 +19169,7 @@ function sqlSafe(v) {
18416
19169
  }
18417
19170
  function logError(msg) {
18418
19171
  try {
18419
- const logPath = path37.join(homedir6(), ".exe-os", "workers.log");
19172
+ const logPath = path38.join(homedir6(), ".exe-os", "workers.log");
18420
19173
  appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
18421
19174
  `);
18422
19175
  } catch {
@@ -18425,7 +19178,7 @@ function logError(msg) {
18425
19178
  var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
18426
19179
  var FETCH_TIMEOUT_MS4 = 3e4;
18427
19180
  var PUSH_BATCH_SIZE = 5e3;
18428
- var ROSTER_LOCK_PATH = path37.join(EXE_AI_DIR, "roster-merge.lock");
19181
+ var ROSTER_LOCK_PATH = path38.join(EXE_AI_DIR, "roster-merge.lock");
18429
19182
  var LOCK_STALE_MS = 3e4;
18430
19183
  async function withRosterLock(fn) {
18431
19184
  try {
@@ -18816,7 +19569,7 @@ async function cloudSync(config2) {
18816
19569
  try {
18817
19570
  const employees = await loadEmployees();
18818
19571
  rosterResult.employees = employees.length;
18819
- const idDir = path37.join(EXE_AI_DIR, "identity");
19572
+ const idDir = path38.join(EXE_AI_DIR, "identity");
18820
19573
  if (existsSync29(idDir)) {
18821
19574
  rosterResult.identities = readdirSync12(idDir).filter((f) => f.endsWith(".md")).length;
18822
19575
  }
@@ -18835,7 +19588,7 @@ async function cloudSync(config2) {
18835
19588
  roster: rosterResult
18836
19589
  };
18837
19590
  }
18838
- var ROSTER_DELETIONS_PATH = path37.join(EXE_AI_DIR, "roster-deletions.json");
19591
+ var ROSTER_DELETIONS_PATH = path38.join(EXE_AI_DIR, "roster-deletions.json");
18839
19592
  function consumeRosterDeletions() {
18840
19593
  try {
18841
19594
  if (!existsSync29(ROSTER_DELETIONS_PATH)) return [];
@@ -18847,9 +19600,9 @@ function consumeRosterDeletions() {
18847
19600
  }
18848
19601
  }
18849
19602
  function buildRosterBlob(paths) {
18850
- const rosterPath = paths?.rosterPath ?? path37.join(EXE_AI_DIR, "exe-employees.json");
18851
- const identityDir = paths?.identityDir ?? path37.join(EXE_AI_DIR, "identity");
18852
- const configPath = paths?.configPath ?? path37.join(EXE_AI_DIR, "config.json");
19603
+ const rosterPath = paths?.rosterPath ?? path38.join(EXE_AI_DIR, "exe-employees.json");
19604
+ const identityDir = paths?.identityDir ?? path38.join(EXE_AI_DIR, "identity");
19605
+ const configPath = paths?.configPath ?? path38.join(EXE_AI_DIR, "config.json");
18853
19606
  let roster = [];
18854
19607
  if (existsSync29(rosterPath)) {
18855
19608
  try {
@@ -18861,7 +19614,7 @@ function buildRosterBlob(paths) {
18861
19614
  if (existsSync29(identityDir)) {
18862
19615
  for (const file of readdirSync12(identityDir).filter((f) => f.endsWith(".md"))) {
18863
19616
  try {
18864
- identities[file] = readFileSync25(path37.join(identityDir, file), "utf-8");
19617
+ identities[file] = readFileSync25(path38.join(identityDir, file), "utf-8");
18865
19618
  } catch {
18866
19619
  }
18867
19620
  }
@@ -18874,7 +19627,7 @@ function buildRosterBlob(paths) {
18874
19627
  }
18875
19628
  }
18876
19629
  let agentConfig;
18877
- const agentConfigPath = path37.join(EXE_AI_DIR, "agent-config.json");
19630
+ const agentConfigPath = path38.join(EXE_AI_DIR, "agent-config.json");
18878
19631
  if (existsSync29(agentConfigPath)) {
18879
19632
  try {
18880
19633
  agentConfig = JSON.parse(readFileSync25(agentConfigPath, "utf-8"));
@@ -18953,7 +19706,7 @@ async function cloudPullRoster(config2) {
18953
19706
  }
18954
19707
  }
18955
19708
  function mergeConfig(remoteConfig, configPath) {
18956
- const cfgPath = configPath ?? path37.join(EXE_AI_DIR, "config.json");
19709
+ const cfgPath = configPath ?? path38.join(EXE_AI_DIR, "config.json");
18957
19710
  let local = {};
18958
19711
  if (existsSync29(cfgPath)) {
18959
19712
  try {
@@ -18962,14 +19715,14 @@ function mergeConfig(remoteConfig, configPath) {
18962
19715
  }
18963
19716
  }
18964
19717
  const merged = { ...remoteConfig, ...local };
18965
- const dir = path37.dirname(cfgPath);
19718
+ const dir = path38.dirname(cfgPath);
18966
19719
  if (!existsSync29(dir)) mkdirSync16(dir, { recursive: true });
18967
19720
  writeFileSync19(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
18968
19721
  }
18969
19722
  async function mergeRosterFromRemote(remote, paths) {
18970
19723
  return withRosterLock(async () => {
18971
19724
  const rosterPath = paths?.rosterPath ?? void 0;
18972
- const identityDir = paths?.identityDir ?? path37.join(EXE_AI_DIR, "identity");
19725
+ const identityDir = paths?.identityDir ?? path38.join(EXE_AI_DIR, "identity");
18973
19726
  const localEmployees = await loadEmployees(rosterPath);
18974
19727
  const localNames = new Set(localEmployees.map((e) => e.name));
18975
19728
  let added = 0;
@@ -18991,7 +19744,7 @@ async function mergeRosterFromRemote(remote, paths) {
18991
19744
  const remoteIdentity = remote.identities[matchedKey];
18992
19745
  if (remoteIdentity) {
18993
19746
  if (!existsSync29(identityDir)) mkdirSync16(identityDir, { recursive: true });
18994
- const idPath = path37.join(identityDir, `${remoteEmp.name}.md`);
19747
+ const idPath = path38.join(identityDir, `${remoteEmp.name}.md`);
18995
19748
  let localIdentity = null;
18996
19749
  try {
18997
19750
  localIdentity = existsSync29(idPath) ? readFileSync25(idPath, "utf-8") : null;
@@ -19024,7 +19777,7 @@ async function mergeRosterFromRemote(remote, paths) {
19024
19777
  }
19025
19778
  if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
19026
19779
  try {
19027
- const agentConfigPath = path37.join(EXE_AI_DIR, "agent-config.json");
19780
+ const agentConfigPath = path38.join(EXE_AI_DIR, "agent-config.json");
19028
19781
  let local = {};
19029
19782
  if (existsSync29(agentConfigPath)) {
19030
19783
  try {
@@ -19753,15 +20506,515 @@ function registerGetLicenseStatus(server2) {
19753
20506
  );
19754
20507
  }
19755
20508
 
19756
- // src/mcp/tools/people-roster.ts
20509
+ // src/mcp/tools/backup-vps.ts
20510
+ init_keychain();
19757
20511
  import { z as z64 } from "zod";
19758
20512
 
20513
+ // src/lib/vps-backup.ts
20514
+ import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
20515
+ import { Readable } from "stream";
20516
+ import { spawn as spawn3 } from "child_process";
20517
+ import {
20518
+ DeleteObjectsCommand,
20519
+ GetObjectCommand,
20520
+ ListObjectsV2Command,
20521
+ PutObjectCommand,
20522
+ S3Client
20523
+ } from "@aws-sdk/client-s3";
20524
+ import {
20525
+ constants as brotliConstants,
20526
+ brotliCompressSync as brotliCompressSync2,
20527
+ brotliDecompressSync as brotliDecompressSync2
20528
+ } from "zlib";
20529
+ var ALGORITHM2 = "aes-256-gcm";
20530
+ var IV_BYTES = 12;
20531
+ var TAG_BYTES = 16;
20532
+ var CLIENT_ID_FALLBACK = "default";
20533
+ var BACKUP_EXT = ".pgdump.br.enc";
20534
+ var BACKUP_PREFIX = "backups/";
20535
+ function makeS3Client(opts) {
20536
+ return new S3Client({
20537
+ region: "auto",
20538
+ endpoint: opts.r2Endpoint,
20539
+ credentials: {
20540
+ accessKeyId: opts.r2AccessKeyId,
20541
+ secretAccessKey: opts.r2SecretAccessKey
20542
+ }
20543
+ });
20544
+ }
20545
+ function parsePostgresClientId(databaseUrl) {
20546
+ try {
20547
+ const parsed = new URL(databaseUrl);
20548
+ const candidate = parsed.pathname.split("/").filter(Boolean).pop() ?? CLIENT_ID_FALLBACK;
20549
+ const name = decodeURIComponent(candidate).trim() || CLIENT_ID_FALLBACK;
20550
+ return name.replace(/[^a-zA-Z0-9._-]/g, "_");
20551
+ } catch {
20552
+ return CLIENT_ID_FALLBACK;
20553
+ }
20554
+ }
20555
+ function makeBackupTimestamp(date = /* @__PURE__ */ new Date()) {
20556
+ return date.toISOString().replace(/[:.]/g, "-");
20557
+ }
20558
+ function makeBackupKey(databaseUrl, date = /* @__PURE__ */ new Date()) {
20559
+ const clientId = parsePostgresClientId(databaseUrl);
20560
+ return `${BACKUP_PREFIX}${clientId}/${makeBackupTimestamp(date)}${BACKUP_EXT}`;
20561
+ }
20562
+ function validateEncryptionKey(encryptionKey) {
20563
+ if (encryptionKey.length !== 32) {
20564
+ throw new Error(`Encryption key must be 32 bytes for AES-256, got ${encryptionKey.length}`);
20565
+ }
20566
+ }
20567
+ function encryptBlob(input, encryptionKey) {
20568
+ validateEncryptionKey(encryptionKey);
20569
+ const iv = randomBytes(IV_BYTES);
20570
+ const cipher = createCipheriv(ALGORITHM2, encryptionKey, iv);
20571
+ const ciphertext = Buffer.concat([cipher.update(input), cipher.final()]);
20572
+ const tag = cipher.getAuthTag();
20573
+ return Buffer.concat([iv, tag, ciphertext]);
20574
+ }
20575
+ function decryptBlob(input, encryptionKey) {
20576
+ validateEncryptionKey(encryptionKey);
20577
+ if (input.length < IV_BYTES + TAG_BYTES) {
20578
+ throw new Error("Encrypted backup blob is too short");
20579
+ }
20580
+ const iv = input.subarray(0, IV_BYTES);
20581
+ const tag = input.subarray(IV_BYTES, IV_BYTES + TAG_BYTES);
20582
+ const ciphertext = input.subarray(IV_BYTES + TAG_BYTES);
20583
+ const decipher = createDecipheriv(ALGORITHM2, encryptionKey, iv);
20584
+ decipher.setAuthTag(tag);
20585
+ return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
20586
+ }
20587
+ function compress2(input) {
20588
+ if (input.length === 0) return Buffer.alloc(0);
20589
+ return brotliCompressSync2(input, {
20590
+ params: {
20591
+ [brotliConstants.BROTLI_PARAM_QUALITY]: 4
20592
+ }
20593
+ });
20594
+ }
20595
+ function decompress2(input) {
20596
+ if (input.length === 0) return Buffer.alloc(0);
20597
+ return brotliDecompressSync2(input);
20598
+ }
20599
+ async function runProcess(command, args, input) {
20600
+ return new Promise((resolve, reject) => {
20601
+ const child = spawn3(command, args, {
20602
+ stdio: ["pipe", "pipe", "pipe"]
20603
+ });
20604
+ const stdoutChunks = [];
20605
+ const stderrChunks = [];
20606
+ child.stdout.on("data", (chunk) => {
20607
+ stdoutChunks.push(chunk);
20608
+ });
20609
+ child.stderr.on("data", (chunk) => {
20610
+ stderrChunks.push(chunk);
20611
+ });
20612
+ child.on("error", (error) => {
20613
+ reject(error);
20614
+ });
20615
+ child.on("close", (code) => {
20616
+ if (code !== 0) {
20617
+ const stderr = Buffer.concat(stderrChunks).toString("utf8").trim();
20618
+ reject(new Error(`Command failed: ${command} ${args.join(" ")} (code ${code})${stderr ? `: ${stderr}` : ""}`));
20619
+ return;
20620
+ }
20621
+ resolve(Buffer.concat(stdoutChunks));
20622
+ });
20623
+ if (input) {
20624
+ child.stdin.write(input);
20625
+ child.stdin.end();
20626
+ }
20627
+ });
20628
+ }
20629
+ async function listBackupObjects(client, bucket, prefix) {
20630
+ const objects = [];
20631
+ let token;
20632
+ do {
20633
+ const command = new ListObjectsV2Command({
20634
+ Bucket: bucket,
20635
+ Prefix: prefix,
20636
+ ContinuationToken: token
20637
+ });
20638
+ const response = await client.send(command);
20639
+ const items = response.Contents ?? [];
20640
+ for (const obj of items) {
20641
+ if (!obj.Key || !obj.LastModified) continue;
20642
+ objects.push({
20643
+ key: obj.Key,
20644
+ size: typeof obj.Size === "number" ? obj.Size : Number(obj.Size ?? 0),
20645
+ lastModified: obj.LastModified
20646
+ });
20647
+ }
20648
+ token = response.IsTruncated ? response.NextContinuationToken : void 0;
20649
+ } while (token);
20650
+ return objects;
20651
+ }
20652
+ function selectRetentionKeep(keys) {
20653
+ if (keys.length <= 11) {
20654
+ return new Set(keys.map((k) => k.key));
20655
+ }
20656
+ const newest = [...keys].sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
20657
+ const keep = /* @__PURE__ */ new Set();
20658
+ const seenDays = /* @__PURE__ */ new Set();
20659
+ const seenWeeks = /* @__PURE__ */ new Set();
20660
+ for (const backup of newest) {
20661
+ if (keep.size >= 11) break;
20662
+ const day = backup.lastModified.toISOString().slice(0, 10);
20663
+ if (seenDays.size < 7 && !seenDays.has(day)) {
20664
+ seenDays.add(day);
20665
+ keep.add(backup.key);
20666
+ }
20667
+ }
20668
+ const dayCount = seenDays.size;
20669
+ if (dayCount >= 7) {
20670
+ for (const backup of newest) {
20671
+ if (keep.has(backup.key) || keep.size >= 11) {
20672
+ continue;
20673
+ }
20674
+ const week = getWeekBucket(backup.lastModified);
20675
+ if (seenWeeks.size < 4 && !seenWeeks.has(week)) {
20676
+ seenWeeks.add(week);
20677
+ keep.add(backup.key);
20678
+ }
20679
+ if (seenWeeks.size >= 4) {
20680
+ break;
20681
+ }
20682
+ }
20683
+ }
20684
+ return keep;
20685
+ }
20686
+ function getWeekBucket(date) {
20687
+ const start = Date.UTC(date.getUTCFullYear(), 0, 0);
20688
+ const dayOfYear = Math.floor((Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()) - start) / 864e5);
20689
+ const week = Math.floor(dayOfYear / 7) + 1;
20690
+ return `${date.getUTCFullYear()}-W${String(week).padStart(2, "0")}`;
20691
+ }
20692
+ async function enforceRetentionPolicy(opts, clientId, allObjects) {
20693
+ const s3 = makeS3Client(opts);
20694
+ const prefix = `${BACKUP_PREFIX}${clientId}/`;
20695
+ const relevant = allObjects.filter((o) => o.key.startsWith(prefix));
20696
+ const keep = selectRetentionKeep(relevant);
20697
+ if (keep.size >= relevant.length) {
20698
+ return;
20699
+ }
20700
+ const toDelete = relevant.filter((obj) => !keep.has(obj.key)).map((obj) => ({ Key: obj.key })).filter((entry) => Boolean(entry.Key));
20701
+ const deleteBatches = chunkArray(toDelete, 1e3);
20702
+ for (const batch of deleteBatches) {
20703
+ await s3.send(
20704
+ new DeleteObjectsCommand({
20705
+ Bucket: opts.r2Bucket,
20706
+ Delete: { Objects: batch }
20707
+ })
20708
+ );
20709
+ }
20710
+ }
20711
+ function chunkArray(items, size) {
20712
+ const chunks = [];
20713
+ for (let i = 0; i < items.length; i += size) {
20714
+ chunks.push(items.slice(i, i + size));
20715
+ }
20716
+ return chunks;
20717
+ }
20718
+ async function readStreamFully(input) {
20719
+ const stream = normalizeS3Body(input);
20720
+ const chunks = [];
20721
+ return new Promise((resolve, reject) => {
20722
+ stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
20723
+ stream.on("end", () => resolve(Buffer.concat(chunks)));
20724
+ stream.on("error", (error) => reject(error));
20725
+ });
20726
+ }
20727
+ function normalizeS3Body(body) {
20728
+ if (body instanceof Readable) return body;
20729
+ if (body && typeof body === "object" && "getReader" in body) {
20730
+ const reader = body.getReader();
20731
+ const readable = new Readable({ read() {
20732
+ } });
20733
+ (async () => {
20734
+ try {
20735
+ while (true) {
20736
+ const { done, value } = await reader.read();
20737
+ if (done) {
20738
+ readable.push(null);
20739
+ break;
20740
+ }
20741
+ readable.push(Buffer.from(value));
20742
+ }
20743
+ } catch (error) {
20744
+ readable.destroy(error);
20745
+ }
20746
+ })();
20747
+ return readable;
20748
+ }
20749
+ throw new Error("Unsupported S3 object body type");
20750
+ }
20751
+ async function createBackup(opts) {
20752
+ validateEncryptionKey(opts.encryptionKey);
20753
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
20754
+ const key = makeBackupKey(opts.databaseUrl, new Date(timestamp));
20755
+ const client = makeS3Client(opts);
20756
+ const dump = await runProcess("pg_dump", ["--format=custom", opts.databaseUrl]);
20757
+ const compressed = compress2(dump);
20758
+ const encrypted = encryptBlob(compressed, opts.encryptionKey);
20759
+ await client.send(
20760
+ new PutObjectCommand({
20761
+ Bucket: opts.r2Bucket,
20762
+ Key: key,
20763
+ Body: Readable.from(encrypted),
20764
+ ContentType: "application/octet-stream",
20765
+ ContentLength: encrypted.length
20766
+ })
20767
+ );
20768
+ const all = await listBackupObjects(client, opts.r2Bucket, BACKUP_PREFIX);
20769
+ const clientId = parsePostgresClientId(opts.databaseUrl);
20770
+ await enforceRetentionPolicy(
20771
+ {
20772
+ r2Bucket: opts.r2Bucket,
20773
+ r2Endpoint: opts.r2Endpoint,
20774
+ r2AccessKeyId: opts.r2AccessKeyId,
20775
+ r2SecretAccessKey: opts.r2SecretAccessKey
20776
+ },
20777
+ clientId,
20778
+ all
20779
+ );
20780
+ return {
20781
+ key,
20782
+ sizeBytes: encrypted.length,
20783
+ timestamp
20784
+ };
20785
+ }
20786
+ async function restoreBackup(opts) {
20787
+ validateEncryptionKey(opts.encryptionKey);
20788
+ const client = makeS3Client(opts);
20789
+ const response = await client.send(
20790
+ new GetObjectCommand({
20791
+ Bucket: opts.r2Bucket,
20792
+ Key: opts.backupKey
20793
+ })
20794
+ );
20795
+ if (!response.Body) {
20796
+ throw new Error(`Backup not found in R2: ${opts.backupKey}`);
20797
+ }
20798
+ const encrypted = await readStreamFully(response.Body);
20799
+ const decompressed = decompress2(decryptBlob(encrypted, opts.encryptionKey));
20800
+ await runProcess("pg_restore", ["--clean", "--if-exists", "-d", opts.databaseUrl], decompressed);
20801
+ }
20802
+ async function listBackups(opts) {
20803
+ const s3 = makeS3Client(opts);
20804
+ const objects = await listBackupObjects(s3, opts.r2Bucket, BACKUP_PREFIX);
20805
+ return objects.filter((obj) => obj.key.endsWith(BACKUP_EXT)).map((obj) => ({
20806
+ key: obj.key,
20807
+ size: obj.size,
20808
+ lastModified: obj.lastModified.toISOString()
20809
+ })).sort((a, b) => b.lastModified.localeCompare(a.lastModified));
20810
+ }
20811
+ async function backupHealth(opts) {
20812
+ const backups = await listBackups(opts);
20813
+ if (backups.length === 0) {
20814
+ return {
20815
+ lastBackup: null,
20816
+ backupCount: 0,
20817
+ totalSizeBytes: 0,
20818
+ oldestBackup: null
20819
+ };
20820
+ }
20821
+ return {
20822
+ lastBackup: backups[0].lastModified,
20823
+ backupCount: backups.length,
20824
+ totalSizeBytes: backups.reduce((acc, item) => acc + item.size, 0),
20825
+ oldestBackup: backups[backups.length - 1].lastModified
20826
+ };
20827
+ }
20828
+
20829
+ // src/mcp/tools/backup-vps.ts
20830
+ function registerBackupVps(server2) {
20831
+ server2.registerTool(
20832
+ "backup_vps",
20833
+ {
20834
+ title: "VPS Backup",
20835
+ description: "Run VPS Postgres backup/restore workflows and check status.",
20836
+ inputSchema: z64.discriminatedUnion("action", [
20837
+ z64.object({
20838
+ action: z64.literal("backup"),
20839
+ databaseUrl: z64.string().min(1).describe("Postgres DATABASE_URL to dump from"),
20840
+ encryptionKeyB64: z64.string().min(1).max(4096).optional().describe(
20841
+ "Base64 AES-256 key (defaults to local master key if omitted)"
20842
+ ),
20843
+ r2Bucket: z64.string().min(1).describe("R2 bucket name"),
20844
+ r2Endpoint: z64.string().min(1).describe("R2 endpoint URL"),
20845
+ r2AccessKeyId: z64.string().min(1).describe("R2 access key ID"),
20846
+ r2SecretAccessKey: z64.string().min(1).describe("R2 secret access key")
20847
+ }),
20848
+ z64.object({
20849
+ action: z64.literal("restore"),
20850
+ databaseUrl: z64.string().min(1).describe("Target Postgres DATABASE_URL for restore"),
20851
+ backupKey: z64.string().min(1).describe("R2 object key to restore from"),
20852
+ encryptionKeyB64: z64.string().min(1).max(4096).optional().describe(
20853
+ "Base64 AES-256 key (defaults to local master key if omitted)"
20854
+ ),
20855
+ r2Bucket: z64.string().min(1).describe("R2 bucket name"),
20856
+ r2Endpoint: z64.string().min(1).describe("R2 endpoint URL"),
20857
+ r2AccessKeyId: z64.string().min(1).describe("R2 access key ID"),
20858
+ r2SecretAccessKey: z64.string().min(1).describe("R2 secret access key")
20859
+ }),
20860
+ z64.object({
20861
+ action: z64.literal("list"),
20862
+ r2Bucket: z64.string().min(1).describe("R2 bucket name"),
20863
+ r2Endpoint: z64.string().min(1).describe("R2 endpoint URL"),
20864
+ r2AccessKeyId: z64.string().min(1).describe("R2 access key ID"),
20865
+ r2SecretAccessKey: z64.string().min(1).describe("R2 secret access key")
20866
+ }),
20867
+ z64.object({
20868
+ action: z64.literal("health"),
20869
+ r2Bucket: z64.string().min(1).describe("R2 bucket name"),
20870
+ r2Endpoint: z64.string().min(1).describe("R2 endpoint URL"),
20871
+ r2AccessKeyId: z64.string().min(1).describe("R2 access key ID"),
20872
+ r2SecretAccessKey: z64.string().min(1).describe("R2 secret access key")
20873
+ })
20874
+ ])
20875
+ },
20876
+ async (input) => {
20877
+ try {
20878
+ switch (input.action) {
20879
+ case "backup": {
20880
+ const encryptionKey = await resolveEncryptionKey(input.encryptionKeyB64);
20881
+ const options = {
20882
+ databaseUrl: input.databaseUrl,
20883
+ encryptionKey,
20884
+ r2Bucket: input.r2Bucket,
20885
+ r2Endpoint: input.r2Endpoint,
20886
+ r2AccessKeyId: input.r2AccessKeyId,
20887
+ r2SecretAccessKey: input.r2SecretAccessKey
20888
+ };
20889
+ const result = await createBackup(options);
20890
+ return {
20891
+ content: [
20892
+ {
20893
+ type: "text",
20894
+ text: `Backup complete: ${result.key}
20895
+ ${result.sizeBytes} bytes
20896
+ ${result.timestamp}`
20897
+ }
20898
+ ]
20899
+ };
20900
+ }
20901
+ case "restore": {
20902
+ const encryptionKey = await resolveEncryptionKey(input.encryptionKeyB64);
20903
+ const options = {
20904
+ databaseUrl: input.databaseUrl,
20905
+ backupKey: input.backupKey,
20906
+ encryptionKey,
20907
+ r2Bucket: input.r2Bucket,
20908
+ r2Endpoint: input.r2Endpoint,
20909
+ r2AccessKeyId: input.r2AccessKeyId,
20910
+ r2SecretAccessKey: input.r2SecretAccessKey
20911
+ };
20912
+ await restoreBackup(options);
20913
+ return {
20914
+ content: [
20915
+ {
20916
+ type: "text",
20917
+ text: `Restore complete from ${input.backupKey}`
20918
+ }
20919
+ ]
20920
+ };
20921
+ }
20922
+ case "list": {
20923
+ const options = {
20924
+ r2Bucket: input.r2Bucket,
20925
+ r2Endpoint: input.r2Endpoint,
20926
+ r2AccessKeyId: input.r2AccessKeyId,
20927
+ r2SecretAccessKey: input.r2SecretAccessKey
20928
+ };
20929
+ const result = await listBackups(options);
20930
+ return {
20931
+ content: [
20932
+ {
20933
+ type: "text",
20934
+ text: formatBackups(result)
20935
+ }
20936
+ ]
20937
+ };
20938
+ }
20939
+ case "health": {
20940
+ const result = await backupHealth({
20941
+ r2Bucket: input.r2Bucket,
20942
+ r2Endpoint: input.r2Endpoint,
20943
+ r2AccessKeyId: input.r2AccessKeyId,
20944
+ r2SecretAccessKey: input.r2SecretAccessKey
20945
+ });
20946
+ return {
20947
+ content: [
20948
+ {
20949
+ type: "text",
20950
+ text: [
20951
+ `last_backup: ${result.lastBackup ?? "none"}`,
20952
+ `backup_count: ${result.backupCount}`,
20953
+ `total_size_bytes: ${result.totalSizeBytes}`,
20954
+ `oldest_backup: ${result.oldestBackup ?? "none"}`
20955
+ ].join("\n")
20956
+ }
20957
+ ]
20958
+ };
20959
+ }
20960
+ default:
20961
+ return {
20962
+ content: [
20963
+ {
20964
+ type: "text",
20965
+ text: "Unknown backup action"
20966
+ }
20967
+ ],
20968
+ isError: true
20969
+ };
20970
+ }
20971
+ } catch (error) {
20972
+ return {
20973
+ content: [
20974
+ {
20975
+ type: "text",
20976
+ text: error instanceof Error ? error.message : String(error)
20977
+ }
20978
+ ],
20979
+ isError: true
20980
+ };
20981
+ }
20982
+ }
20983
+ );
20984
+ }
20985
+ function formatBackups(backups) {
20986
+ if (backups.length === 0) {
20987
+ return "No backups found.";
20988
+ }
20989
+ const lines = [
20990
+ "# VPS backups",
20991
+ "",
20992
+ "| Key | Size | Last Modified |",
20993
+ "| --- | ----: | ------------- |",
20994
+ ...backups.map((item) => `| ${item.key} | ${item.size.toLocaleString()} | ${item.lastModified} |`)
20995
+ ];
20996
+ return lines.join("\n");
20997
+ }
20998
+ async function resolveEncryptionKey(providedB64) {
20999
+ if (providedB64) {
21000
+ return Buffer.from(providedB64, "base64");
21001
+ }
21002
+ const masterKey = await getMasterKey();
21003
+ if (!masterKey) {
21004
+ throw new Error("No encryption key provided and no local master key is available.");
21005
+ }
21006
+ return masterKey;
21007
+ }
21008
+
21009
+ // src/mcp/tools/people-roster.ts
21010
+ import { z as z65 } from "zod";
21011
+
19759
21012
  // src/lib/people.ts
19760
21013
  init_config();
19761
21014
  import { readFile as readFile5, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
19762
21015
  import { existsSync as existsSync30, readFileSync as readFileSync26 } from "fs";
19763
- import path38 from "path";
19764
- var PEOPLE_PATH = path38.join(EXE_AI_DIR, "people.json");
21016
+ import path39 from "path";
21017
+ var PEOPLE_PATH = path39.join(EXE_AI_DIR, "people.json");
19765
21018
  async function loadPeople() {
19766
21019
  if (!existsSync30(PEOPLE_PATH)) return [];
19767
21020
  try {
@@ -19772,7 +21025,7 @@ async function loadPeople() {
19772
21025
  }
19773
21026
  }
19774
21027
  async function savePeople(people) {
19775
- await mkdir5(path38.dirname(PEOPLE_PATH), { recursive: true });
21028
+ await mkdir5(path39.dirname(PEOPLE_PATH), { recursive: true });
19776
21029
  await writeFile6(PEOPLE_PATH, JSON.stringify(people, null, 2) + "\n", "utf-8");
19777
21030
  }
19778
21031
  async function addPerson(person) {
@@ -19802,10 +21055,10 @@ function registerAddPerson(server2) {
19802
21055
  title: "Add Person",
19803
21056
  description: "Add or update a key human in the people roster. Used for co-founders, partners, customers \u2014 anyone agents need to know about.",
19804
21057
  inputSchema: {
19805
- name: z64.string().describe("Person's name"),
19806
- role: z64.string().describe("Their role (e.g. co-founder, customer, partner)"),
19807
- relationship: z64.string().describe("Relationship to the organization (e.g. co-founder, early adopter, investor)"),
19808
- notes: z64.string().optional().describe("Additional context about this person")
21058
+ name: z65.string().describe("Person's name"),
21059
+ role: z65.string().describe("Their role (e.g. co-founder, customer, partner)"),
21060
+ relationship: z65.string().describe("Relationship to the organization (e.g. co-founder, early adopter, investor)"),
21061
+ notes: z65.string().optional().describe("Additional context about this person")
19809
21062
  }
19810
21063
  },
19811
21064
  async ({ name, role, relationship, notes }) => {
@@ -19851,7 +21104,7 @@ function registerGetPerson(server2) {
19851
21104
  title: "Get Person",
19852
21105
  description: "Look up a specific person by name from the people roster.",
19853
21106
  inputSchema: {
19854
- name: z64.string().describe("Person's name to look up")
21107
+ name: z65.string().describe("Person's name to look up")
19855
21108
  }
19856
21109
  },
19857
21110
  async ({ name }) => {
@@ -19879,7 +21132,7 @@ init_active_agent();
19879
21132
  init_agent_config();
19880
21133
  init_runtime_table();
19881
21134
  init_employees();
19882
- import { z as z65 } from "zod";
21135
+ import { z as z66 } from "zod";
19883
21136
  function registerSetAgentConfig(server2) {
19884
21137
  server2.registerTool(
19885
21138
  "set_agent_config",
@@ -19887,14 +21140,22 @@ function registerSetAgentConfig(server2) {
19887
21140
  title: "Set Agent Config",
19888
21141
  description: "Set or view per-agent runtime + model configuration. Controls which runtime (claude, codex, opencode) and model each agent uses when dispatched. COO-only. Omit runtime/model to view current config for an agent or all agents.",
19889
21142
  inputSchema: {
19890
- agent_id: z65.string().optional().describe("Agent name, or 'default' for org-wide default. Omit to view all agents."),
19891
- runtime: z65.string().optional().describe(`Runtime: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`),
19892
- model: z65.string().optional().describe("Model name (e.g. claude-opus-4, gpt-5.4, anthropic/claude-sonnet-4-6)")
21143
+ agent_id: z66.string().optional().describe("Agent name, or 'default' for org-wide default. Omit to view all agents."),
21144
+ runtime: z66.string().optional().describe(`Runtime: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`),
21145
+ model: z66.string().optional().describe("Model name (e.g. claude-opus-4, gpt-5.4, anthropic/claude-sonnet-4-6)")
19893
21146
  }
19894
21147
  },
19895
21148
  async ({ agent_id, runtime, model }) => {
19896
- const { agentRole } = getActiveAgent();
19897
- if (agentRole !== "COO") {
21149
+ const { agentId, agentRole } = getActiveAgent();
21150
+ const isCoordinator = isCoordinatorRole(agentRole) || (() => {
21151
+ try {
21152
+ const emp = getEmployee(loadEmployeesSync(), agentId);
21153
+ return emp ? isCoordinatorRole(emp.role) : false;
21154
+ } catch {
21155
+ return false;
21156
+ }
21157
+ })();
21158
+ if (!isCoordinator) {
19898
21159
  return {
19899
21160
  content: [
19900
21161
  {
@@ -19979,6 +21240,72 @@ function registerSetAgentConfig(server2) {
19979
21240
  );
19980
21241
  }
19981
21242
 
21243
+ // src/mcp/tools/list-employees.ts
21244
+ init_employees();
21245
+ init_agent_config();
21246
+ import { z as z67 } from "zod";
21247
+ function registerListEmployees(server2) {
21248
+ server2.registerTool(
21249
+ "list_employees",
21250
+ {
21251
+ title: "List Employees",
21252
+ description: "List all AI employees in the organization with their role, runtime, and model configuration. Use to see who's on the team and how they're configured.",
21253
+ inputSchema: {
21254
+ role: z67.string().optional().describe("Filter by role (case-insensitive)")
21255
+ }
21256
+ },
21257
+ async ({ role }) => {
21258
+ try {
21259
+ let employees = loadEmployeesSync();
21260
+ if (role) {
21261
+ const lower = role.toLowerCase();
21262
+ employees = employees.filter(
21263
+ (e) => e.role.toLowerCase().includes(lower)
21264
+ );
21265
+ }
21266
+ if (employees.length === 0) {
21267
+ return {
21268
+ content: [
21269
+ {
21270
+ type: "text",
21271
+ text: role ? `No employees found with role matching "${role}".` : "No employees in roster."
21272
+ }
21273
+ ]
21274
+ };
21275
+ }
21276
+ const lines = [
21277
+ "Employee Roster",
21278
+ "\u2550".repeat(70),
21279
+ "Name".padEnd(12) + "Role".padEnd(20) + "Runtime".padEnd(16) + "Model",
21280
+ "\u2500".repeat(70)
21281
+ ];
21282
+ for (const emp of employees) {
21283
+ const cfg = getAgentRuntime(emp.name);
21284
+ const rtLabel = RUNTIME_LABELS[cfg.runtime] ?? cfg.runtime;
21285
+ lines.push(
21286
+ emp.name.padEnd(12) + emp.role.padEnd(20) + rtLabel.substring(0, 15).padEnd(16) + cfg.model
21287
+ );
21288
+ }
21289
+ lines.push("\u2500".repeat(70));
21290
+ lines.push(`${employees.length} employee(s)`);
21291
+ return {
21292
+ content: [{ type: "text", text: lines.join("\n") }]
21293
+ };
21294
+ } catch (err) {
21295
+ return {
21296
+ content: [
21297
+ {
21298
+ type: "text",
21299
+ text: `Failed to list employees: ${err instanceof Error ? err.message : String(err)}`
21300
+ }
21301
+ ],
21302
+ isError: true
21303
+ };
21304
+ }
21305
+ }
21306
+ );
21307
+ }
21308
+
19982
21309
  // src/lib/telemetry.ts
19983
21310
  var ENABLED = process.env.EXE_TELEMETRY === "1";
19984
21311
  var initialized = false;
@@ -20106,6 +21433,7 @@ registerGetAutoWakeStatus(server);
20106
21433
  registerGetWorkerGate(server);
20107
21434
  registerRunMemoryAudit(server);
20108
21435
  registerCloudSync(server);
21436
+ registerBackupVps(server);
20109
21437
  registerGetMemoryCardinality(server);
20110
21438
  registerRunConsolidation(server);
20111
21439
  registerGetLicenseStatus(server);
@@ -20113,12 +21441,19 @@ registerAddPerson(server);
20113
21441
  registerListPeople(server);
20114
21442
  registerGetPerson(server);
20115
21443
  registerSetAgentConfig(server);
21444
+ registerListEmployees(server);
20116
21445
  try {
20117
21446
  await initStore();
20118
21447
  process.stderr.write("[exe-os] MCP server starting...\n");
20119
21448
  const transport = new StdioServerTransport();
20120
21449
  await server.connect(transport);
20121
21450
  process.stderr.write("[exe-os] MCP server connected.\n");
21451
+ void initDaemonClient().catch((err) => {
21452
+ process.stderr.write(
21453
+ `[exe-os] Daemon DB routing unavailable (non-fatal): ${err instanceof Error ? err.message : String(err)}
21454
+ `
21455
+ );
21456
+ });
20122
21457
  void (async () => {
20123
21458
  try {
20124
21459
  const { checkLicense: checkLicense2 } = await Promise.resolve().then(() => (init_license(), license_exports));
@@ -20175,20 +21510,20 @@ try {
20175
21510
  `
20176
21511
  );
20177
21512
  const thisFile = fileURLToPath5(import.meta.url);
20178
- const backfillPath = path39.resolve(
20179
- path39.dirname(thisFile),
21513
+ const backfillPath = path40.resolve(
21514
+ path40.dirname(thisFile),
20180
21515
  "../bin/backfill-vectors.js"
20181
21516
  );
20182
21517
  if (existsSync31(backfillPath)) {
20183
21518
  const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
20184
- const logPath = path39.join(exeDir, "workers.log");
20185
- mkdirSync17(path39.dirname(logPath), { recursive: true });
21519
+ const logPath = path40.join(exeDir, "workers.log");
21520
+ mkdirSync17(path40.dirname(logPath), { recursive: true });
20186
21521
  let logFd = "ignore";
20187
21522
  try {
20188
21523
  logFd = openSync3(logPath, "a");
20189
21524
  } catch {
20190
21525
  }
20191
- const child = spawn3(process.execPath, [backfillPath], {
21526
+ const child = spawn4(process.execPath, [backfillPath], {
20192
21527
  detached: true,
20193
21528
  stdio: ["ignore", "ignore", logFd]
20194
21529
  });