@askexenow/exe-os 0.9.7 → 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 +657 -35
  5. package/dist/bin/cli.js +1388 -605
  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 +784 -153
  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 +692 -70
  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 +1053 -271
  17. package/dist/bin/exe-heartbeat.js +665 -43
  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 +834 -150
  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 +657 -35
  33. package/dist/bin/exe-team.js +635 -13
  34. package/dist/bin/git-sweep.js +720 -89
  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 +724 -93
  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 +1027 -245
  43. package/dist/hooks/bug-report-worker.js +891 -170
  44. package/dist/hooks/commit-complete.js +718 -87
  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 +840 -156
  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 +674 -43
  52. package/dist/hooks/pre-compact.js +718 -87
  53. package/dist/hooks/pre-tool-use.js +872 -125
  54. package/dist/hooks/prompt-ingest-worker.js +758 -83
  55. package/dist/hooks/prompt-submit.js +1060 -319
  56. package/dist/hooks/response-ingest-worker.js +758 -83
  57. package/dist/hooks/session-end.js +721 -90
  58. package/dist/hooks/session-start.js +1031 -207
  59. package/dist/hooks/stop.js +680 -49
  60. package/dist/hooks/subagent-stop.js +674 -43
  61. package/dist/hooks/summary-worker.js +816 -132
  62. package/dist/index.js +1015 -232
  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 +894 -162
  73. package/dist/lib/hybrid-search.js +771 -88
  74. package/dist/lib/identity.js +27 -7
  75. package/dist/lib/messaging.js +55 -28
  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 +98 -71
  82. package/dist/lib/tmux-routing.js +87 -60
  83. package/dist/lib/token-spend.js +26 -6
  84. package/dist/mcp/server.js +1784 -458
  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 +290 -164
  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 +195 -38
  91. package/dist/mcp/tools/send-message.js +58 -31
  92. package/dist/mcp/tools/update-task.js +75 -48
  93. package/dist/runtime/index.js +720 -89
  94. package/dist/tui/App.js +853 -123
  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 };
611
+ }
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);
560
621
  }
561
- spawnDaemon();
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,8 +1162,8 @@ 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
  }
@@ -979,15 +1176,601 @@ function registerBinSymlinks(name) {
979
1176
  }
980
1177
  return { created, skipped, errors };
981
1178
  }
982
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
1179
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
983
1180
  var init_employees = __esm({
984
1181
  "src/lib/employees.ts"() {
985
1182
  "use strict";
986
1183
  init_config();
987
- EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
1184
+ EMPLOYEES_PATH = path4.join(EXE_AI_DIR, "exe-employees.json");
988
1185
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
989
1186
  COORDINATOR_ROLE = "COO";
990
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;
1603
+ }
1604
+ current += ch;
1605
+ }
1606
+ if (current.trim()) {
1607
+ parts.push(current.trim());
1608
+ }
1609
+ return parts;
1610
+ }
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"() {
1720
+ "use strict";
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;
@@ -6947,7 +7681,7 @@ function sendIntercom(targetSession) {
6947
7681
  try {
6948
7682
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
6949
7683
  const agent = baseAgentName(rawAgent);
6950
- const markerPath = path18.join(SESSION_CACHE, `current-task-${agent}.json`);
7684
+ const markerPath = path19.join(SESSION_CACHE, `current-task-${agent}.json`);
6951
7685
  if (existsSync14(markerPath)) {
6952
7686
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
6953
7687
  return "debounced";
@@ -6957,7 +7691,7 @@ function sendIntercom(targetSession) {
6957
7691
  try {
6958
7692
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
6959
7693
  const agent = baseAgentName(rawAgent);
6960
- const taskDir = path18.join(process.cwd(), "exe", agent);
7694
+ const taskDir = path19.join(process.cwd(), "exe", agent);
6961
7695
  if (existsSync14(taskDir)) {
6962
7696
  const files = readdirSync5(taskDir).filter(
6963
7697
  (f) => f.endsWith(".md") && f !== "DONE.txt"
@@ -7091,8 +7825,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7091
7825
  const transport = getTransport();
7092
7826
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
7093
7827
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
7094
- const logDir = path18.join(os8.homedir(), ".exe-os", "session-logs");
7095
- 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`);
7096
7830
  if (!existsSync14(logDir)) {
7097
7831
  mkdirSync7(logDir, { recursive: true });
7098
7832
  }
@@ -7100,14 +7834,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7100
7834
  let cleanupSuffix = "";
7101
7835
  try {
7102
7836
  const thisFile = fileURLToPath2(import.meta.url);
7103
- 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");
7104
7838
  if (existsSync14(cleanupScript)) {
7105
7839
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
7106
7840
  }
7107
7841
  } catch {
7108
7842
  }
7109
7843
  try {
7110
- const claudeJsonPath = path18.join(os8.homedir(), ".claude.json");
7844
+ const claudeJsonPath = path19.join(os9.homedir(), ".claude.json");
7111
7845
  let claudeJson = {};
7112
7846
  try {
7113
7847
  claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
@@ -7122,10 +7856,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7122
7856
  } catch {
7123
7857
  }
7124
7858
  try {
7125
- const settingsDir = path18.join(os8.homedir(), ".claude", "projects");
7859
+ const settingsDir = path19.join(os9.homedir(), ".claude", "projects");
7126
7860
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
7127
- const projSettingsDir = path18.join(settingsDir, normalizedKey);
7128
- const settingsPath = path18.join(projSettingsDir, "settings.json");
7861
+ const projSettingsDir = path19.join(settingsDir, normalizedKey);
7862
+ const settingsPath = path19.join(projSettingsDir, "settings.json");
7129
7863
  let settings = {};
7130
7864
  try {
7131
7865
  settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
@@ -7172,8 +7906,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7172
7906
  let behaviorsFlag = "";
7173
7907
  let legacyFallbackWarned = false;
7174
7908
  if (!useExeAgent && !useBinSymlink) {
7175
- const identityPath2 = path18.join(
7176
- os8.homedir(),
7909
+ const identityPath2 = path19.join(
7910
+ os9.homedir(),
7177
7911
  ".exe-os",
7178
7912
  "identity",
7179
7913
  `${employeeName}.md`
@@ -7188,7 +7922,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7188
7922
  }
7189
7923
  const behaviorsFile = exportBehaviorsSync(
7190
7924
  employeeName,
7191
- path18.basename(spawnCwd),
7925
+ path19.basename(spawnCwd),
7192
7926
  sessionName
7193
7927
  );
7194
7928
  if (behaviorsFile) {
@@ -7203,9 +7937,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7203
7937
  }
7204
7938
  let sessionContextFlag = "";
7205
7939
  try {
7206
- const ctxDir = path18.join(os8.homedir(), ".exe-os", "session-cache");
7940
+ const ctxDir = path19.join(os9.homedir(), ".exe-os", "session-cache");
7207
7941
  mkdirSync7(ctxDir, { recursive: true });
7208
- const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
7942
+ const ctxFile = path19.join(ctxDir, `session-context-${sessionName}.md`);
7209
7943
  const ctxContent = [
7210
7944
  `## Session Context`,
7211
7945
  `You are running in tmux session: ${sessionName}.`,
@@ -7289,7 +8023,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7289
8023
  transport.pipeLog(sessionName, logFile);
7290
8024
  try {
7291
8025
  const mySession = getMySession();
7292
- const dispatchInfo = path18.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
8026
+ const dispatchInfo = path19.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
7293
8027
  writeFileSync9(dispatchInfo, JSON.stringify({
7294
8028
  dispatchedBy: mySession,
7295
8029
  rootExe: exeSession,
@@ -7364,15 +8098,15 @@ var init_tmux_routing = __esm({
7364
8098
  init_intercom_queue();
7365
8099
  init_plan_limits();
7366
8100
  init_employees();
7367
- SPAWN_LOCK_DIR = path18.join(os8.homedir(), ".exe-os", "spawn-locks");
7368
- 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");
7369
8103
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
7370
8104
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
7371
8105
  VERIFY_PANE_LINES = 200;
7372
8106
  INTERCOM_DEBOUNCE_MS = 3e4;
7373
8107
  CODEX_DEBOUNCE_MS = 12e4;
7374
- INTERCOM_LOG2 = path18.join(os8.homedir(), ".exe-os", "intercom.log");
7375
- 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");
7376
8110
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
7377
8111
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
7378
8112
  }
@@ -7420,8 +8154,8 @@ __export(tasks_crud_exports, {
7420
8154
  writeCheckpoint: () => writeCheckpoint
7421
8155
  });
7422
8156
  import crypto6 from "crypto";
7423
- import path19 from "path";
7424
- import os9 from "os";
8157
+ import path20 from "path";
8158
+ import os10 from "os";
7425
8159
  import { execSync as execSync8 } from "child_process";
7426
8160
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
7427
8161
  import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
@@ -7599,8 +8333,8 @@ ${laneWarning}` : laneWarning;
7599
8333
  }
7600
8334
  if (input.baseDir) {
7601
8335
  try {
7602
- await mkdir4(path19.join(input.baseDir, "exe", "output"), { recursive: true });
7603
- 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 });
7604
8338
  await ensureArchitectureDoc(input.baseDir, input.projectName);
7605
8339
  await ensureGitignoreExe(input.baseDir);
7606
8340
  } catch {
@@ -7636,9 +8370,9 @@ ${laneWarning}` : laneWarning;
7636
8370
  });
7637
8371
  if (input.baseDir) {
7638
8372
  try {
7639
- const EXE_OS_DIR = path19.join(os9.homedir(), ".exe-os");
7640
- const mdPath = path19.join(EXE_OS_DIR, taskFile);
7641
- 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);
7642
8376
  if (!existsSync15(mdDir)) await mkdir4(mdDir, { recursive: true });
7643
8377
  const reviewer = input.reviewer ?? input.assignedBy;
7644
8378
  const mdContent = `# ${input.title}
@@ -7939,7 +8673,7 @@ async function deleteTaskCore(taskId, _baseDir) {
7939
8673
  return { taskFile, assignedTo, assignedBy, taskSlug };
7940
8674
  }
7941
8675
  async function ensureArchitectureDoc(baseDir, projectName) {
7942
- const archPath = path19.join(baseDir, "exe", "ARCHITECTURE.md");
8676
+ const archPath = path20.join(baseDir, "exe", "ARCHITECTURE.md");
7943
8677
  try {
7944
8678
  if (existsSync15(archPath)) return;
7945
8679
  const template = [
@@ -7974,7 +8708,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
7974
8708
  }
7975
8709
  }
7976
8710
  async function ensureGitignoreExe(baseDir) {
7977
- const gitignorePath = path19.join(baseDir, ".gitignore");
8711
+ const gitignorePath = path20.join(baseDir, ".gitignore");
7978
8712
  try {
7979
8713
  if (existsSync15(gitignorePath)) {
7980
8714
  const content = readFileSync13(gitignorePath, "utf-8");
@@ -8008,13 +8742,13 @@ var init_tasks_crud = __esm({
8008
8742
  });
8009
8743
 
8010
8744
  // src/lib/tasks-review.ts
8011
- import path20 from "path";
8745
+ import path21 from "path";
8012
8746
  import { existsSync as existsSync16, readdirSync as readdirSync6, unlinkSync as unlinkSync6 } from "fs";
8013
8747
  async function countPendingReviews(sessionScope) {
8014
8748
  const client = getClient();
8015
8749
  if (sessionScope) {
8016
8750
  const result2 = await client.execute({
8017
- 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 = ?",
8018
8752
  args: [sessionScope]
8019
8753
  });
8020
8754
  return Number(result2.rows[0]?.cnt) || 0;
@@ -8190,11 +8924,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
8190
8924
  );
8191
8925
  }
8192
8926
  try {
8193
- const cacheDir = path20.join(EXE_AI_DIR, "session-cache");
8927
+ const cacheDir = path21.join(EXE_AI_DIR, "session-cache");
8194
8928
  if (existsSync16(cacheDir)) {
8195
8929
  for (const f of readdirSync6(cacheDir)) {
8196
8930
  if (f.startsWith("review-notified-")) {
8197
- unlinkSync6(path20.join(cacheDir, f));
8931
+ unlinkSync6(path21.join(cacheDir, f));
8198
8932
  }
8199
8933
  }
8200
8934
  }
@@ -8215,7 +8949,7 @@ var init_tasks_review = __esm({
8215
8949
  });
8216
8950
 
8217
8951
  // src/lib/tasks-chain.ts
8218
- import path21 from "path";
8952
+ import path22 from "path";
8219
8953
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
8220
8954
  async function cascadeUnblock(taskId, baseDir, now) {
8221
8955
  const client = getClient();
@@ -8232,7 +8966,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
8232
8966
  });
8233
8967
  for (const ur of unblockedRows.rows) {
8234
8968
  try {
8235
- const ubFile = path21.join(baseDir, String(ur.task_file));
8969
+ const ubFile = path22.join(baseDir, String(ur.task_file));
8236
8970
  let ubContent = await readFile4(ubFile, "utf-8");
8237
8971
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
8238
8972
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -8786,7 +9520,7 @@ __export(tasks_exports, {
8786
9520
  updateTaskStatus: () => updateTaskStatus,
8787
9521
  writeCheckpoint: () => writeCheckpoint
8788
9522
  });
8789
- import path22 from "path";
9523
+ import path23 from "path";
8790
9524
  import { writeFileSync as writeFileSync10, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
8791
9525
  async function createTask(input) {
8792
9526
  const result = await createTaskCore(input);
@@ -8806,8 +9540,8 @@ async function updateTask(input) {
8806
9540
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
8807
9541
  try {
8808
9542
  const agent = String(row.assigned_to);
8809
- const cacheDir = path22.join(EXE_AI_DIR, "session-cache");
8810
- 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`);
8811
9545
  if (input.status === "in_progress") {
8812
9546
  mkdirSync8(cacheDir, { recursive: true });
8813
9547
  writeFileSync10(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
@@ -8978,15 +9712,15 @@ __export(identity_exports, {
8978
9712
  });
8979
9713
  import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "fs";
8980
9714
  import { readdirSync as readdirSync7 } from "fs";
8981
- import path23 from "path";
9715
+ import path24 from "path";
8982
9716
  import { createHash as createHash2 } from "crypto";
8983
9717
  function ensureDir2() {
8984
- if (!existsSync17(IDENTITY_DIR)) {
8985
- mkdirSync9(IDENTITY_DIR, { recursive: true });
9718
+ if (!existsSync17(IDENTITY_DIR2)) {
9719
+ mkdirSync9(IDENTITY_DIR2, { recursive: true });
8986
9720
  }
8987
9721
  }
8988
9722
  function identityPath(agentId) {
8989
- return path23.join(IDENTITY_DIR, `${agentId}.md`);
9723
+ return path24.join(IDENTITY_DIR2, `${agentId}.md`);
8990
9724
  }
8991
9725
  function parseFrontmatter(raw) {
8992
9726
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
@@ -9059,7 +9793,7 @@ async function updateIdentity(agentId, content, updatedBy) {
9059
9793
  }
9060
9794
  function listIdentities() {
9061
9795
  ensureDir2();
9062
- const files = readdirSync7(IDENTITY_DIR).filter((f) => f.endsWith(".md"));
9796
+ const files = readdirSync7(IDENTITY_DIR2).filter((f) => f.endsWith(".md"));
9063
9797
  const results = [];
9064
9798
  for (const file of files) {
9065
9799
  const agentId = file.replace(".md", "");
@@ -9092,13 +9826,13 @@ ${teamLines.join("\n")}`);
9092
9826
  }
9093
9827
  return parts.join("\n\n");
9094
9828
  }
9095
- var IDENTITY_DIR;
9829
+ var IDENTITY_DIR2;
9096
9830
  var init_identity = __esm({
9097
9831
  "src/lib/identity.ts"() {
9098
9832
  "use strict";
9099
9833
  init_config();
9100
9834
  init_database();
9101
- IDENTITY_DIR = path23.join(EXE_AI_DIR, "identity");
9835
+ IDENTITY_DIR2 = path24.join(EXE_AI_DIR, "identity");
9102
9836
  }
9103
9837
  });
9104
9838
 
@@ -10211,8 +10945,8 @@ __export(wiki_client_exports, {
10211
10945
  listDocuments: () => listDocuments,
10212
10946
  listWorkspaces: () => listWorkspaces
10213
10947
  });
10214
- async function wikiFetch(config2, path40, method = "GET", body) {
10215
- const url = `${config2.baseUrl}/api/v1${path40}`;
10948
+ async function wikiFetch(config2, path41, method = "GET", body) {
10949
+ const url = `${config2.baseUrl}/api/v1${path41}`;
10216
10950
  const headers = {
10217
10951
  Authorization: `Bearer ${config2.apiKey}`,
10218
10952
  "Content-Type": "application/json"
@@ -10245,7 +10979,7 @@ async function wikiFetch(config2, path40, method = "GET", body) {
10245
10979
  }
10246
10980
  }
10247
10981
  if (!response.ok) {
10248
- throw new Error(`Wiki API ${method} ${path40}: ${response.status} ${response.statusText}`);
10982
+ throw new Error(`Wiki API ${method} ${path41}: ${response.status} ${response.statusText}`);
10249
10983
  }
10250
10984
  return response.json();
10251
10985
  } finally {
@@ -10355,12 +11089,12 @@ __export(worker_gate_exports, {
10355
11089
  tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
10356
11090
  });
10357
11091
  import { readdirSync as readdirSync10, writeFileSync as writeFileSync17, unlinkSync as unlinkSync8, mkdirSync as mkdirSync14, existsSync as existsSync25 } from "fs";
10358
- import path33 from "path";
11092
+ import path34 from "path";
10359
11093
  function tryAcquireWorkerSlot() {
10360
11094
  try {
10361
11095
  mkdirSync14(WORKER_PID_DIR, { recursive: true });
10362
11096
  const reservationId = `res-${process.pid}-${Date.now()}`;
10363
- const reservationPath = path33.join(WORKER_PID_DIR, `${reservationId}.pid`);
11097
+ const reservationPath = path34.join(WORKER_PID_DIR, `${reservationId}.pid`);
10364
11098
  writeFileSync17(reservationPath, String(process.pid));
10365
11099
  const files = readdirSync10(WORKER_PID_DIR);
10366
11100
  let alive = 0;
@@ -10378,7 +11112,7 @@ function tryAcquireWorkerSlot() {
10378
11112
  alive++;
10379
11113
  } catch {
10380
11114
  try {
10381
- unlinkSync8(path33.join(WORKER_PID_DIR, f));
11115
+ unlinkSync8(path34.join(WORKER_PID_DIR, f));
10382
11116
  } catch {
10383
11117
  }
10384
11118
  }
@@ -10402,13 +11136,13 @@ function tryAcquireWorkerSlot() {
10402
11136
  function registerWorkerPid(pid) {
10403
11137
  try {
10404
11138
  mkdirSync14(WORKER_PID_DIR, { recursive: true });
10405
- writeFileSync17(path33.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
11139
+ writeFileSync17(path34.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
10406
11140
  } catch {
10407
11141
  }
10408
11142
  }
10409
11143
  function cleanupWorkerPid() {
10410
11144
  try {
10411
- unlinkSync8(path33.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
11145
+ unlinkSync8(path34.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
10412
11146
  } catch {
10413
11147
  }
10414
11148
  }
@@ -10448,9 +11182,9 @@ var init_worker_gate = __esm({
10448
11182
  "src/lib/worker-gate.ts"() {
10449
11183
  "use strict";
10450
11184
  init_config();
10451
- WORKER_PID_DIR = path33.join(EXE_AI_DIR, "worker-pids");
11185
+ WORKER_PID_DIR = path34.join(EXE_AI_DIR, "worker-pids");
10452
11186
  MAX_CONCURRENT_WORKERS = 3;
10453
- BACKFILL_LOCK = path33.join(WORKER_PID_DIR, "backfill.lock");
11187
+ BACKFILL_LOCK = path34.join(WORKER_PID_DIR, "backfill.lock");
10454
11188
  }
10455
11189
  });
10456
11190
 
@@ -10474,7 +11208,7 @@ __export(crdt_sync_exports, {
10474
11208
  });
10475
11209
  import * as Y from "yjs";
10476
11210
  import { readFileSync as readFileSync24, writeFileSync as writeFileSync18, existsSync as existsSync28, mkdirSync as mkdirSync15, unlinkSync as unlinkSync9 } from "fs";
10477
- import path36 from "path";
11211
+ import path37 from "path";
10478
11212
  import { homedir as homedir5 } from "os";
10479
11213
  function getStatePath() {
10480
11214
  return _statePathOverride ?? DEFAULT_STATE_PATH;
@@ -10630,7 +11364,7 @@ function persistState() {
10630
11364
  if (!doc) return;
10631
11365
  try {
10632
11366
  const sp = getStatePath();
10633
- const dir = path36.dirname(sp);
11367
+ const dir = path37.dirname(sp);
10634
11368
  if (!existsSync28(dir)) mkdirSync15(dir, { recursive: true });
10635
11369
  const state = Y.encodeStateAsUpdate(doc);
10636
11370
  writeFileSync18(sp, Buffer.from(state));
@@ -10674,7 +11408,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
10674
11408
  var init_crdt_sync = __esm({
10675
11409
  "src/lib/crdt-sync.ts"() {
10676
11410
  "use strict";
10677
- DEFAULT_STATE_PATH = path36.join(homedir5(), ".exe-os", "crdt-state.bin");
11411
+ DEFAULT_STATE_PATH = path37.join(homedir5(), ".exe-os", "crdt-state.bin");
10678
11412
  _statePathOverride = null;
10679
11413
  doc = null;
10680
11414
  }
@@ -10686,9 +11420,9 @@ init_store();
10686
11420
  init_database();
10687
11421
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10688
11422
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
10689
- import { spawn as spawn3 } from "child_process";
11423
+ import { spawn as spawn4 } from "child_process";
10690
11424
  import { existsSync as existsSync31, openSync as openSync3, mkdirSync as mkdirSync17, closeSync as closeSync3 } from "fs";
10691
- import path39 from "path";
11425
+ import path40 from "path";
10692
11426
  import { fileURLToPath as fileURLToPath5 } from "url";
10693
11427
 
10694
11428
  // src/mcp/tools/recall-my-memory.ts
@@ -10996,8 +11730,8 @@ init_active_agent();
10996
11730
  init_plan_limits();
10997
11731
  import { z as z4 } from "zod";
10998
11732
  import crypto2 from "crypto";
10999
- import { writeFileSync as writeFileSync4 } from "fs";
11000
- import path12 from "path";
11733
+ import { writeFileSync as writeFileSync5 } from "fs";
11734
+ import path14 from "path";
11001
11735
  function registerStoreMemory(server2) {
11002
11736
  server2.registerTool(
11003
11737
  "store_memory",
@@ -11065,8 +11799,8 @@ function registerStoreMemory(server2) {
11065
11799
  if (needsBackfill) {
11066
11800
  try {
11067
11801
  const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
11068
- const flagPath = path12.join(exeDir, "session-cache", "needs-backfill");
11069
- writeFileSync4(flagPath, "1");
11802
+ const flagPath = path14.join(exeDir, "session-cache", "needs-backfill");
11803
+ writeFileSync5(flagPath, "1");
11070
11804
  } catch {
11071
11805
  }
11072
11806
  }
@@ -11090,8 +11824,8 @@ init_active_agent();
11090
11824
  init_employees();
11091
11825
  import { z as z5 } from "zod";
11092
11826
  import crypto3 from "crypto";
11093
- import { writeFileSync as writeFileSync5 } from "fs";
11094
- import path13 from "path";
11827
+ import { writeFileSync as writeFileSync6 } from "fs";
11828
+ import path15 from "path";
11095
11829
  function registerCommitMemory(server2) {
11096
11830
  server2.registerTool(
11097
11831
  "commit_to_long_term_memory",
@@ -11181,8 +11915,8 @@ function registerCommitMemory(server2) {
11181
11915
  if (needsBackfill) {
11182
11916
  try {
11183
11917
  const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
11184
- const flagPath = path13.join(exeDir, "session-cache", "needs-backfill");
11185
- writeFileSync5(flagPath, "1");
11918
+ const flagPath = path15.join(exeDir, "session-cache", "needs-backfill");
11919
+ writeFileSync6(flagPath, "1");
11186
11920
  } catch {
11187
11921
  }
11188
11922
  }
@@ -11448,13 +12182,14 @@ Warning: ${task.warning}`;
11448
12182
  // src/mcp/tools/list-tasks.ts
11449
12183
  init_tasks();
11450
12184
  init_project_name();
12185
+ init_active_agent();
11451
12186
  import { z as z8 } from "zod";
11452
12187
  function registerListTasks(server2) {
11453
12188
  server2.registerTool(
11454
12189
  "list_tasks",
11455
12190
  {
11456
12191
  title: "List Tasks",
11457
- 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.",
11458
12193
  inputSchema: {
11459
12194
  assigned_to: z8.string().optional().describe("Filter by employee name"),
11460
12195
  status: z8.enum(["open", "in_progress", "done", "blocked", "cancelled"]).optional().describe("Filter by status"),
@@ -11464,7 +12199,16 @@ function registerListTasks(server2) {
11464
12199
  },
11465
12200
  async ({ assigned_to, status, project_name, priority }) => {
11466
12201
  try {
11467
- 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
+ }
11468
12212
  const tasks = await listTasks({
11469
12213
  assignedTo: assigned_to,
11470
12214
  status,
@@ -13219,9 +13963,9 @@ import { z as z29 } from "zod";
13219
13963
  // src/automation/trigger-engine.ts
13220
13964
  import { readFileSync as readFileSync16, writeFileSync as writeFileSync12, existsSync as existsSync18, mkdirSync as mkdirSync10 } from "fs";
13221
13965
  import { randomUUID as randomUUID4 } from "crypto";
13222
- import path24 from "path";
13223
- import os10 from "os";
13224
- 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");
13225
13969
  function loadTriggers(project) {
13226
13970
  if (!existsSync18(TRIGGERS_PATH)) return [];
13227
13971
  try {
@@ -13237,7 +13981,7 @@ function loadTriggers(project) {
13237
13981
  }
13238
13982
  }
13239
13983
  function saveTriggers(triggers) {
13240
- const dir = path24.dirname(TRIGGERS_PATH);
13984
+ const dir = path25.dirname(TRIGGERS_PATH);
13241
13985
  if (!existsSync18(dir)) mkdirSync10(dir, { recursive: true });
13242
13986
  writeFileSync12(TRIGGERS_PATH, JSON.stringify(triggers, null, 2), "utf-8");
13243
13987
  }
@@ -13524,23 +14268,23 @@ import { z as z31 } from "zod";
13524
14268
 
13525
14269
  // src/automation/starter-packs/index.ts
13526
14270
  import { readFileSync as readFileSync17, readdirSync as readdirSync8, existsSync as existsSync19 } from "fs";
13527
- import path25 from "path";
14271
+ import path26 from "path";
13528
14272
  import { fileURLToPath as fileURLToPath3 } from "url";
13529
- var __dirname = path25.dirname(fileURLToPath3(import.meta.url));
14273
+ var __dirname = path26.dirname(fileURLToPath3(import.meta.url));
13530
14274
  function listPacks() {
13531
- const packsDir = path25.join(__dirname, ".");
14275
+ const packsDir = path26.join(__dirname, ".");
13532
14276
  if (!existsSync19(packsDir)) return [];
13533
14277
  return readdirSync8(packsDir, { withFileTypes: true }).filter(
13534
- (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"))
13535
14279
  ).map((d) => d.name);
13536
14280
  }
13537
14281
  function loadPack(industry) {
13538
- const packDir = path25.join(__dirname, industry);
13539
- const objectsPath = path25.join(packDir, "custom-objects.json");
13540
- const triggersPath = path25.join(packDir, "triggers.json");
13541
- const wikiDir = path25.join(packDir, "wiki-seeds");
13542
- const manifestPath = path25.join(packDir, "pack.json");
13543
- 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");
13544
14288
  if (!existsSync19(objectsPath)) return null;
13545
14289
  let customObjects = [];
13546
14290
  try {
@@ -13564,7 +14308,7 @@ function loadPack(industry) {
13564
14308
  if (existsSync19(wikiDir)) {
13565
14309
  const files = readdirSync8(wikiDir).filter((f) => f.endsWith(".md"));
13566
14310
  for (const file of files) {
13567
- const content = readFileSync17(path25.join(wikiDir, file), "utf-8");
14311
+ const content = readFileSync17(path26.join(wikiDir, file), "utf-8");
13568
14312
  const titleMatch = content.match(/^#\s+(.+)/m);
13569
14313
  wikiSeeds.push({
13570
14314
  filename: file,
@@ -13642,7 +14386,7 @@ function applyPack(industry, project) {
13642
14386
  // src/lib/client-coo.ts
13643
14387
  init_config();
13644
14388
  import { existsSync as existsSync20, mkdirSync as mkdirSync11, writeFileSync as writeFileSync13 } from "fs";
13645
- import path26 from "path";
14389
+ import path27 from "path";
13646
14390
 
13647
14391
  // src/lib/employee-templates.ts
13648
14392
  init_global_procedures();
@@ -13780,10 +14524,10 @@ var ClientCOOClobberError = class extends Error {
13780
14524
  var COO_ROLE = "Chief Operating Officer";
13781
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.";
13782
14526
  async function provisionClientCOO(vars, opts = {}) {
13783
- const identityDir = opts.identityDir ?? path26.join(EXE_AI_DIR, "identity");
14527
+ const identityDir = opts.identityDir ?? path27.join(EXE_AI_DIR, "identity");
13784
14528
  const rosterPath = opts.employeesPath ?? EMPLOYEES_PATH;
13785
14529
  const storeFeedbackBehavior = opts.storeFeedbackBehavior ?? true;
13786
- const identityPath2 = path26.join(identityDir, `${vars.agent_name}.md`);
14530
+ const identityPath2 = path27.join(identityDir, `${vars.agent_name}.md`);
13787
14531
  if (existsSync20(identityPath2)) {
13788
14532
  throw new ClientCOOClobberError(vars.agent_name, identityPath2);
13789
14533
  }
@@ -14681,7 +15425,7 @@ function registerUpdateWikiPage(server2) {
14681
15425
  import { z as z37 } from "zod";
14682
15426
  import { execFile } from "child_process";
14683
15427
  import { promisify } from "util";
14684
- import path27 from "path";
15428
+ import path28 from "path";
14685
15429
  import { existsSync as existsSync21 } from "fs";
14686
15430
 
14687
15431
  // src/lib/hostinger-api.ts
@@ -14730,9 +15474,9 @@ var HostingerApiClient = class {
14730
15474
  }
14731
15475
  this.lastRequestTime = Date.now();
14732
15476
  }
14733
- async request(method, path40, body) {
15477
+ async request(method, path41, body) {
14734
15478
  await this.rateLimit();
14735
- const url = `${this.baseUrl}${path40}`;
15479
+ const url = `${this.baseUrl}${path41}`;
14736
15480
  const headers = {
14737
15481
  Authorization: `Bearer ${this.apiKey}`,
14738
15482
  "Content-Type": "application/json",
@@ -14805,8 +15549,8 @@ async function requestCloudflare(cfApiToken, zoneId, options) {
14805
15549
  }
14806
15550
  return envelope.result;
14807
15551
  }
14808
- function buildUrl(zoneId, path40 = "/dns_records", query) {
14809
- const normalizedPath = path40.startsWith("/") ? path40 : `/${path40}`;
15552
+ function buildUrl(zoneId, path41 = "/dns_records", query) {
15553
+ const normalizedPath = path41.startsWith("/") ? path41 : `/${path41}`;
14810
15554
  const url = new URL(
14811
15555
  `${CLOUDFLARE_API_BASE_URL}/zones/${zoneId}${normalizedPath}`
14812
15556
  );
@@ -15019,12 +15763,12 @@ async function waitForReady(client, vpsId) {
15019
15763
  }
15020
15764
  async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
15021
15765
  const safeClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "-");
15022
- const playbookDir = path27.resolve(process.cwd(), "infrastructure", "ansible");
15023
- const playbookPath = path27.join(playbookDir, "deploy.yml");
15024
- const inventoryPath = path27.join(playbookDir, "inventory", "hosts.yml");
15025
- const clientVarsPath = path27.join(playbookDir, "vars", `${safeClientName}.yml`);
15026
- const varsDir = path27.join(playbookDir, "vars");
15027
- 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))) {
15028
15772
  throw new Error(`Invalid client name for vars path: ${clientName}`);
15029
15773
  }
15030
15774
  const args = [
@@ -15114,7 +15858,7 @@ function buildInventoryRecord(params) {
15114
15858
  // src/mcp/tools/export-orchestration.ts
15115
15859
  init_active_agent();
15116
15860
  import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync15 } from "fs";
15117
- import path29 from "path";
15861
+ import path30 from "path";
15118
15862
  import { z as z38 } from "zod";
15119
15863
 
15120
15864
  // src/lib/orchestration-package.ts
@@ -15123,8 +15867,8 @@ init_identity();
15123
15867
  init_platform_procedures();
15124
15868
  import { randomUUID as randomUUID5 } from "crypto";
15125
15869
  import { copyFileSync, existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "fs";
15126
- import os11 from "os";
15127
- import path28 from "path";
15870
+ import os12 from "os";
15871
+ import path29 from "path";
15128
15872
  var PACKAGE_VERSION = "1.0";
15129
15873
  var ROSTER_FILENAME = "exe-employees.json";
15130
15874
  var ROSTER_BACKUP_FILENAME = "exe-employees.json.bak";
@@ -15190,10 +15934,10 @@ function validateProcedureEntry(value, index) {
15190
15934
  };
15191
15935
  }
15192
15936
  function getRosterPath() {
15193
- return path28.join(os11.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
15937
+ return path29.join(os12.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
15194
15938
  }
15195
15939
  function getBackupPath() {
15196
- return path28.join(os11.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
15940
+ return path29.join(os12.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
15197
15941
  }
15198
15942
  function readRosterFile() {
15199
15943
  const rosterPath = getRosterPath();
@@ -15210,7 +15954,7 @@ function writeRosterFile(roster) {
15210
15954
  throw new Error("Refusing to write empty roster \u2014 this would delete all employees");
15211
15955
  }
15212
15956
  const rosterPath = getRosterPath();
15213
- mkdirSync12(path28.dirname(rosterPath), { recursive: true });
15957
+ mkdirSync12(path29.dirname(rosterPath), { recursive: true });
15214
15958
  if (existsSync22(rosterPath)) {
15215
15959
  const currentRoster = readRosterFile();
15216
15960
  if (roster.length < currentRoster.length) {
@@ -15482,7 +16226,7 @@ function registerExportOrchestration(server2) {
15482
16226
  try {
15483
16227
  await initStore();
15484
16228
  const pkg = await exportOrchestration(getActiveAgent().agentId);
15485
- mkdirSync13(path29.dirname(output_path), { recursive: true });
16229
+ mkdirSync13(path30.dirname(output_path), { recursive: true });
15486
16230
  writeFileSync15(output_path, `${JSON.stringify(pkg, null, 2)}
15487
16231
  `, "utf-8");
15488
16232
  return {
@@ -15683,17 +16427,17 @@ function registerQueryConversations(server2) {
15683
16427
  // src/mcp/tools/load-skill.ts
15684
16428
  import { z as z41 } from "zod";
15685
16429
  import { readFileSync as readFileSync20, readdirSync as readdirSync9, statSync as statSync3 } from "fs";
15686
- import path30 from "path";
16430
+ import path31 from "path";
15687
16431
  import { homedir as homedir2 } from "os";
15688
- var SKILLS_DIR = path30.join(homedir2(), ".claude", "skills");
16432
+ var SKILLS_DIR = path31.join(homedir2(), ".claude", "skills");
15689
16433
  function listAvailableSkills() {
15690
16434
  try {
15691
16435
  const entries = readdirSync9(SKILLS_DIR);
15692
16436
  return entries.filter((entry) => {
15693
16437
  try {
15694
- const entryPath = path30.join(SKILLS_DIR, entry);
16438
+ const entryPath = path31.join(SKILLS_DIR, entry);
15695
16439
  if (!statSync3(entryPath).isDirectory()) return false;
15696
- const skillFile = path30.join(entryPath, "SKILL.md");
16440
+ const skillFile = path31.join(entryPath, "SKILL.md");
15697
16441
  statSync3(skillFile);
15698
16442
  return true;
15699
16443
  } catch {
@@ -15736,8 +16480,8 @@ ${skills.map((s) => `- ${s}`).join("\n")}`
15736
16480
  }]
15737
16481
  };
15738
16482
  }
15739
- const sanitized = path30.basename(skill_name);
15740
- 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");
15741
16485
  try {
15742
16486
  const content = readFileSync20(skillFile, "utf-8");
15743
16487
  return {
@@ -16378,8 +17122,8 @@ init_database();
16378
17122
  import { readdir } from "fs/promises";
16379
17123
  import { createReadStream } from "fs";
16380
17124
  import { createInterface } from "readline";
16381
- import path31 from "path";
16382
- import os12 from "os";
17125
+ import path32 from "path";
17126
+ import os13 from "os";
16383
17127
  var MODEL_PRICING = {
16384
17128
  // Opus 4.5+ ($5/$25 — Anthropic price drop from original Opus 4)
16385
17129
  "claude-opus-4-7": { input: 5 / 1e6, output: 25 / 1e6, cacheRead: 0.5 / 1e6, cacheWrite: 6.25 / 1e6 },
@@ -16427,18 +17171,18 @@ async function getAgentSpend(period = "7d") {
16427
17171
  for (const row of result.rows) {
16428
17172
  sessionAgent.set(row.session_uuid, row.agent_id);
16429
17173
  }
16430
- const claudeDir = path31.join(os12.homedir(), ".claude", "projects");
17174
+ const claudeDir = path32.join(os13.homedir(), ".claude", "projects");
16431
17175
  let projectDirs = [];
16432
17176
  try {
16433
17177
  const entries = await readdir(claudeDir);
16434
- projectDirs = entries.map((e) => path31.join(claudeDir, e));
17178
+ projectDirs = entries.map((e) => path32.join(claudeDir, e));
16435
17179
  } catch {
16436
17180
  return [];
16437
17181
  }
16438
17182
  const agentTotals = /* @__PURE__ */ new Map();
16439
17183
  for (const [sessionUuid, agentId] of sessionAgent) {
16440
17184
  for (const dir of projectDirs) {
16441
- const jsonlPath = path31.join(dir, `${sessionUuid}.jsonl`);
17185
+ const jsonlPath = path32.join(dir, `${sessionUuid}.jsonl`);
16442
17186
  try {
16443
17187
  const usage = await extractSessionUsage(jsonlPath);
16444
17188
  if (usage.input === 0 && usage.output === 0) continue;
@@ -17016,12 +17760,12 @@ function registerExportGraph(server2) {
17016
17760
  }
17017
17761
  const html = await exportGraphHTML(client);
17018
17762
  const fs = await import("fs");
17019
- const path40 = await import("path");
17020
- const os14 = await import("os");
17021
- 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");
17022
17766
  fs.mkdirSync(outDir, { recursive: true });
17023
17767
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
17024
- const filePath = path40.join(outDir, `graph-${timestamp}.html`);
17768
+ const filePath = path41.join(outDir, `graph-${timestamp}.html`);
17025
17769
  fs.writeFileSync(filePath, html, "utf-8");
17026
17770
  return {
17027
17771
  content: [
@@ -17243,9 +17987,9 @@ function registerListAgentSessions(server2) {
17243
17987
  // src/mcp/tools/get-daemon-health.ts
17244
17988
  import { z as z56 } from "zod";
17245
17989
  import { existsSync as existsSync23, readFileSync as readFileSync21 } from "fs";
17246
- import path32 from "path";
17990
+ import path33 from "path";
17247
17991
  import { homedir as homedir3 } from "os";
17248
- var PID_PATH2 = path32.join(homedir3(), ".exe-os", "exed.pid");
17992
+ var PID_PATH2 = path33.join(homedir3(), ".exe-os", "exed.pid");
17249
17993
  function formatUptime(seconds) {
17250
17994
  const h = Math.floor(seconds / 3600);
17251
17995
  const m = Math.floor(seconds % 3600 / 60);
@@ -17435,8 +18179,8 @@ init_worker_gate();
17435
18179
  init_config();
17436
18180
  import { z as z58 } from "zod";
17437
18181
  import { readdirSync as readdirSync11, existsSync as existsSync26 } from "fs";
17438
- import path34 from "path";
17439
- 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");
17440
18184
  function countAliveWorkers() {
17441
18185
  let alive = 0;
17442
18186
  let stale = 0;
@@ -17512,7 +18256,7 @@ import { z as z59 } from "zod";
17512
18256
  // src/bin/exe-doctor.ts
17513
18257
  init_store();
17514
18258
  init_database();
17515
- import os13 from "os";
18259
+ import os14 from "os";
17516
18260
 
17517
18261
  // src/lib/is-main.ts
17518
18262
  import { realpathSync } from "fs";
@@ -17532,7 +18276,7 @@ function isMainModule(importMetaUrl) {
17532
18276
  // src/bin/exe-doctor.ts
17533
18277
  import { existsSync as existsSync27, readFileSync as readFileSync23 } from "fs";
17534
18278
  import { spawn as spawn2 } from "child_process";
17535
- import path35 from "path";
18279
+ import path36 from "path";
17536
18280
  import { randomUUID as randomUUID7 } from "crypto";
17537
18281
 
17538
18282
  // src/lib/conflict-detector.ts
@@ -17935,7 +18679,7 @@ async function auditOrphanedProjects(client) {
17935
18679
  for (const row of result.rows) {
17936
18680
  const name = row.project_name;
17937
18681
  const count = Number(row.cnt);
17938
- 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));
17939
18683
  if (!exists) {
17940
18684
  orphans.push({ project_name: name, count });
17941
18685
  }
@@ -17943,7 +18687,7 @@ async function auditOrphanedProjects(client) {
17943
18687
  return orphans;
17944
18688
  }
17945
18689
  function auditHookHealth() {
17946
- const logPath = path35.join(
18690
+ const logPath = path36.join(
17947
18691
  process.env.HOME ?? process.env.USERPROFILE ?? "",
17948
18692
  ".exe-os",
17949
18693
  "logs",
@@ -18029,7 +18773,7 @@ function formatReport(report, flags) {
18029
18773
  }
18030
18774
  lines.push("");
18031
18775
  }
18032
- const totalMemGB = os13.totalmem() / (1024 * 1024 * 1024);
18776
+ const totalMemGB = os14.totalmem() / (1024 * 1024 * 1024);
18033
18777
  const isLowMemSystem = totalMemGB <= 8;
18034
18778
  if (isLowMemSystem && report.nullVectors > 0) {
18035
18779
  lines.push(`\u{1F7E2} Null vectors: ${fmtNum(report.nullVectors)} / ${fmtNum(s.total)} (expected \u2014 8GB system, keyword search mode)`);
@@ -18151,7 +18895,7 @@ async function fixNullVectors() {
18151
18895
  }
18152
18896
  }
18153
18897
  const npmRoot = (await import("child_process")).execSync("npm root -g", { encoding: "utf8" }).trim();
18154
- 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");
18155
18899
  return new Promise((resolve, reject) => {
18156
18900
  const child = spawn2("node", [backfillPath], { stdio: "inherit" });
18157
18901
  if (child.pid) registerWorkerPid2(child.pid);
@@ -18351,7 +19095,7 @@ import { z as z60 } from "zod";
18351
19095
  init_database();
18352
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";
18353
19097
  import crypto16 from "crypto";
18354
- import path37 from "path";
19098
+ import path38 from "path";
18355
19099
  import { homedir as homedir6 } from "os";
18356
19100
 
18357
19101
  // src/lib/crypto.ts
@@ -18425,7 +19169,7 @@ function sqlSafe(v) {
18425
19169
  }
18426
19170
  function logError(msg) {
18427
19171
  try {
18428
- const logPath = path37.join(homedir6(), ".exe-os", "workers.log");
19172
+ const logPath = path38.join(homedir6(), ".exe-os", "workers.log");
18429
19173
  appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
18430
19174
  `);
18431
19175
  } catch {
@@ -18434,7 +19178,7 @@ function logError(msg) {
18434
19178
  var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
18435
19179
  var FETCH_TIMEOUT_MS4 = 3e4;
18436
19180
  var PUSH_BATCH_SIZE = 5e3;
18437
- 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");
18438
19182
  var LOCK_STALE_MS = 3e4;
18439
19183
  async function withRosterLock(fn) {
18440
19184
  try {
@@ -18825,7 +19569,7 @@ async function cloudSync(config2) {
18825
19569
  try {
18826
19570
  const employees = await loadEmployees();
18827
19571
  rosterResult.employees = employees.length;
18828
- const idDir = path37.join(EXE_AI_DIR, "identity");
19572
+ const idDir = path38.join(EXE_AI_DIR, "identity");
18829
19573
  if (existsSync29(idDir)) {
18830
19574
  rosterResult.identities = readdirSync12(idDir).filter((f) => f.endsWith(".md")).length;
18831
19575
  }
@@ -18844,7 +19588,7 @@ async function cloudSync(config2) {
18844
19588
  roster: rosterResult
18845
19589
  };
18846
19590
  }
18847
- 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");
18848
19592
  function consumeRosterDeletions() {
18849
19593
  try {
18850
19594
  if (!existsSync29(ROSTER_DELETIONS_PATH)) return [];
@@ -18856,9 +19600,9 @@ function consumeRosterDeletions() {
18856
19600
  }
18857
19601
  }
18858
19602
  function buildRosterBlob(paths) {
18859
- const rosterPath = paths?.rosterPath ?? path37.join(EXE_AI_DIR, "exe-employees.json");
18860
- const identityDir = paths?.identityDir ?? path37.join(EXE_AI_DIR, "identity");
18861
- 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");
18862
19606
  let roster = [];
18863
19607
  if (existsSync29(rosterPath)) {
18864
19608
  try {
@@ -18870,7 +19614,7 @@ function buildRosterBlob(paths) {
18870
19614
  if (existsSync29(identityDir)) {
18871
19615
  for (const file of readdirSync12(identityDir).filter((f) => f.endsWith(".md"))) {
18872
19616
  try {
18873
- identities[file] = readFileSync25(path37.join(identityDir, file), "utf-8");
19617
+ identities[file] = readFileSync25(path38.join(identityDir, file), "utf-8");
18874
19618
  } catch {
18875
19619
  }
18876
19620
  }
@@ -18883,7 +19627,7 @@ function buildRosterBlob(paths) {
18883
19627
  }
18884
19628
  }
18885
19629
  let agentConfig;
18886
- const agentConfigPath = path37.join(EXE_AI_DIR, "agent-config.json");
19630
+ const agentConfigPath = path38.join(EXE_AI_DIR, "agent-config.json");
18887
19631
  if (existsSync29(agentConfigPath)) {
18888
19632
  try {
18889
19633
  agentConfig = JSON.parse(readFileSync25(agentConfigPath, "utf-8"));
@@ -18962,7 +19706,7 @@ async function cloudPullRoster(config2) {
18962
19706
  }
18963
19707
  }
18964
19708
  function mergeConfig(remoteConfig, configPath) {
18965
- const cfgPath = configPath ?? path37.join(EXE_AI_DIR, "config.json");
19709
+ const cfgPath = configPath ?? path38.join(EXE_AI_DIR, "config.json");
18966
19710
  let local = {};
18967
19711
  if (existsSync29(cfgPath)) {
18968
19712
  try {
@@ -18971,14 +19715,14 @@ function mergeConfig(remoteConfig, configPath) {
18971
19715
  }
18972
19716
  }
18973
19717
  const merged = { ...remoteConfig, ...local };
18974
- const dir = path37.dirname(cfgPath);
19718
+ const dir = path38.dirname(cfgPath);
18975
19719
  if (!existsSync29(dir)) mkdirSync16(dir, { recursive: true });
18976
19720
  writeFileSync19(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
18977
19721
  }
18978
19722
  async function mergeRosterFromRemote(remote, paths) {
18979
19723
  return withRosterLock(async () => {
18980
19724
  const rosterPath = paths?.rosterPath ?? void 0;
18981
- const identityDir = paths?.identityDir ?? path37.join(EXE_AI_DIR, "identity");
19725
+ const identityDir = paths?.identityDir ?? path38.join(EXE_AI_DIR, "identity");
18982
19726
  const localEmployees = await loadEmployees(rosterPath);
18983
19727
  const localNames = new Set(localEmployees.map((e) => e.name));
18984
19728
  let added = 0;
@@ -19000,7 +19744,7 @@ async function mergeRosterFromRemote(remote, paths) {
19000
19744
  const remoteIdentity = remote.identities[matchedKey];
19001
19745
  if (remoteIdentity) {
19002
19746
  if (!existsSync29(identityDir)) mkdirSync16(identityDir, { recursive: true });
19003
- const idPath = path37.join(identityDir, `${remoteEmp.name}.md`);
19747
+ const idPath = path38.join(identityDir, `${remoteEmp.name}.md`);
19004
19748
  let localIdentity = null;
19005
19749
  try {
19006
19750
  localIdentity = existsSync29(idPath) ? readFileSync25(idPath, "utf-8") : null;
@@ -19033,7 +19777,7 @@ async function mergeRosterFromRemote(remote, paths) {
19033
19777
  }
19034
19778
  if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
19035
19779
  try {
19036
- const agentConfigPath = path37.join(EXE_AI_DIR, "agent-config.json");
19780
+ const agentConfigPath = path38.join(EXE_AI_DIR, "agent-config.json");
19037
19781
  let local = {};
19038
19782
  if (existsSync29(agentConfigPath)) {
19039
19783
  try {
@@ -19762,15 +20506,515 @@ function registerGetLicenseStatus(server2) {
19762
20506
  );
19763
20507
  }
19764
20508
 
19765
- // src/mcp/tools/people-roster.ts
20509
+ // src/mcp/tools/backup-vps.ts
20510
+ init_keychain();
19766
20511
  import { z as z64 } from "zod";
19767
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
+
19768
21012
  // src/lib/people.ts
19769
21013
  init_config();
19770
21014
  import { readFile as readFile5, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
19771
21015
  import { existsSync as existsSync30, readFileSync as readFileSync26 } from "fs";
19772
- import path38 from "path";
19773
- 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");
19774
21018
  async function loadPeople() {
19775
21019
  if (!existsSync30(PEOPLE_PATH)) return [];
19776
21020
  try {
@@ -19781,7 +21025,7 @@ async function loadPeople() {
19781
21025
  }
19782
21026
  }
19783
21027
  async function savePeople(people) {
19784
- await mkdir5(path38.dirname(PEOPLE_PATH), { recursive: true });
21028
+ await mkdir5(path39.dirname(PEOPLE_PATH), { recursive: true });
19785
21029
  await writeFile6(PEOPLE_PATH, JSON.stringify(people, null, 2) + "\n", "utf-8");
19786
21030
  }
19787
21031
  async function addPerson(person) {
@@ -19811,10 +21055,10 @@ function registerAddPerson(server2) {
19811
21055
  title: "Add Person",
19812
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.",
19813
21057
  inputSchema: {
19814
- name: z64.string().describe("Person's name"),
19815
- role: z64.string().describe("Their role (e.g. co-founder, customer, partner)"),
19816
- relationship: z64.string().describe("Relationship to the organization (e.g. co-founder, early adopter, investor)"),
19817
- 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")
19818
21062
  }
19819
21063
  },
19820
21064
  async ({ name, role, relationship, notes }) => {
@@ -19860,7 +21104,7 @@ function registerGetPerson(server2) {
19860
21104
  title: "Get Person",
19861
21105
  description: "Look up a specific person by name from the people roster.",
19862
21106
  inputSchema: {
19863
- name: z64.string().describe("Person's name to look up")
21107
+ name: z65.string().describe("Person's name to look up")
19864
21108
  }
19865
21109
  },
19866
21110
  async ({ name }) => {
@@ -19888,7 +21132,7 @@ init_active_agent();
19888
21132
  init_agent_config();
19889
21133
  init_runtime_table();
19890
21134
  init_employees();
19891
- import { z as z65 } from "zod";
21135
+ import { z as z66 } from "zod";
19892
21136
  function registerSetAgentConfig(server2) {
19893
21137
  server2.registerTool(
19894
21138
  "set_agent_config",
@@ -19896,14 +21140,22 @@ function registerSetAgentConfig(server2) {
19896
21140
  title: "Set Agent Config",
19897
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.",
19898
21142
  inputSchema: {
19899
- agent_id: z65.string().optional().describe("Agent name, or 'default' for org-wide default. Omit to view all agents."),
19900
- runtime: z65.string().optional().describe(`Runtime: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`),
19901
- 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)")
19902
21146
  }
19903
21147
  },
19904
21148
  async ({ agent_id, runtime, model }) => {
19905
- const { agentRole } = getActiveAgent();
19906
- 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) {
19907
21159
  return {
19908
21160
  content: [
19909
21161
  {
@@ -19988,6 +21240,72 @@ function registerSetAgentConfig(server2) {
19988
21240
  );
19989
21241
  }
19990
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
+
19991
21309
  // src/lib/telemetry.ts
19992
21310
  var ENABLED = process.env.EXE_TELEMETRY === "1";
19993
21311
  var initialized = false;
@@ -20115,6 +21433,7 @@ registerGetAutoWakeStatus(server);
20115
21433
  registerGetWorkerGate(server);
20116
21434
  registerRunMemoryAudit(server);
20117
21435
  registerCloudSync(server);
21436
+ registerBackupVps(server);
20118
21437
  registerGetMemoryCardinality(server);
20119
21438
  registerRunConsolidation(server);
20120
21439
  registerGetLicenseStatus(server);
@@ -20122,12 +21441,19 @@ registerAddPerson(server);
20122
21441
  registerListPeople(server);
20123
21442
  registerGetPerson(server);
20124
21443
  registerSetAgentConfig(server);
21444
+ registerListEmployees(server);
20125
21445
  try {
20126
21446
  await initStore();
20127
21447
  process.stderr.write("[exe-os] MCP server starting...\n");
20128
21448
  const transport = new StdioServerTransport();
20129
21449
  await server.connect(transport);
20130
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
+ });
20131
21457
  void (async () => {
20132
21458
  try {
20133
21459
  const { checkLicense: checkLicense2 } = await Promise.resolve().then(() => (init_license(), license_exports));
@@ -20184,20 +21510,20 @@ try {
20184
21510
  `
20185
21511
  );
20186
21512
  const thisFile = fileURLToPath5(import.meta.url);
20187
- const backfillPath = path39.resolve(
20188
- path39.dirname(thisFile),
21513
+ const backfillPath = path40.resolve(
21514
+ path40.dirname(thisFile),
20189
21515
  "../bin/backfill-vectors.js"
20190
21516
  );
20191
21517
  if (existsSync31(backfillPath)) {
20192
21518
  const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
20193
- const logPath = path39.join(exeDir, "workers.log");
20194
- mkdirSync17(path39.dirname(logPath), { recursive: true });
21519
+ const logPath = path40.join(exeDir, "workers.log");
21520
+ mkdirSync17(path40.dirname(logPath), { recursive: true });
20195
21521
  let logFd = "ignore";
20196
21522
  try {
20197
21523
  logFd = openSync3(logPath, "a");
20198
21524
  } catch {
20199
21525
  }
20200
- const child = spawn3(process.execPath, [backfillPath], {
21526
+ const child = spawn4(process.execPath, [backfillPath], {
20201
21527
  detached: true,
20202
21528
  stdio: ["ignore", "ignore", logFd]
20203
21529
  });