@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
@@ -632,6 +632,118 @@ var init_config = __esm({
632
632
  }
633
633
  });
634
634
 
635
+ // src/lib/runtime-table.ts
636
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
637
+ var init_runtime_table = __esm({
638
+ "src/lib/runtime-table.ts"() {
639
+ "use strict";
640
+ RUNTIME_TABLE = {
641
+ codex: {
642
+ binary: "codex",
643
+ launchMode: "interactive",
644
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
645
+ inlineFlag: "--no-alt-screen",
646
+ apiKeyEnv: "OPENAI_API_KEY",
647
+ defaultModel: "gpt-5.4"
648
+ },
649
+ opencode: {
650
+ binary: "opencode",
651
+ launchMode: "exec",
652
+ autoApproveFlag: "--dangerously-skip-permissions",
653
+ inlineFlag: "",
654
+ apiKeyEnv: "ANTHROPIC_API_KEY",
655
+ defaultModel: "anthropic/claude-sonnet-4-6"
656
+ }
657
+ };
658
+ DEFAULT_RUNTIME = "claude";
659
+ }
660
+ });
661
+
662
+ // src/lib/agent-config.ts
663
+ var agent_config_exports = {};
664
+ __export(agent_config_exports, {
665
+ AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
666
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
667
+ KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
668
+ RUNTIME_LABELS: () => RUNTIME_LABELS,
669
+ clearAgentRuntime: () => clearAgentRuntime,
670
+ getAgentRuntime: () => getAgentRuntime,
671
+ loadAgentConfig: () => loadAgentConfig,
672
+ saveAgentConfig: () => saveAgentConfig,
673
+ setAgentRuntime: () => setAgentRuntime
674
+ });
675
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
676
+ import path2 from "path";
677
+ function loadAgentConfig() {
678
+ if (!existsSync2(AGENT_CONFIG_PATH)) return {};
679
+ try {
680
+ return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
681
+ } catch {
682
+ return {};
683
+ }
684
+ }
685
+ function saveAgentConfig(config2) {
686
+ const dir = path2.dirname(AGENT_CONFIG_PATH);
687
+ if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
688
+ writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
689
+ }
690
+ function getAgentRuntime(agentId) {
691
+ const config2 = loadAgentConfig();
692
+ const entry = config2[agentId];
693
+ if (entry) return entry;
694
+ const orgDefault = config2["default"];
695
+ if (orgDefault) return orgDefault;
696
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
697
+ }
698
+ function setAgentRuntime(agentId, runtime, model) {
699
+ const knownModels = KNOWN_RUNTIMES[runtime];
700
+ if (!knownModels) {
701
+ return {
702
+ ok: false,
703
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
704
+ };
705
+ }
706
+ if (!knownModels.includes(model)) {
707
+ return {
708
+ ok: false,
709
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
710
+ };
711
+ }
712
+ const config2 = loadAgentConfig();
713
+ config2[agentId] = { runtime, model };
714
+ saveAgentConfig(config2);
715
+ return { ok: true };
716
+ }
717
+ function clearAgentRuntime(agentId) {
718
+ const config2 = loadAgentConfig();
719
+ delete config2[agentId];
720
+ saveAgentConfig(config2);
721
+ }
722
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
723
+ var init_agent_config = __esm({
724
+ "src/lib/agent-config.ts"() {
725
+ "use strict";
726
+ init_config();
727
+ init_runtime_table();
728
+ AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
729
+ KNOWN_RUNTIMES = {
730
+ claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
731
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
732
+ opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
733
+ };
734
+ RUNTIME_LABELS = {
735
+ claude: "Claude Code (Anthropic)",
736
+ codex: "Codex (OpenAI)",
737
+ opencode: "OpenCode (open source)"
738
+ };
739
+ DEFAULT_MODELS = {
740
+ claude: "claude-opus-4",
741
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
742
+ opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
743
+ };
744
+ }
745
+ });
746
+
635
747
  // src/lib/employees.ts
636
748
  var employees_exports = {};
637
749
  __export(employees_exports, {
@@ -647,6 +759,7 @@ __export(employees_exports, {
647
759
  getEmployeeByRole: () => getEmployeeByRole,
648
760
  getEmployeeNamesByRole: () => getEmployeeNamesByRole,
649
761
  hasRole: () => hasRole,
762
+ hireEmployee: () => hireEmployee,
650
763
  isCoordinatorName: () => isCoordinatorName,
651
764
  isCoordinatorRole: () => isCoordinatorRole,
652
765
  isMultiInstance: () => isMultiInstance,
@@ -659,9 +772,9 @@ __export(employees_exports, {
659
772
  validateEmployeeName: () => validateEmployeeName
660
773
  });
661
774
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
662
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
775
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
663
776
  import { execSync } from "child_process";
664
- import path2 from "path";
777
+ import path3 from "path";
665
778
  import os2 from "os";
666
779
  function normalizeRole(role) {
667
780
  return (role ?? "").trim().toLowerCase();
@@ -698,7 +811,7 @@ function validateEmployeeName(name) {
698
811
  return { valid: true };
699
812
  }
700
813
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
701
- if (!existsSync2(employeesPath)) {
814
+ if (!existsSync3(employeesPath)) {
702
815
  return [];
703
816
  }
704
817
  const raw = await readFile2(employeesPath, "utf-8");
@@ -709,13 +822,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
709
822
  }
710
823
  }
711
824
  async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
712
- await mkdir2(path2.dirname(employeesPath), { recursive: true });
825
+ await mkdir2(path3.dirname(employeesPath), { recursive: true });
713
826
  await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
714
827
  }
715
828
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
716
- if (!existsSync2(employeesPath)) return [];
829
+ if (!existsSync3(employeesPath)) return [];
717
830
  try {
718
- return JSON.parse(readFileSync2(employeesPath, "utf-8"));
831
+ return JSON.parse(readFileSync3(employeesPath, "utf-8"));
719
832
  } catch {
720
833
  return [];
721
834
  }
@@ -757,6 +870,52 @@ function addEmployee(employees, employee) {
757
870
  }
758
871
  return [...employees, normalized];
759
872
  }
873
+ function appendToCoordinatorTeam(employee) {
874
+ const coordinator = getCoordinatorEmployee(loadEmployeesSync());
875
+ if (!coordinator) return;
876
+ const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
877
+ if (!existsSync3(idPath)) return;
878
+ const content = readFileSync3(idPath, "utf-8");
879
+ if (content.includes(`**${capitalize(employee.name)}`)) return;
880
+ const teamMatch = content.match(TEAM_SECTION_RE);
881
+ if (!teamMatch || teamMatch.index === void 0) return;
882
+ const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
883
+ const nextHeading = afterTeam.match(/\n## /);
884
+ const entry = `
885
+ **${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
886
+ `;
887
+ let updated;
888
+ if (nextHeading && nextHeading.index !== void 0) {
889
+ const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
890
+ updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
891
+ } else {
892
+ updated = content.trimEnd() + "\n" + entry;
893
+ }
894
+ writeFileSync2(idPath, updated, "utf-8");
895
+ }
896
+ function capitalize(s) {
897
+ return s.charAt(0).toUpperCase() + s.slice(1);
898
+ }
899
+ async function hireEmployee(employee) {
900
+ const employees = await loadEmployees();
901
+ const updated = addEmployee(employees, employee);
902
+ await saveEmployees(updated);
903
+ try {
904
+ appendToCoordinatorTeam(employee);
905
+ } catch {
906
+ }
907
+ try {
908
+ const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
909
+ const config2 = loadAgentConfig2();
910
+ const name = employee.name.toLowerCase();
911
+ if (!config2[name] && config2["default"]) {
912
+ config2[name] = { ...config2["default"] };
913
+ saveAgentConfig2(config2);
914
+ }
915
+ } catch {
916
+ }
917
+ return updated;
918
+ }
760
919
  async function normalizeRosterCase(rosterPath) {
761
920
  const employees = await loadEmployees(rosterPath);
762
921
  let changed = false;
@@ -766,14 +925,14 @@ async function normalizeRosterCase(rosterPath) {
766
925
  emp.name = emp.name.toLowerCase();
767
926
  changed = true;
768
927
  try {
769
- const identityDir = path2.join(os2.homedir(), ".exe-os", "identity");
770
- const oldPath = path2.join(identityDir, `${oldName}.md`);
771
- const newPath = path2.join(identityDir, `${emp.name}.md`);
772
- if (existsSync2(oldPath) && !existsSync2(newPath)) {
928
+ const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
929
+ const oldPath = path3.join(identityDir, `${oldName}.md`);
930
+ const newPath = path3.join(identityDir, `${emp.name}.md`);
931
+ if (existsSync3(oldPath) && !existsSync3(newPath)) {
773
932
  renameSync2(oldPath, newPath);
774
- } else if (existsSync2(oldPath) && oldPath !== newPath) {
775
- const content = readFileSync2(oldPath, "utf-8");
776
- writeFileSync(newPath, content, "utf-8");
933
+ } else if (existsSync3(oldPath) && oldPath !== newPath) {
934
+ const content = readFileSync3(oldPath, "utf-8");
935
+ writeFileSync2(newPath, content, "utf-8");
777
936
  if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
778
937
  unlinkSync(oldPath);
779
938
  }
@@ -803,7 +962,7 @@ function registerBinSymlinks(name) {
803
962
  errors.push("Could not find 'exe-os' in PATH");
804
963
  return { created, skipped, errors };
805
964
  }
806
- const binDir = path2.dirname(exeBinPath);
965
+ const binDir = path3.dirname(exeBinPath);
807
966
  let target;
808
967
  try {
809
968
  target = readlinkSync(exeBinPath);
@@ -813,8 +972,8 @@ function registerBinSymlinks(name) {
813
972
  }
814
973
  for (const suffix of ["", "-opencode"]) {
815
974
  const linkName = `${name}${suffix}`;
816
- const linkPath = path2.join(binDir, linkName);
817
- if (existsSync2(linkPath)) {
975
+ const linkPath = path3.join(binDir, linkName);
976
+ if (existsSync3(linkPath)) {
818
977
  skipped.push(linkName);
819
978
  continue;
820
979
  }
@@ -827,21 +986,619 @@ function registerBinSymlinks(name) {
827
986
  }
828
987
  return { created, skipped, errors };
829
988
  }
830
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
989
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
831
990
  var init_employees = __esm({
832
991
  "src/lib/employees.ts"() {
833
992
  "use strict";
834
993
  init_config();
835
- EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
994
+ EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
836
995
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
837
996
  COORDINATOR_ROLE = "COO";
838
997
  MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
998
+ IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
999
+ TEAM_SECTION_RE = /^## Team\b.*$/m;
1000
+ }
1001
+ });
1002
+
1003
+ // src/lib/database-adapter.ts
1004
+ import os3 from "os";
1005
+ import path4 from "path";
1006
+ import { createRequire } from "module";
1007
+ import { pathToFileURL } from "url";
1008
+ function quotedIdentifier(identifier) {
1009
+ return `"${identifier.replace(/"/g, '""')}"`;
1010
+ }
1011
+ function unqualifiedTableName(name) {
1012
+ const raw = name.trim().replace(/^"|"$/g, "");
1013
+ const parts = raw.split(".");
1014
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
1015
+ }
1016
+ function stripTrailingSemicolon(sql) {
1017
+ return sql.trim().replace(/;+\s*$/u, "");
1018
+ }
1019
+ function appendClause(sql, clause) {
1020
+ const trimmed = stripTrailingSemicolon(sql);
1021
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
1022
+ if (!returningMatch) {
1023
+ return `${trimmed}${clause}`;
1024
+ }
1025
+ const idx = returningMatch.index;
1026
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
1027
+ }
1028
+ function normalizeStatement(stmt) {
1029
+ if (typeof stmt === "string") {
1030
+ return { kind: "positional", sql: stmt, args: [] };
1031
+ }
1032
+ const sql = stmt.sql;
1033
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
1034
+ return { kind: "positional", sql, args: stmt.args ?? [] };
1035
+ }
1036
+ return { kind: "named", sql, args: stmt.args };
1037
+ }
1038
+ function rewriteBooleanLiterals(sql) {
1039
+ let out = sql;
1040
+ for (const column of BOOLEAN_COLUMN_NAMES) {
1041
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
1042
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
1043
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
1044
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
1045
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
1046
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
1047
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
1048
+ }
1049
+ return out;
1050
+ }
1051
+ function rewriteInsertOrIgnore(sql) {
1052
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
1053
+ return sql;
1054
+ }
1055
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
1056
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
1057
+ }
1058
+ function rewriteInsertOrReplace(sql) {
1059
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
1060
+ if (!match) {
1061
+ return sql;
1062
+ }
1063
+ const rawTable = match[1];
1064
+ const rawColumns = match[2];
1065
+ const remainder = match[3];
1066
+ const tableName = unqualifiedTableName(rawTable);
1067
+ const conflictKeys = UPSERT_KEYS[tableName];
1068
+ if (!conflictKeys?.length) {
1069
+ return sql;
1070
+ }
1071
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
1072
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
1073
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
1074
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
1075
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
1076
+ }
1077
+ function rewriteSql(sql) {
1078
+ let out = sql;
1079
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
1080
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
1081
+ out = rewriteBooleanLiterals(out);
1082
+ out = rewriteInsertOrReplace(out);
1083
+ out = rewriteInsertOrIgnore(out);
1084
+ return stripTrailingSemicolon(out);
1085
+ }
1086
+ function toBoolean(value) {
1087
+ if (value === null || value === void 0) return value;
1088
+ if (typeof value === "boolean") return value;
1089
+ if (typeof value === "number") return value !== 0;
1090
+ if (typeof value === "bigint") return value !== 0n;
1091
+ if (typeof value === "string") {
1092
+ const normalized = value.trim().toLowerCase();
1093
+ if (normalized === "0" || normalized === "false") return false;
1094
+ if (normalized === "1" || normalized === "true") return true;
1095
+ }
1096
+ return Boolean(value);
1097
+ }
1098
+ function countQuestionMarks(sql, end) {
1099
+ let count = 0;
1100
+ let inSingle = false;
1101
+ let inDouble = false;
1102
+ let inLineComment = false;
1103
+ let inBlockComment = false;
1104
+ for (let i = 0; i < end; i++) {
1105
+ const ch = sql[i];
1106
+ const next = sql[i + 1];
1107
+ if (inLineComment) {
1108
+ if (ch === "\n") inLineComment = false;
1109
+ continue;
1110
+ }
1111
+ if (inBlockComment) {
1112
+ if (ch === "*" && next === "/") {
1113
+ inBlockComment = false;
1114
+ i += 1;
1115
+ }
1116
+ continue;
1117
+ }
1118
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1119
+ inLineComment = true;
1120
+ i += 1;
1121
+ continue;
1122
+ }
1123
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1124
+ inBlockComment = true;
1125
+ i += 1;
1126
+ continue;
1127
+ }
1128
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1129
+ inSingle = !inSingle;
1130
+ continue;
1131
+ }
1132
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1133
+ inDouble = !inDouble;
1134
+ continue;
1135
+ }
1136
+ if (!inSingle && !inDouble && ch === "?") {
1137
+ count += 1;
1138
+ }
1139
+ }
1140
+ return count;
1141
+ }
1142
+ function findBooleanPlaceholderIndexes(sql) {
1143
+ const indexes = /* @__PURE__ */ new Set();
1144
+ for (const column of BOOLEAN_COLUMN_NAMES) {
1145
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
1146
+ for (const match of sql.matchAll(pattern)) {
1147
+ const matchText = match[0];
1148
+ const qIndex = match.index + matchText.lastIndexOf("?");
1149
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
1150
+ }
1151
+ }
1152
+ return indexes;
1153
+ }
1154
+ function coerceInsertBooleanArgs(sql, args) {
1155
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
1156
+ if (!match) return;
1157
+ const rawTable = match[1];
1158
+ const rawColumns = match[2];
1159
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
1160
+ if (!boolColumns?.size) return;
1161
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
1162
+ for (const [index, column] of columns.entries()) {
1163
+ if (boolColumns.has(column) && index < args.length) {
1164
+ args[index] = toBoolean(args[index]);
1165
+ }
1166
+ }
1167
+ }
1168
+ function coerceUpdateBooleanArgs(sql, args) {
1169
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
1170
+ if (!match) return;
1171
+ const rawTable = match[1];
1172
+ const setClause = match[2];
1173
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
1174
+ if (!boolColumns?.size) return;
1175
+ const assignments = setClause.split(",");
1176
+ let placeholderIndex = 0;
1177
+ for (const assignment of assignments) {
1178
+ if (!assignment.includes("?")) continue;
1179
+ placeholderIndex += 1;
1180
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
1181
+ if (colMatch && boolColumns.has(colMatch[1])) {
1182
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
1183
+ }
1184
+ }
1185
+ }
1186
+ function coerceBooleanArgs(sql, args) {
1187
+ const nextArgs = [...args];
1188
+ coerceInsertBooleanArgs(sql, nextArgs);
1189
+ coerceUpdateBooleanArgs(sql, nextArgs);
1190
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
1191
+ for (const index of placeholderIndexes) {
1192
+ if (index > 0 && index <= nextArgs.length) {
1193
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
1194
+ }
1195
+ }
1196
+ return nextArgs;
1197
+ }
1198
+ function convertQuestionMarksToDollarParams(sql) {
1199
+ let out = "";
1200
+ let placeholder = 0;
1201
+ let inSingle = false;
1202
+ let inDouble = false;
1203
+ let inLineComment = false;
1204
+ let inBlockComment = false;
1205
+ for (let i = 0; i < sql.length; i++) {
1206
+ const ch = sql[i];
1207
+ const next = sql[i + 1];
1208
+ if (inLineComment) {
1209
+ out += ch;
1210
+ if (ch === "\n") inLineComment = false;
1211
+ continue;
1212
+ }
1213
+ if (inBlockComment) {
1214
+ out += ch;
1215
+ if (ch === "*" && next === "/") {
1216
+ out += next;
1217
+ inBlockComment = false;
1218
+ i += 1;
1219
+ }
1220
+ continue;
1221
+ }
1222
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1223
+ out += ch + next;
1224
+ inLineComment = true;
1225
+ i += 1;
1226
+ continue;
1227
+ }
1228
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1229
+ out += ch + next;
1230
+ inBlockComment = true;
1231
+ i += 1;
1232
+ continue;
1233
+ }
1234
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1235
+ inSingle = !inSingle;
1236
+ out += ch;
1237
+ continue;
1238
+ }
1239
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1240
+ inDouble = !inDouble;
1241
+ out += ch;
1242
+ continue;
1243
+ }
1244
+ if (!inSingle && !inDouble && ch === "?") {
1245
+ placeholder += 1;
1246
+ out += `$${placeholder}`;
1247
+ continue;
1248
+ }
1249
+ out += ch;
1250
+ }
1251
+ return out;
1252
+ }
1253
+ function translateStatementForPostgres(stmt) {
1254
+ const normalized = normalizeStatement(stmt);
1255
+ if (normalized.kind === "named") {
1256
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
1257
+ }
1258
+ const rewrittenSql = rewriteSql(normalized.sql);
1259
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
1260
+ return {
1261
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
1262
+ args: coercedArgs
1263
+ };
1264
+ }
1265
+ function shouldBypassPostgres(stmt) {
1266
+ const normalized = normalizeStatement(stmt);
1267
+ if (normalized.kind === "named") {
1268
+ return true;
1269
+ }
1270
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
1271
+ }
1272
+ function shouldFallbackOnError(error) {
1273
+ const message = error instanceof Error ? error.message : String(error);
1274
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
1275
+ }
1276
+ function isReadQuery(sql) {
1277
+ const trimmed = sql.trimStart();
1278
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
1279
+ }
1280
+ function buildRow(row, columns) {
1281
+ const values = columns.map((column) => row[column]);
1282
+ return Object.assign(values, row);
1283
+ }
1284
+ function buildResultSet(rows, rowsAffected = 0) {
1285
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
1286
+ const resultRows = rows.map((row) => buildRow(row, columns));
1287
+ return {
1288
+ columns,
1289
+ columnTypes: columns.map(() => ""),
1290
+ rows: resultRows,
1291
+ rowsAffected,
1292
+ lastInsertRowid: void 0,
1293
+ toJSON() {
1294
+ return {
1295
+ columns,
1296
+ columnTypes: columns.map(() => ""),
1297
+ rows,
1298
+ rowsAffected,
1299
+ lastInsertRowid: void 0
1300
+ };
1301
+ }
1302
+ };
1303
+ }
1304
+ async function loadPrismaClient() {
1305
+ if (!prismaClientPromise) {
1306
+ prismaClientPromise = (async () => {
1307
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
1308
+ if (explicitPath) {
1309
+ const module2 = await import(pathToFileURL(explicitPath).href);
1310
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
1311
+ if (!PrismaClient2) {
1312
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
1313
+ }
1314
+ return new PrismaClient2();
1315
+ }
1316
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
1317
+ const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
1318
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
1319
+ const module = await import(pathToFileURL(prismaEntry).href);
1320
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
1321
+ if (!PrismaClient) {
1322
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
1323
+ }
1324
+ return new PrismaClient();
1325
+ })();
1326
+ }
1327
+ return prismaClientPromise;
1328
+ }
1329
+ async function ensureCompatibilityViews(prisma) {
1330
+ if (!compatibilityBootstrapPromise) {
1331
+ compatibilityBootstrapPromise = (async () => {
1332
+ for (const mapping of VIEW_MAPPINGS) {
1333
+ const relation = mapping.source.replace(/"/g, "");
1334
+ const rows = await prisma.$queryRawUnsafe(
1335
+ "SELECT to_regclass($1) AS regclass",
1336
+ relation
1337
+ );
1338
+ if (!rows[0]?.regclass) {
1339
+ continue;
1340
+ }
1341
+ await prisma.$executeRawUnsafe(
1342
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
1343
+ );
1344
+ }
1345
+ })();
1346
+ }
1347
+ return compatibilityBootstrapPromise;
1348
+ }
1349
+ async function executeOnPrisma(executor, stmt) {
1350
+ const translated = translateStatementForPostgres(stmt);
1351
+ if (isReadQuery(translated.sql)) {
1352
+ const rows = await executor.$queryRawUnsafe(
1353
+ translated.sql,
1354
+ ...translated.args
1355
+ );
1356
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
1357
+ }
1358
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
1359
+ return buildResultSet([], rowsAffected);
1360
+ }
1361
+ function splitSqlStatements(sql) {
1362
+ const parts = [];
1363
+ let current = "";
1364
+ let inSingle = false;
1365
+ let inDouble = false;
1366
+ let inLineComment = false;
1367
+ let inBlockComment = false;
1368
+ for (let i = 0; i < sql.length; i++) {
1369
+ const ch = sql[i];
1370
+ const next = sql[i + 1];
1371
+ if (inLineComment) {
1372
+ current += ch;
1373
+ if (ch === "\n") inLineComment = false;
1374
+ continue;
1375
+ }
1376
+ if (inBlockComment) {
1377
+ current += ch;
1378
+ if (ch === "*" && next === "/") {
1379
+ current += next;
1380
+ inBlockComment = false;
1381
+ i += 1;
1382
+ }
1383
+ continue;
1384
+ }
1385
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1386
+ current += ch + next;
1387
+ inLineComment = true;
1388
+ i += 1;
1389
+ continue;
1390
+ }
1391
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1392
+ current += ch + next;
1393
+ inBlockComment = true;
1394
+ i += 1;
1395
+ continue;
1396
+ }
1397
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1398
+ inSingle = !inSingle;
1399
+ current += ch;
1400
+ continue;
1401
+ }
1402
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1403
+ inDouble = !inDouble;
1404
+ current += ch;
1405
+ continue;
1406
+ }
1407
+ if (!inSingle && !inDouble && ch === ";") {
1408
+ if (current.trim()) {
1409
+ parts.push(current.trim());
1410
+ }
1411
+ current = "";
1412
+ continue;
1413
+ }
1414
+ current += ch;
1415
+ }
1416
+ if (current.trim()) {
1417
+ parts.push(current.trim());
1418
+ }
1419
+ return parts;
1420
+ }
1421
+ async function createPrismaDbAdapter(fallbackClient) {
1422
+ const prisma = await loadPrismaClient();
1423
+ await ensureCompatibilityViews(prisma);
1424
+ let closed = false;
1425
+ let adapter;
1426
+ const fallbackExecute = async (stmt, error) => {
1427
+ if (!fallbackClient) {
1428
+ if (error) throw error;
1429
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
1430
+ }
1431
+ if (error) {
1432
+ process.stderr.write(
1433
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
1434
+ `
1435
+ );
1436
+ }
1437
+ return fallbackClient.execute(stmt);
1438
+ };
1439
+ adapter = {
1440
+ async execute(stmt) {
1441
+ if (shouldBypassPostgres(stmt)) {
1442
+ return fallbackExecute(stmt);
1443
+ }
1444
+ try {
1445
+ return await executeOnPrisma(prisma, stmt);
1446
+ } catch (error) {
1447
+ if (shouldFallbackOnError(error)) {
1448
+ return fallbackExecute(stmt, error);
1449
+ }
1450
+ throw error;
1451
+ }
1452
+ },
1453
+ async batch(stmts, mode) {
1454
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
1455
+ if (!fallbackClient) {
1456
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
1457
+ }
1458
+ return fallbackClient.batch(stmts, mode);
1459
+ }
1460
+ try {
1461
+ if (prisma.$transaction) {
1462
+ return await prisma.$transaction(async (tx) => {
1463
+ const results2 = [];
1464
+ for (const stmt of stmts) {
1465
+ results2.push(await executeOnPrisma(tx, stmt));
1466
+ }
1467
+ return results2;
1468
+ });
1469
+ }
1470
+ const results = [];
1471
+ for (const stmt of stmts) {
1472
+ results.push(await executeOnPrisma(prisma, stmt));
1473
+ }
1474
+ return results;
1475
+ } catch (error) {
1476
+ if (fallbackClient && shouldFallbackOnError(error)) {
1477
+ process.stderr.write(
1478
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
1479
+ `
1480
+ );
1481
+ return fallbackClient.batch(stmts, mode);
1482
+ }
1483
+ throw error;
1484
+ }
1485
+ },
1486
+ async migrate(stmts) {
1487
+ if (fallbackClient) {
1488
+ return fallbackClient.migrate(stmts);
1489
+ }
1490
+ return adapter.batch(stmts, "deferred");
1491
+ },
1492
+ async transaction(mode) {
1493
+ if (!fallbackClient) {
1494
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
1495
+ }
1496
+ return fallbackClient.transaction(mode);
1497
+ },
1498
+ async executeMultiple(sql) {
1499
+ if (fallbackClient && shouldBypassPostgres(sql)) {
1500
+ return fallbackClient.executeMultiple(sql);
1501
+ }
1502
+ for (const statement of splitSqlStatements(sql)) {
1503
+ await adapter.execute(statement);
1504
+ }
1505
+ },
1506
+ async sync() {
1507
+ if (fallbackClient) {
1508
+ return fallbackClient.sync();
1509
+ }
1510
+ return { frame_no: 0, frames_synced: 0 };
1511
+ },
1512
+ close() {
1513
+ closed = true;
1514
+ prismaClientPromise = null;
1515
+ compatibilityBootstrapPromise = null;
1516
+ void prisma.$disconnect?.();
1517
+ },
1518
+ get closed() {
1519
+ return closed;
1520
+ },
1521
+ get protocol() {
1522
+ return "prisma-postgres";
1523
+ }
1524
+ };
1525
+ return adapter;
1526
+ }
1527
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
1528
+ var init_database_adapter = __esm({
1529
+ "src/lib/database-adapter.ts"() {
1530
+ "use strict";
1531
+ VIEW_MAPPINGS = [
1532
+ { view: "memories", source: "memory.memory_records" },
1533
+ { view: "tasks", source: "memory.tasks" },
1534
+ { view: "behaviors", source: "memory.behaviors" },
1535
+ { view: "entities", source: "memory.entities" },
1536
+ { view: "relationships", source: "memory.relationships" },
1537
+ { view: "entity_memories", source: "memory.entity_memories" },
1538
+ { view: "entity_aliases", source: "memory.entity_aliases" },
1539
+ { view: "notifications", source: "memory.notifications" },
1540
+ { view: "messages", source: "memory.messages" },
1541
+ { view: "users", source: "wiki.users" },
1542
+ { view: "workspaces", source: "wiki.workspaces" },
1543
+ { view: "workspace_users", source: "wiki.workspace_users" },
1544
+ { view: "documents", source: "wiki.workspace_documents" },
1545
+ { view: "chats", source: "wiki.workspace_chats" }
1546
+ ];
1547
+ UPSERT_KEYS = {
1548
+ memories: ["id"],
1549
+ tasks: ["id"],
1550
+ behaviors: ["id"],
1551
+ entities: ["id"],
1552
+ relationships: ["id"],
1553
+ entity_aliases: ["alias"],
1554
+ notifications: ["id"],
1555
+ messages: ["id"],
1556
+ users: ["id"],
1557
+ workspaces: ["id"],
1558
+ workspace_users: ["id"],
1559
+ documents: ["id"],
1560
+ chats: ["id"]
1561
+ };
1562
+ BOOLEAN_COLUMNS_BY_TABLE = {
1563
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
1564
+ behaviors: /* @__PURE__ */ new Set(["active"]),
1565
+ notifications: /* @__PURE__ */ new Set(["read"]),
1566
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
1567
+ };
1568
+ BOOLEAN_COLUMN_NAMES = new Set(
1569
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
1570
+ );
1571
+ IMMEDIATE_FALLBACK_PATTERNS = [
1572
+ /\bPRAGMA\b/i,
1573
+ /\bsqlite_master\b/i,
1574
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
1575
+ /\bMATCH\b/i,
1576
+ /\bvector_distance_cos\s*\(/i,
1577
+ /\bjson_extract\s*\(/i,
1578
+ /\bjulianday\s*\(/i,
1579
+ /\bstrftime\s*\(/i,
1580
+ /\blast_insert_rowid\s*\(/i
1581
+ ];
1582
+ prismaClientPromise = null;
1583
+ compatibilityBootstrapPromise = null;
839
1584
  }
840
1585
  });
841
1586
 
842
1587
  // src/lib/database.ts
843
1588
  import { createClient } from "@libsql/client";
844
1589
  async function initDatabase(config2) {
1590
+ if (_walCheckpointTimer) {
1591
+ clearInterval(_walCheckpointTimer);
1592
+ _walCheckpointTimer = null;
1593
+ }
1594
+ if (_daemonClient) {
1595
+ _daemonClient.close();
1596
+ _daemonClient = null;
1597
+ }
1598
+ if (_adapterClient && _adapterClient !== _resilientClient) {
1599
+ _adapterClient.close();
1600
+ }
1601
+ _adapterClient = null;
845
1602
  if (_client) {
846
1603
  _client.close();
847
1604
  _client = null;
@@ -855,6 +1612,7 @@ async function initDatabase(config2) {
855
1612
  }
856
1613
  _client = createClient(opts);
857
1614
  _resilientClient = wrapWithRetry(_client);
1615
+ _adapterClient = _resilientClient;
858
1616
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
859
1617
  });
860
1618
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -865,11 +1623,17 @@ async function initDatabase(config2) {
865
1623
  });
866
1624
  }, 3e4);
867
1625
  _walCheckpointTimer.unref();
1626
+ if (process.env.DATABASE_URL) {
1627
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
1628
+ }
868
1629
  }
869
1630
  function getClient() {
870
- if (!_resilientClient) {
1631
+ if (!_adapterClient) {
871
1632
  throw new Error("Database client not initialized. Call initDatabase() first.");
872
1633
  }
1634
+ if (process.env.DATABASE_URL) {
1635
+ return _adapterClient;
1636
+ }
873
1637
  if (process.env.EXE_IS_DAEMON === "1") {
874
1638
  return _resilientClient;
875
1639
  }
@@ -1810,26 +2574,36 @@ async function ensureSchema() {
1810
2574
  }
1811
2575
  }
1812
2576
  async function disposeDatabase() {
2577
+ if (_walCheckpointTimer) {
2578
+ clearInterval(_walCheckpointTimer);
2579
+ _walCheckpointTimer = null;
2580
+ }
1813
2581
  if (_daemonClient) {
1814
2582
  _daemonClient.close();
1815
2583
  _daemonClient = null;
1816
2584
  }
2585
+ if (_adapterClient && _adapterClient !== _resilientClient) {
2586
+ _adapterClient.close();
2587
+ }
2588
+ _adapterClient = null;
1817
2589
  if (_client) {
1818
2590
  _client.close();
1819
2591
  _client = null;
1820
2592
  _resilientClient = null;
1821
2593
  }
1822
2594
  }
1823
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
2595
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
1824
2596
  var init_database = __esm({
1825
2597
  "src/lib/database.ts"() {
1826
2598
  "use strict";
1827
2599
  init_db_retry();
1828
2600
  init_employees();
2601
+ init_database_adapter();
1829
2602
  _client = null;
1830
2603
  _resilientClient = null;
1831
2604
  _walCheckpointTimer = null;
1832
2605
  _daemonClient = null;
2606
+ _adapterClient = null;
1833
2607
  initTurso = initDatabase;
1834
2608
  disposeTurso = disposeDatabase;
1835
2609
  }
@@ -1846,11 +2620,11 @@ var init_memory = __esm({
1846
2620
 
1847
2621
  // src/lib/exe-daemon-client.ts
1848
2622
  import net from "net";
1849
- import os3 from "os";
2623
+ import os4 from "os";
1850
2624
  import { spawn } from "child_process";
1851
2625
  import { randomUUID } from "crypto";
1852
- import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
1853
- import path3 from "path";
2626
+ import { existsSync as existsSync4, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
2627
+ import path5 from "path";
1854
2628
  import { fileURLToPath } from "url";
1855
2629
  function handleData(chunk) {
1856
2630
  _buffer += chunk.toString();
@@ -1878,9 +2652,9 @@ function handleData(chunk) {
1878
2652
  }
1879
2653
  }
1880
2654
  function cleanupStaleFiles() {
1881
- if (existsSync3(PID_PATH)) {
2655
+ if (existsSync4(PID_PATH)) {
1882
2656
  try {
1883
- const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
2657
+ const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1884
2658
  if (pid > 0) {
1885
2659
  try {
1886
2660
  process.kill(pid, 0);
@@ -1901,17 +2675,17 @@ function cleanupStaleFiles() {
1901
2675
  }
1902
2676
  }
1903
2677
  function findPackageRoot() {
1904
- let dir = path3.dirname(fileURLToPath(import.meta.url));
1905
- const { root } = path3.parse(dir);
2678
+ let dir = path5.dirname(fileURLToPath(import.meta.url));
2679
+ const { root } = path5.parse(dir);
1906
2680
  while (dir !== root) {
1907
- if (existsSync3(path3.join(dir, "package.json"))) return dir;
1908
- dir = path3.dirname(dir);
2681
+ if (existsSync4(path5.join(dir, "package.json"))) return dir;
2682
+ dir = path5.dirname(dir);
1909
2683
  }
1910
2684
  return null;
1911
2685
  }
1912
2686
  function spawnDaemon() {
1913
- const freeGB = os3.freemem() / (1024 * 1024 * 1024);
1914
- const totalGB = os3.totalmem() / (1024 * 1024 * 1024);
2687
+ const freeGB = os4.freemem() / (1024 * 1024 * 1024);
2688
+ const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1915
2689
  if (totalGB <= 8) {
1916
2690
  process.stderr.write(
1917
2691
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
@@ -1931,8 +2705,8 @@ function spawnDaemon() {
1931
2705
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
1932
2706
  return;
1933
2707
  }
1934
- const daemonPath = path3.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1935
- if (!existsSync3(daemonPath)) {
2708
+ const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
2709
+ if (!existsSync4(daemonPath)) {
1936
2710
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1937
2711
  `);
1938
2712
  return;
@@ -1940,7 +2714,7 @@ function spawnDaemon() {
1940
2714
  const resolvedPath = daemonPath;
1941
2715
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1942
2716
  `);
1943
- const logPath = path3.join(path3.dirname(SOCKET_PATH), "exed.log");
2717
+ const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
1944
2718
  let stderrFd = "ignore";
1945
2719
  try {
1946
2720
  stderrFd = openSync(logPath, "a");
@@ -2091,74 +2865,123 @@ async function pingDaemon() {
2091
2865
  return null;
2092
2866
  }
2093
2867
  function killAndRespawnDaemon() {
2094
- process.stderr.write("[exed-client] Killing daemon for restart...\n");
2095
- if (existsSync3(PID_PATH)) {
2096
- try {
2097
- const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
2098
- if (pid > 0) {
2099
- try {
2100
- process.kill(pid, "SIGKILL");
2101
- } catch {
2868
+ if (!acquireSpawnLock()) {
2869
+ process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
2870
+ if (_socket) {
2871
+ _socket.destroy();
2872
+ _socket = null;
2873
+ }
2874
+ _connected = false;
2875
+ _buffer = "";
2876
+ return;
2877
+ }
2878
+ try {
2879
+ process.stderr.write("[exed-client] Killing daemon for restart...\n");
2880
+ if (existsSync4(PID_PATH)) {
2881
+ try {
2882
+ const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
2883
+ if (pid > 0) {
2884
+ try {
2885
+ process.kill(pid, "SIGKILL");
2886
+ } catch {
2887
+ }
2102
2888
  }
2889
+ } catch {
2103
2890
  }
2891
+ }
2892
+ if (_socket) {
2893
+ _socket.destroy();
2894
+ _socket = null;
2895
+ }
2896
+ _connected = false;
2897
+ _buffer = "";
2898
+ try {
2899
+ unlinkSync2(PID_PATH);
2104
2900
  } catch {
2105
2901
  }
2902
+ try {
2903
+ unlinkSync2(SOCKET_PATH);
2904
+ } catch {
2905
+ }
2906
+ spawnDaemon();
2907
+ } finally {
2908
+ releaseSpawnLock();
2106
2909
  }
2107
- if (_socket) {
2108
- _socket.destroy();
2109
- _socket = null;
2110
- }
2111
- _connected = false;
2112
- _buffer = "";
2910
+ }
2911
+ function isDaemonTooYoung() {
2113
2912
  try {
2114
- unlinkSync2(PID_PATH);
2913
+ const stat = statSync(PID_PATH);
2914
+ return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
2115
2915
  } catch {
2916
+ return false;
2116
2917
  }
2117
- try {
2118
- unlinkSync2(SOCKET_PATH);
2119
- } catch {
2918
+ }
2919
+ async function retryThenRestart(doRequest, label) {
2920
+ const result = await doRequest();
2921
+ if (!result.error) {
2922
+ _consecutiveFailures = 0;
2923
+ return result;
2924
+ }
2925
+ _consecutiveFailures++;
2926
+ for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
2927
+ const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
2928
+ process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
2929
+ `);
2930
+ await new Promise((r) => setTimeout(r, delayMs));
2931
+ if (!_connected) {
2932
+ if (!await connectToSocket()) continue;
2933
+ }
2934
+ const retry = await doRequest();
2935
+ if (!retry.error) {
2936
+ _consecutiveFailures = 0;
2937
+ return retry;
2938
+ }
2939
+ _consecutiveFailures++;
2940
+ }
2941
+ if (isDaemonTooYoung()) {
2942
+ process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
2943
+ `);
2944
+ return { error: result.error };
2945
+ }
2946
+ process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
2947
+ `);
2948
+ killAndRespawnDaemon();
2949
+ const start = Date.now();
2950
+ let delay2 = 200;
2951
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
2952
+ await new Promise((r) => setTimeout(r, delay2));
2953
+ if (await connectToSocket()) break;
2954
+ delay2 = Math.min(delay2 * 2, 3e3);
2120
2955
  }
2121
- spawnDaemon();
2956
+ if (!_connected) return { error: "Daemon restart failed" };
2957
+ const final = await doRequest();
2958
+ if (!final.error) _consecutiveFailures = 0;
2959
+ return final;
2122
2960
  }
2123
2961
  async function embedViaClient(text, priority = "high") {
2124
2962
  if (!_connected && !await connectEmbedDaemon()) return null;
2125
2963
  _requestCount++;
2126
2964
  if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
2127
2965
  const health = await pingDaemon();
2128
- if (!health) {
2966
+ if (!health && !isDaemonTooYoung()) {
2129
2967
  process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
2130
2968
  `);
2131
2969
  killAndRespawnDaemon();
2132
2970
  const start = Date.now();
2133
- let delay2 = 200;
2971
+ let d = 200;
2134
2972
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
2135
- await new Promise((r) => setTimeout(r, delay2));
2973
+ await new Promise((r) => setTimeout(r, d));
2136
2974
  if (await connectToSocket()) break;
2137
- delay2 = Math.min(delay2 * 2, 3e3);
2975
+ d = Math.min(d * 2, 3e3);
2138
2976
  }
2139
2977
  if (!_connected) return null;
2140
2978
  }
2141
2979
  }
2142
- const result = await sendRequest([text], priority);
2143
- if (!result.error && result.vectors?.[0]) return result.vectors[0];
2144
- if (result.error) {
2145
- process.stderr.write(`[exed-client] Embed failed (${result.error}) \u2014 attempting restart
2146
- `);
2147
- killAndRespawnDaemon();
2148
- const start = Date.now();
2149
- let delay2 = 200;
2150
- while (Date.now() - start < CONNECT_TIMEOUT_MS) {
2151
- await new Promise((r) => setTimeout(r, delay2));
2152
- if (await connectToSocket()) break;
2153
- delay2 = Math.min(delay2 * 2, 3e3);
2154
- }
2155
- if (!_connected) return null;
2156
- const retry = await sendRequest([text], priority);
2157
- if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
2158
- process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
2159
- `);
2160
- }
2161
- return null;
2980
+ const result = await retryThenRestart(
2981
+ () => sendRequest([text], priority),
2982
+ "Embed"
2983
+ );
2984
+ return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
2162
2985
  }
2163
2986
  function disconnectClient() {
2164
2987
  if (_socket) {
@@ -2173,14 +2996,14 @@ function disconnectClient() {
2173
2996
  entry.resolve({ error: "Client disconnected" });
2174
2997
  }
2175
2998
  }
2176
- 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;
2999
+ 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;
2177
3000
  var init_exe_daemon_client = __esm({
2178
3001
  "src/lib/exe-daemon-client.ts"() {
2179
3002
  "use strict";
2180
3003
  init_config();
2181
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path3.join(EXE_AI_DIR, "exed.sock");
2182
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path3.join(EXE_AI_DIR, "exed.pid");
2183
- SPAWN_LOCK_PATH = path3.join(EXE_AI_DIR, "exed-spawn.lock");
3004
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
3005
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
3006
+ SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
2184
3007
  SPAWN_LOCK_STALE_MS = 3e4;
2185
3008
  CONNECT_TIMEOUT_MS = 15e3;
2186
3009
  REQUEST_TIMEOUT_MS = 3e4;
@@ -2188,7 +3011,11 @@ var init_exe_daemon_client = __esm({
2188
3011
  _connected = false;
2189
3012
  _buffer = "";
2190
3013
  _requestCount = 0;
3014
+ _consecutiveFailures = 0;
2191
3015
  HEALTH_CHECK_INTERVAL = 100;
3016
+ MAX_RETRIES_BEFORE_RESTART = 3;
3017
+ RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
3018
+ MIN_DAEMON_AGE_MS = 3e4;
2192
3019
  _pending = /* @__PURE__ */ new Map();
2193
3020
  MAX_BUFFER = 1e7;
2194
3021
  }
@@ -2232,8 +3059,8 @@ async function embedDirect(text) {
2232
3059
  const llamaCpp = await import("node-llama-cpp");
2233
3060
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
2234
3061
  const { existsSync: existsSync17 } = await import("fs");
2235
- const path21 = await import("path");
2236
- const modelPath = path21.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
3062
+ const path22 = await import("path");
3063
+ const modelPath = path22.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
2237
3064
  if (!existsSync17(modelPath)) {
2238
3065
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
2239
3066
  }
@@ -2264,14 +3091,14 @@ var init_embedder = __esm({
2264
3091
 
2265
3092
  // src/lib/keychain.ts
2266
3093
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
2267
- import { existsSync as existsSync4 } from "fs";
2268
- import path4 from "path";
2269
- import os4 from "os";
3094
+ import { existsSync as existsSync5 } from "fs";
3095
+ import path6 from "path";
3096
+ import os5 from "os";
2270
3097
  function getKeyDir() {
2271
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path4.join(os4.homedir(), ".exe-os");
3098
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
2272
3099
  }
2273
3100
  function getKeyPath() {
2274
- return path4.join(getKeyDir(), "master.key");
3101
+ return path6.join(getKeyDir(), "master.key");
2275
3102
  }
2276
3103
  async function tryKeytar() {
2277
3104
  try {
@@ -2292,9 +3119,9 @@ async function getMasterKey() {
2292
3119
  }
2293
3120
  }
2294
3121
  const keyPath = getKeyPath();
2295
- if (!existsSync4(keyPath)) {
3122
+ if (!existsSync5(keyPath)) {
2296
3123
  process.stderr.write(
2297
- `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
3124
+ `[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2298
3125
  `
2299
3126
  );
2300
3127
  return null;
@@ -2332,13 +3159,13 @@ __export(shard_manager_exports, {
2332
3159
  listShards: () => listShards,
2333
3160
  shardExists: () => shardExists
2334
3161
  });
2335
- import path5 from "path";
2336
- import { existsSync as existsSync5, mkdirSync, readdirSync } from "fs";
3162
+ import path7 from "path";
3163
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync } from "fs";
2337
3164
  import { createClient as createClient2 } from "@libsql/client";
2338
3165
  function initShardManager(encryptionKey) {
2339
3166
  _encryptionKey = encryptionKey;
2340
- if (!existsSync5(SHARDS_DIR)) {
2341
- mkdirSync(SHARDS_DIR, { recursive: true });
3167
+ if (!existsSync6(SHARDS_DIR)) {
3168
+ mkdirSync2(SHARDS_DIR, { recursive: true });
2342
3169
  }
2343
3170
  _shardingEnabled = true;
2344
3171
  }
@@ -2358,7 +3185,7 @@ function getShardClient(projectName) {
2358
3185
  }
2359
3186
  const cached = _shards.get(safeName);
2360
3187
  if (cached) return cached;
2361
- const dbPath = path5.join(SHARDS_DIR, `${safeName}.db`);
3188
+ const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
2362
3189
  const client = createClient2({
2363
3190
  url: `file:${dbPath}`,
2364
3191
  encryptionKey: _encryptionKey
@@ -2368,10 +3195,10 @@ function getShardClient(projectName) {
2368
3195
  }
2369
3196
  function shardExists(projectName) {
2370
3197
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
2371
- return existsSync5(path5.join(SHARDS_DIR, `${safeName}.db`));
3198
+ return existsSync6(path7.join(SHARDS_DIR, `${safeName}.db`));
2372
3199
  }
2373
3200
  function listShards() {
2374
- if (!existsSync5(SHARDS_DIR)) return [];
3201
+ if (!existsSync6(SHARDS_DIR)) return [];
2375
3202
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
2376
3203
  }
2377
3204
  async function ensureShardSchema(client) {
@@ -2445,7 +3272,23 @@ async function ensureShardSchema(client) {
2445
3272
  // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
2446
3273
  "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
2447
3274
  "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
2448
- "ALTER TABLE memories ADD COLUMN trajectory TEXT"
3275
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT",
3276
+ // Metadata enrichment columns (must match database.ts)
3277
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
3278
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
3279
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
3280
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
3281
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
3282
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
3283
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
3284
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
3285
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
3286
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
3287
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
3288
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
3289
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
3290
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
3291
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
2449
3292
  ]) {
2450
3293
  try {
2451
3294
  await client.execute(col);
@@ -2557,7 +3400,7 @@ var init_shard_manager = __esm({
2557
3400
  "src/lib/shard-manager.ts"() {
2558
3401
  "use strict";
2559
3402
  init_config();
2560
- SHARDS_DIR = path5.join(EXE_AI_DIR, "shards");
3403
+ SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
2561
3404
  _shards = /* @__PURE__ */ new Map();
2562
3405
  _encryptionKey = null;
2563
3406
  _shardingEnabled = false;
@@ -3330,8 +4173,8 @@ __export(wiki_client_exports, {
3330
4173
  listDocuments: () => listDocuments,
3331
4174
  listWorkspaces: () => listWorkspaces
3332
4175
  });
3333
- async function wikiFetch(config2, path21, method = "GET", body) {
3334
- const url = `${config2.baseUrl}/api/v1${path21}`;
4176
+ async function wikiFetch(config2, path22, method = "GET", body) {
4177
+ const url = `${config2.baseUrl}/api/v1${path22}`;
3335
4178
  const headers = {
3336
4179
  Authorization: `Bearer ${config2.apiKey}`,
3337
4180
  "Content-Type": "application/json"
@@ -3364,7 +4207,7 @@ async function wikiFetch(config2, path21, method = "GET", body) {
3364
4207
  }
3365
4208
  }
3366
4209
  if (!response.ok) {
3367
- throw new Error(`Wiki API ${method} ${path21}: ${response.status} ${response.statusText}`);
4210
+ throw new Error(`Wiki API ${method} ${path22}: ${response.status} ${response.statusText}`);
3368
4211
  }
3369
4212
  return response.json();
3370
4213
  } finally {
@@ -4393,9 +5236,9 @@ __export(license_exports, {
4393
5236
  stopLicenseRevalidation: () => stopLicenseRevalidation,
4394
5237
  validateLicense: () => validateLicense
4395
5238
  });
4396
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
5239
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
4397
5240
  import { randomUUID as randomUUID3 } from "crypto";
4398
- import path6 from "path";
5241
+ import path8 from "path";
4399
5242
  import { jwtVerify, importSPKI } from "jose";
4400
5243
  async function fetchRetry(url, init) {
4401
5244
  try {
@@ -4406,37 +5249,37 @@ async function fetchRetry(url, init) {
4406
5249
  }
4407
5250
  }
4408
5251
  function loadDeviceId() {
4409
- const deviceJsonPath = path6.join(EXE_AI_DIR, "device.json");
5252
+ const deviceJsonPath = path8.join(EXE_AI_DIR, "device.json");
4410
5253
  try {
4411
- if (existsSync6(deviceJsonPath)) {
4412
- const data = JSON.parse(readFileSync4(deviceJsonPath, "utf8"));
5254
+ if (existsSync7(deviceJsonPath)) {
5255
+ const data = JSON.parse(readFileSync5(deviceJsonPath, "utf8"));
4413
5256
  if (data.deviceId) return data.deviceId;
4414
5257
  }
4415
5258
  } catch {
4416
5259
  }
4417
5260
  try {
4418
- if (existsSync6(DEVICE_ID_PATH)) {
4419
- const id2 = readFileSync4(DEVICE_ID_PATH, "utf8").trim();
5261
+ if (existsSync7(DEVICE_ID_PATH)) {
5262
+ const id2 = readFileSync5(DEVICE_ID_PATH, "utf8").trim();
4420
5263
  if (id2) return id2;
4421
5264
  }
4422
5265
  } catch {
4423
5266
  }
4424
5267
  const id = randomUUID3();
4425
- mkdirSync2(EXE_AI_DIR, { recursive: true });
4426
- writeFileSync2(DEVICE_ID_PATH, id, "utf8");
5268
+ mkdirSync3(EXE_AI_DIR, { recursive: true });
5269
+ writeFileSync3(DEVICE_ID_PATH, id, "utf8");
4427
5270
  return id;
4428
5271
  }
4429
5272
  function loadLicense() {
4430
5273
  try {
4431
- if (!existsSync6(LICENSE_PATH)) return null;
4432
- return readFileSync4(LICENSE_PATH, "utf8").trim();
5274
+ if (!existsSync7(LICENSE_PATH)) return null;
5275
+ return readFileSync5(LICENSE_PATH, "utf8").trim();
4433
5276
  } catch {
4434
5277
  return null;
4435
5278
  }
4436
5279
  }
4437
5280
  function saveLicense(apiKey) {
4438
- mkdirSync2(EXE_AI_DIR, { recursive: true });
4439
- writeFileSync2(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
5281
+ mkdirSync3(EXE_AI_DIR, { recursive: true });
5282
+ writeFileSync3(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
4440
5283
  }
4441
5284
  async function verifyLicenseJwt(token) {
4442
5285
  try {
@@ -4462,8 +5305,8 @@ async function verifyLicenseJwt(token) {
4462
5305
  }
4463
5306
  async function getCachedLicense() {
4464
5307
  try {
4465
- if (!existsSync6(CACHE_PATH)) return null;
4466
- const raw = JSON.parse(readFileSync4(CACHE_PATH, "utf8"));
5308
+ if (!existsSync7(CACHE_PATH)) return null;
5309
+ const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
4467
5310
  if (!raw.token || typeof raw.token !== "string") return null;
4468
5311
  return await verifyLicenseJwt(raw.token);
4469
5312
  } catch {
@@ -4472,8 +5315,8 @@ async function getCachedLicense() {
4472
5315
  }
4473
5316
  function readCachedToken() {
4474
5317
  try {
4475
- if (!existsSync6(CACHE_PATH)) return null;
4476
- const raw = JSON.parse(readFileSync4(CACHE_PATH, "utf8"));
5318
+ if (!existsSync7(CACHE_PATH)) return null;
5319
+ const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
4477
5320
  return typeof raw.token === "string" ? raw.token : null;
4478
5321
  } catch {
4479
5322
  return null;
@@ -4507,7 +5350,7 @@ function getRawCachedPlan() {
4507
5350
  }
4508
5351
  function cacheResponse(token) {
4509
5352
  try {
4510
- writeFileSync2(CACHE_PATH, JSON.stringify({ token }), "utf8");
5353
+ writeFileSync3(CACHE_PATH, JSON.stringify({ token }), "utf8");
4511
5354
  } catch {
4512
5355
  }
4513
5356
  }
@@ -4571,9 +5414,9 @@ async function checkLicense() {
4571
5414
  let key = loadLicense();
4572
5415
  if (!key) {
4573
5416
  try {
4574
- const configPath = path6.join(EXE_AI_DIR, "config.json");
4575
- if (existsSync6(configPath)) {
4576
- const raw = JSON.parse(readFileSync4(configPath, "utf8"));
5417
+ const configPath = path8.join(EXE_AI_DIR, "config.json");
5418
+ if (existsSync7(configPath)) {
5419
+ const raw = JSON.parse(readFileSync5(configPath, "utf8"));
4577
5420
  const cloud = raw.cloud;
4578
5421
  if (cloud?.apiKey) {
4579
5422
  key = cloud.apiKey;
@@ -4732,9 +5575,9 @@ var init_license = __esm({
4732
5575
  "src/lib/license.ts"() {
4733
5576
  "use strict";
4734
5577
  init_config();
4735
- LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
4736
- CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
4737
- DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
5578
+ LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
5579
+ CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
5580
+ DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
4738
5581
  API_BASE = "https://askexe.com/cloud";
4739
5582
  RETRY_DELAY_MS = 500;
4740
5583
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
@@ -4772,7 +5615,7 @@ __export(whatsapp_exports, {
4772
5615
  import { randomUUID as randomUUID4 } from "crypto";
4773
5616
  import { homedir } from "os";
4774
5617
  import { join } from "path";
4775
- import { mkdirSync as mkdirSync3 } from "fs";
5618
+ import { mkdirSync as mkdirSync4 } from "fs";
4776
5619
  function calculateBackoff(retryCount) {
4777
5620
  const base = Math.min(
4778
5621
  INITIAL_BACKOFF_MS * BACKOFF_MULTIPLIER ** retryCount,
@@ -4802,7 +5645,7 @@ var init_whatsapp = __esm({
4802
5645
  disconnectedAt = 0;
4803
5646
  async connect(config2) {
4804
5647
  this.authDir = config2.credentials.authDir ?? AUTH_DIR;
4805
- mkdirSync3(this.authDir, { recursive: true });
5648
+ mkdirSync4(this.authDir, { recursive: true });
4806
5649
  const baileys = await import("@whiskeysockets/baileys");
4807
5650
  const { makeWASocket, useMultiFileAuthState, fetchLatestBaileysVersion, DisconnectReason, makeCacheableSignalKeyStore } = baileys;
4808
5651
  const { state, saveCreds } = await useMultiFileAuthState(this.authDir);
@@ -5632,16 +6475,16 @@ __export(imessage_exports, {
5632
6475
  });
5633
6476
  import { execFile } from "child_process";
5634
6477
  import { promisify } from "util";
5635
- import os5 from "os";
5636
- import path7 from "path";
6478
+ import os6 from "os";
6479
+ import path9 from "path";
5637
6480
  var execFileAsync, POLL_INTERVAL_MS, MESSAGES_DB_PATH, IMessageAdapter;
5638
6481
  var init_imessage = __esm({
5639
6482
  "src/gateway/adapters/imessage.ts"() {
5640
6483
  "use strict";
5641
6484
  execFileAsync = promisify(execFile);
5642
6485
  POLL_INTERVAL_MS = 5e3;
5643
- MESSAGES_DB_PATH = path7.join(
5644
- process.env.HOME ?? os5.homedir(),
6486
+ MESSAGES_DB_PATH = path9.join(
6487
+ process.env.HOME ?? os6.homedir(),
5645
6488
  "Library/Messages/chat.db"
5646
6489
  );
5647
6490
  IMessageAdapter = class {
@@ -5954,9 +6797,9 @@ __export(webhook_exports, {
5954
6797
  WebhookAdapter: () => WebhookAdapter
5955
6798
  });
5956
6799
  import { randomUUID as randomUUID7 } from "crypto";
5957
- function resolvePath(obj, path21) {
6800
+ function resolvePath(obj, path22) {
5958
6801
  let current = obj;
5959
- for (const segment of path21.split(".")) {
6802
+ for (const segment of path22.split(".")) {
5960
6803
  if (current == null || typeof current !== "object") return void 0;
5961
6804
  current = current[segment];
5962
6805
  }
@@ -6059,13 +6902,13 @@ __export(whatsapp_accounts_exports, {
6059
6902
  getDefaultAccount: () => getDefaultAccount,
6060
6903
  loadAccounts: () => loadAccounts
6061
6904
  });
6062
- import { readFileSync as readFileSync5 } from "fs";
6905
+ import { readFileSync as readFileSync6 } from "fs";
6063
6906
  import { join as join2 } from "path";
6064
6907
  import { homedir as homedir2 } from "os";
6065
6908
  function loadAccounts() {
6066
6909
  if (cachedAccounts !== null) return cachedAccounts;
6067
6910
  try {
6068
- const raw = readFileSync5(CONFIG_PATH2, "utf8");
6911
+ const raw = readFileSync6(CONFIG_PATH2, "utf8");
6069
6912
  const parsed = JSON.parse(raw);
6070
6913
  if (!Array.isArray(parsed)) {
6071
6914
  console.warn("[whatsapp] Config is not an array, ignoring");
@@ -6105,13 +6948,13 @@ var init_whatsapp_accounts = __esm({
6105
6948
  });
6106
6949
 
6107
6950
  // src/lib/session-registry.ts
6108
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync7 } from "fs";
6109
- import path8 from "path";
6110
- import os6 from "os";
6951
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync8 } from "fs";
6952
+ import path10 from "path";
6953
+ import os7 from "os";
6111
6954
  function registerSession(entry) {
6112
- const dir = path8.dirname(REGISTRY_PATH);
6113
- if (!existsSync7(dir)) {
6114
- mkdirSync4(dir, { recursive: true });
6955
+ const dir = path10.dirname(REGISTRY_PATH);
6956
+ if (!existsSync8(dir)) {
6957
+ mkdirSync5(dir, { recursive: true });
6115
6958
  }
6116
6959
  const sessions = listSessions();
6117
6960
  const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
@@ -6120,11 +6963,11 @@ function registerSession(entry) {
6120
6963
  } else {
6121
6964
  sessions.push(entry);
6122
6965
  }
6123
- writeFileSync3(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
6966
+ writeFileSync4(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
6124
6967
  }
6125
6968
  function listSessions() {
6126
6969
  try {
6127
- const raw = readFileSync6(REGISTRY_PATH, "utf8");
6970
+ const raw = readFileSync7(REGISTRY_PATH, "utf8");
6128
6971
  return JSON.parse(raw);
6129
6972
  } catch {
6130
6973
  return [];
@@ -6134,7 +6977,7 @@ var REGISTRY_PATH;
6134
6977
  var init_session_registry = __esm({
6135
6978
  "src/lib/session-registry.ts"() {
6136
6979
  "use strict";
6137
- REGISTRY_PATH = path8.join(os6.homedir(), ".exe-os", "session-registry.json");
6980
+ REGISTRY_PATH = path10.join(os7.homedir(), ".exe-os", "session-registry.json");
6138
6981
  }
6139
6982
  });
6140
6983
 
@@ -6386,67 +7229,6 @@ var init_provider_table = __esm({
6386
7229
  }
6387
7230
  });
6388
7231
 
6389
- // src/lib/runtime-table.ts
6390
- var RUNTIME_TABLE, DEFAULT_RUNTIME;
6391
- var init_runtime_table = __esm({
6392
- "src/lib/runtime-table.ts"() {
6393
- "use strict";
6394
- RUNTIME_TABLE = {
6395
- codex: {
6396
- binary: "codex",
6397
- launchMode: "interactive",
6398
- autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
6399
- inlineFlag: "--no-alt-screen",
6400
- apiKeyEnv: "OPENAI_API_KEY",
6401
- defaultModel: "gpt-5.4"
6402
- },
6403
- opencode: {
6404
- binary: "opencode",
6405
- launchMode: "exec",
6406
- autoApproveFlag: "--dangerously-skip-permissions",
6407
- inlineFlag: "",
6408
- apiKeyEnv: "ANTHROPIC_API_KEY",
6409
- defaultModel: "anthropic/claude-sonnet-4-6"
6410
- }
6411
- };
6412
- DEFAULT_RUNTIME = "claude";
6413
- }
6414
- });
6415
-
6416
- // src/lib/agent-config.ts
6417
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
6418
- import path9 from "path";
6419
- function loadAgentConfig() {
6420
- if (!existsSync8(AGENT_CONFIG_PATH)) return {};
6421
- try {
6422
- return JSON.parse(readFileSync7(AGENT_CONFIG_PATH, "utf-8"));
6423
- } catch {
6424
- return {};
6425
- }
6426
- }
6427
- function getAgentRuntime(agentId) {
6428
- const config2 = loadAgentConfig();
6429
- const entry = config2[agentId];
6430
- if (entry) return entry;
6431
- const orgDefault = config2["default"];
6432
- if (orgDefault) return orgDefault;
6433
- return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
6434
- }
6435
- var AGENT_CONFIG_PATH, DEFAULT_MODELS;
6436
- var init_agent_config = __esm({
6437
- "src/lib/agent-config.ts"() {
6438
- "use strict";
6439
- init_config();
6440
- init_runtime_table();
6441
- AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
6442
- DEFAULT_MODELS = {
6443
- claude: "claude-opus-4",
6444
- codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
6445
- opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
6446
- };
6447
- }
6448
- });
6449
-
6450
7232
  // src/lib/intercom-queue.ts
6451
7233
  var intercom_queue_exports = {};
6452
7234
  __export(intercom_queue_exports, {
@@ -6457,10 +7239,10 @@ __export(intercom_queue_exports, {
6457
7239
  readQueue: () => readQueue
6458
7240
  });
6459
7241
  import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync9, mkdirSync as mkdirSync6 } from "fs";
6460
- import path10 from "path";
6461
- import os7 from "os";
7242
+ import path11 from "path";
7243
+ import os8 from "os";
6462
7244
  function ensureDir() {
6463
- const dir = path10.dirname(QUEUE_PATH);
7245
+ const dir = path11.dirname(QUEUE_PATH);
6464
7246
  if (!existsSync9(dir)) mkdirSync6(dir, { recursive: true });
6465
7247
  }
6466
7248
  function readQueue() {
@@ -6566,16 +7348,16 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
6566
7348
  var init_intercom_queue = __esm({
6567
7349
  "src/lib/intercom-queue.ts"() {
6568
7350
  "use strict";
6569
- QUEUE_PATH = path10.join(os7.homedir(), ".exe-os", "intercom-queue.json");
7351
+ QUEUE_PATH = path11.join(os8.homedir(), ".exe-os", "intercom-queue.json");
6570
7352
  MAX_RETRIES2 = 5;
6571
7353
  TTL_MS = 60 * 60 * 1e3;
6572
- INTERCOM_LOG = path10.join(os7.homedir(), ".exe-os", "intercom.log");
7354
+ INTERCOM_LOG = path11.join(os8.homedir(), ".exe-os", "intercom.log");
6573
7355
  }
6574
7356
  });
6575
7357
 
6576
7358
  // src/lib/plan-limits.ts
6577
7359
  import { readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
6578
- import path11 from "path";
7360
+ import path12 from "path";
6579
7361
  function getLicenseSync() {
6580
7362
  try {
6581
7363
  if (!existsSync10(CACHE_PATH2)) return freeLicense();
@@ -6647,14 +7429,14 @@ var init_plan_limits = __esm({
6647
7429
  this.name = "PlanLimitError";
6648
7430
  }
6649
7431
  };
6650
- CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
7432
+ CACHE_PATH2 = path12.join(EXE_AI_DIR, "license-cache.json");
6651
7433
  }
6652
7434
  });
6653
7435
 
6654
7436
  // src/lib/notifications.ts
6655
7437
  import crypto3 from "crypto";
6656
- import path12 from "path";
6657
- import os8 from "os";
7438
+ import path13 from "path";
7439
+ import os9 from "os";
6658
7440
  import {
6659
7441
  readFileSync as readFileSync10,
6660
7442
  readdirSync as readdirSync2,
@@ -6763,8 +7545,8 @@ var init_task_scope = __esm({
6763
7545
 
6764
7546
  // src/lib/tasks-crud.ts
6765
7547
  import crypto5 from "crypto";
6766
- import path13 from "path";
6767
- import os9 from "os";
7548
+ import path14 from "path";
7549
+ import os10 from "os";
6768
7550
  import { execSync as execSync4 } from "child_process";
6769
7551
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
6770
7552
  import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
@@ -6942,8 +7724,8 @@ ${laneWarning}` : laneWarning;
6942
7724
  }
6943
7725
  if (input.baseDir) {
6944
7726
  try {
6945
- await mkdir4(path13.join(input.baseDir, "exe", "output"), { recursive: true });
6946
- await mkdir4(path13.join(input.baseDir, "exe", "research"), { recursive: true });
7727
+ await mkdir4(path14.join(input.baseDir, "exe", "output"), { recursive: true });
7728
+ await mkdir4(path14.join(input.baseDir, "exe", "research"), { recursive: true });
6947
7729
  await ensureArchitectureDoc(input.baseDir, input.projectName);
6948
7730
  await ensureGitignoreExe(input.baseDir);
6949
7731
  } catch {
@@ -6979,9 +7761,9 @@ ${laneWarning}` : laneWarning;
6979
7761
  });
6980
7762
  if (input.baseDir) {
6981
7763
  try {
6982
- const EXE_OS_DIR = path13.join(os9.homedir(), ".exe-os");
6983
- const mdPath = path13.join(EXE_OS_DIR, taskFile);
6984
- const mdDir = path13.dirname(mdPath);
7764
+ const EXE_OS_DIR = path14.join(os10.homedir(), ".exe-os");
7765
+ const mdPath = path14.join(EXE_OS_DIR, taskFile);
7766
+ const mdDir = path14.dirname(mdPath);
6985
7767
  if (!existsSync12(mdDir)) await mkdir4(mdDir, { recursive: true });
6986
7768
  const reviewer = input.reviewer ?? input.assignedBy;
6987
7769
  const mdContent = `# ${input.title}
@@ -7282,7 +8064,7 @@ async function deleteTaskCore(taskId, _baseDir) {
7282
8064
  return { taskFile, assignedTo, assignedBy, taskSlug };
7283
8065
  }
7284
8066
  async function ensureArchitectureDoc(baseDir, projectName) {
7285
- const archPath = path13.join(baseDir, "exe", "ARCHITECTURE.md");
8067
+ const archPath = path14.join(baseDir, "exe", "ARCHITECTURE.md");
7286
8068
  try {
7287
8069
  if (existsSync12(archPath)) return;
7288
8070
  const template = [
@@ -7317,7 +8099,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
7317
8099
  }
7318
8100
  }
7319
8101
  async function ensureGitignoreExe(baseDir) {
7320
- const gitignorePath = path13.join(baseDir, ".gitignore");
8102
+ const gitignorePath = path14.join(baseDir, ".gitignore");
7321
8103
  try {
7322
8104
  if (existsSync12(gitignorePath)) {
7323
8105
  const content = readFileSync11(gitignorePath, "utf-8");
@@ -7351,13 +8133,13 @@ var init_tasks_crud = __esm({
7351
8133
  });
7352
8134
 
7353
8135
  // src/lib/tasks-review.ts
7354
- import path14 from "path";
8136
+ import path15 from "path";
7355
8137
  import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
7356
8138
  async function countPendingReviews(sessionScope) {
7357
8139
  const client = getClient();
7358
8140
  if (sessionScope) {
7359
8141
  const result2 = await client.execute({
7360
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND (session_scope = ? OR session_scope IS NULL)",
8142
+ sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
7361
8143
  args: [sessionScope]
7362
8144
  });
7363
8145
  return Number(result2.rows[0]?.cnt) || 0;
@@ -7533,11 +8315,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
7533
8315
  );
7534
8316
  }
7535
8317
  try {
7536
- const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
8318
+ const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
7537
8319
  if (existsSync13(cacheDir)) {
7538
8320
  for (const f of readdirSync3(cacheDir)) {
7539
8321
  if (f.startsWith("review-notified-")) {
7540
- unlinkSync4(path14.join(cacheDir, f));
8322
+ unlinkSync4(path15.join(cacheDir, f));
7541
8323
  }
7542
8324
  }
7543
8325
  }
@@ -7558,7 +8340,7 @@ var init_tasks_review = __esm({
7558
8340
  });
7559
8341
 
7560
8342
  // src/lib/tasks-chain.ts
7561
- import path15 from "path";
8343
+ import path16 from "path";
7562
8344
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
7563
8345
  async function cascadeUnblock(taskId, baseDir, now) {
7564
8346
  const client = getClient();
@@ -7575,7 +8357,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
7575
8357
  });
7576
8358
  for (const ur of unblockedRows.rows) {
7577
8359
  try {
7578
- const ubFile = path15.join(baseDir, String(ur.task_file));
8360
+ const ubFile = path16.join(baseDir, String(ur.task_file));
7579
8361
  let ubContent = await readFile4(ubFile, "utf-8");
7580
8362
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
7581
8363
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -7644,7 +8426,7 @@ var init_tasks_chain = __esm({
7644
8426
 
7645
8427
  // src/lib/project-name.ts
7646
8428
  import { execSync as execSync5 } from "child_process";
7647
- import path16 from "path";
8429
+ import path17 from "path";
7648
8430
  function getProjectName(cwd) {
7649
8431
  const dir = cwd ?? process.cwd();
7650
8432
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -7657,7 +8439,7 @@ function getProjectName(cwd) {
7657
8439
  timeout: 2e3,
7658
8440
  stdio: ["pipe", "pipe", "pipe"]
7659
8441
  }).trim();
7660
- repoRoot = path16.dirname(gitCommonDir);
8442
+ repoRoot = path17.dirname(gitCommonDir);
7661
8443
  } catch {
7662
8444
  repoRoot = execSync5("git rev-parse --show-toplevel", {
7663
8445
  cwd: dir,
@@ -7666,11 +8448,11 @@ function getProjectName(cwd) {
7666
8448
  stdio: ["pipe", "pipe", "pipe"]
7667
8449
  }).trim();
7668
8450
  }
7669
- _cached2 = path16.basename(repoRoot);
8451
+ _cached2 = path17.basename(repoRoot);
7670
8452
  _cachedCwd = dir;
7671
8453
  return _cached2;
7672
8454
  } catch {
7673
- _cached2 = path16.basename(dir);
8455
+ _cached2 = path17.basename(dir);
7674
8456
  _cachedCwd = dir;
7675
8457
  return _cached2;
7676
8458
  }
@@ -8143,7 +8925,7 @@ __export(tasks_exports, {
8143
8925
  updateTaskStatus: () => updateTaskStatus,
8144
8926
  writeCheckpoint: () => writeCheckpoint
8145
8927
  });
8146
- import path17 from "path";
8928
+ import path18 from "path";
8147
8929
  import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, unlinkSync as unlinkSync5 } from "fs";
8148
8930
  async function createTask(input) {
8149
8931
  const result = await createTaskCore(input);
@@ -8163,8 +8945,8 @@ async function updateTask(input) {
8163
8945
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
8164
8946
  try {
8165
8947
  const agent = String(row.assigned_to);
8166
- const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
8167
- const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
8948
+ const cacheDir = path18.join(EXE_AI_DIR, "session-cache");
8949
+ const cachePath = path18.join(cacheDir, `current-task-${agent}.json`);
8168
8950
  if (input.status === "in_progress") {
8169
8951
  mkdirSync7(cacheDir, { recursive: true });
8170
8952
  writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
@@ -8635,12 +9417,12 @@ __export(tmux_routing_exports, {
8635
9417
  });
8636
9418
  import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
8637
9419
  import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync4 } from "fs";
8638
- import path18 from "path";
8639
- import os10 from "os";
9420
+ import path19 from "path";
9421
+ import os11 from "os";
8640
9422
  import { fileURLToPath as fileURLToPath2 } from "url";
8641
9423
  import { unlinkSync as unlinkSync6 } from "fs";
8642
9424
  function spawnLockPath(sessionName) {
8643
- return path18.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
9425
+ return path19.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
8644
9426
  }
8645
9427
  function isProcessAlive(pid) {
8646
9428
  try {
@@ -8677,8 +9459,8 @@ function releaseSpawnLock2(sessionName) {
8677
9459
  function resolveBehaviorsExporterScript() {
8678
9460
  try {
8679
9461
  const thisFile = fileURLToPath2(import.meta.url);
8680
- const scriptPath = path18.join(
8681
- path18.dirname(thisFile),
9462
+ const scriptPath = path19.join(
9463
+ path19.dirname(thisFile),
8682
9464
  "..",
8683
9465
  "bin",
8684
9466
  "exe-export-behaviors.js"
@@ -8753,7 +9535,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
8753
9535
  mkdirSync8(SESSION_CACHE, { recursive: true });
8754
9536
  }
8755
9537
  const rootExe = extractRootExe(parentExe) ?? parentExe;
8756
- const filePath = path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
9538
+ const filePath = path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
8757
9539
  writeFileSync7(filePath, JSON.stringify({
8758
9540
  parentExe: rootExe,
8759
9541
  dispatchedBy: dispatchedBy || rootExe,
@@ -8762,7 +9544,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
8762
9544
  }
8763
9545
  function getParentExe(sessionKey) {
8764
9546
  try {
8765
- const data = JSON.parse(readFileSync12(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
9547
+ const data = JSON.parse(readFileSync12(path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
8766
9548
  return data.parentExe || null;
8767
9549
  } catch {
8768
9550
  return null;
@@ -8771,7 +9553,7 @@ function getParentExe(sessionKey) {
8771
9553
  function getDispatchedBy(sessionKey) {
8772
9554
  try {
8773
9555
  const data = JSON.parse(readFileSync12(
8774
- path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
9556
+ path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
8775
9557
  "utf8"
8776
9558
  ));
8777
9559
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -8957,7 +9739,7 @@ function sendIntercom(targetSession) {
8957
9739
  try {
8958
9740
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
8959
9741
  const agent = baseAgentName(rawAgent);
8960
- const markerPath = path18.join(SESSION_CACHE, `current-task-${agent}.json`);
9742
+ const markerPath = path19.join(SESSION_CACHE, `current-task-${agent}.json`);
8961
9743
  if (existsSync14(markerPath)) {
8962
9744
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
8963
9745
  return "debounced";
@@ -8967,7 +9749,7 @@ function sendIntercom(targetSession) {
8967
9749
  try {
8968
9750
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
8969
9751
  const agent = baseAgentName(rawAgent);
8970
- const taskDir = path18.join(process.cwd(), "exe", agent);
9752
+ const taskDir = path19.join(process.cwd(), "exe", agent);
8971
9753
  if (existsSync14(taskDir)) {
8972
9754
  const files = readdirSync4(taskDir).filter(
8973
9755
  (f) => f.endsWith(".md") && f !== "DONE.txt"
@@ -9101,8 +9883,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
9101
9883
  const transport = getTransport();
9102
9884
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
9103
9885
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
9104
- const logDir = path18.join(os10.homedir(), ".exe-os", "session-logs");
9105
- const logFile = path18.join(logDir, `${instanceLabel}-${Date.now()}.log`);
9886
+ const logDir = path19.join(os11.homedir(), ".exe-os", "session-logs");
9887
+ const logFile = path19.join(logDir, `${instanceLabel}-${Date.now()}.log`);
9106
9888
  if (!existsSync14(logDir)) {
9107
9889
  mkdirSync8(logDir, { recursive: true });
9108
9890
  }
@@ -9110,14 +9892,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
9110
9892
  let cleanupSuffix = "";
9111
9893
  try {
9112
9894
  const thisFile = fileURLToPath2(import.meta.url);
9113
- const cleanupScript = path18.join(path18.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
9895
+ const cleanupScript = path19.join(path19.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
9114
9896
  if (existsSync14(cleanupScript)) {
9115
9897
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
9116
9898
  }
9117
9899
  } catch {
9118
9900
  }
9119
9901
  try {
9120
- const claudeJsonPath = path18.join(os10.homedir(), ".claude.json");
9902
+ const claudeJsonPath = path19.join(os11.homedir(), ".claude.json");
9121
9903
  let claudeJson = {};
9122
9904
  try {
9123
9905
  claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
@@ -9132,10 +9914,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
9132
9914
  } catch {
9133
9915
  }
9134
9916
  try {
9135
- const settingsDir = path18.join(os10.homedir(), ".claude", "projects");
9917
+ const settingsDir = path19.join(os11.homedir(), ".claude", "projects");
9136
9918
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
9137
- const projSettingsDir = path18.join(settingsDir, normalizedKey);
9138
- const settingsPath = path18.join(projSettingsDir, "settings.json");
9919
+ const projSettingsDir = path19.join(settingsDir, normalizedKey);
9920
+ const settingsPath = path19.join(projSettingsDir, "settings.json");
9139
9921
  let settings = {};
9140
9922
  try {
9141
9923
  settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
@@ -9182,8 +9964,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
9182
9964
  let behaviorsFlag = "";
9183
9965
  let legacyFallbackWarned = false;
9184
9966
  if (!useExeAgent && !useBinSymlink) {
9185
- const identityPath = path18.join(
9186
- os10.homedir(),
9967
+ const identityPath = path19.join(
9968
+ os11.homedir(),
9187
9969
  ".exe-os",
9188
9970
  "identity",
9189
9971
  `${employeeName}.md`
@@ -9198,7 +9980,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
9198
9980
  }
9199
9981
  const behaviorsFile = exportBehaviorsSync(
9200
9982
  employeeName,
9201
- path18.basename(spawnCwd),
9983
+ path19.basename(spawnCwd),
9202
9984
  sessionName
9203
9985
  );
9204
9986
  if (behaviorsFile) {
@@ -9213,9 +9995,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
9213
9995
  }
9214
9996
  let sessionContextFlag = "";
9215
9997
  try {
9216
- const ctxDir = path18.join(os10.homedir(), ".exe-os", "session-cache");
9998
+ const ctxDir = path19.join(os11.homedir(), ".exe-os", "session-cache");
9217
9999
  mkdirSync8(ctxDir, { recursive: true });
9218
- const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
10000
+ const ctxFile = path19.join(ctxDir, `session-context-${sessionName}.md`);
9219
10001
  const ctxContent = [
9220
10002
  `## Session Context`,
9221
10003
  `You are running in tmux session: ${sessionName}.`,
@@ -9299,7 +10081,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
9299
10081
  transport.pipeLog(sessionName, logFile);
9300
10082
  try {
9301
10083
  const mySession = getMySession();
9302
- const dispatchInfo = path18.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
10084
+ const dispatchInfo = path19.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
9303
10085
  writeFileSync7(dispatchInfo, JSON.stringify({
9304
10086
  dispatchedBy: mySession,
9305
10087
  rootExe: exeSession,
@@ -9374,15 +10156,15 @@ var init_tmux_routing = __esm({
9374
10156
  init_intercom_queue();
9375
10157
  init_plan_limits();
9376
10158
  init_employees();
9377
- SPAWN_LOCK_DIR = path18.join(os10.homedir(), ".exe-os", "spawn-locks");
9378
- SESSION_CACHE = path18.join(os10.homedir(), ".exe-os", "session-cache");
10159
+ SPAWN_LOCK_DIR = path19.join(os11.homedir(), ".exe-os", "spawn-locks");
10160
+ SESSION_CACHE = path19.join(os11.homedir(), ".exe-os", "session-cache");
9379
10161
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
9380
10162
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
9381
10163
  VERIFY_PANE_LINES = 200;
9382
10164
  INTERCOM_DEBOUNCE_MS = 3e4;
9383
10165
  CODEX_DEBOUNCE_MS = 12e4;
9384
- INTERCOM_LOG2 = path18.join(os10.homedir(), ".exe-os", "intercom.log");
9385
- DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
10166
+ INTERCOM_LOG2 = path19.join(os11.homedir(), ".exe-os", "intercom.log");
10167
+ DEBOUNCE_FILE = path19.join(SESSION_CACHE, "intercom-debounce.json");
9386
10168
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
9387
10169
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
9388
10170
  }
@@ -9642,8 +10424,8 @@ var init_messaging = __esm({
9642
10424
  // src/automation/trigger-engine.ts
9643
10425
  import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, existsSync as existsSync15, mkdirSync as mkdirSync9 } from "fs";
9644
10426
  import { randomUUID as randomUUID8 } from "crypto";
9645
- import path19 from "path";
9646
- import os11 from "os";
10427
+ import path20 from "path";
10428
+ import os12 from "os";
9647
10429
  function substituteTemplate(template, record) {
9648
10430
  return template.replace(
9649
10431
  /\{\{(\w+(?:\.\w+)*)\}\}/g,
@@ -9938,7 +10720,7 @@ var TRIGGERS_PATH, GRAPH_API_VERSION;
9938
10720
  var init_trigger_engine = __esm({
9939
10721
  "src/automation/trigger-engine.ts"() {
9940
10722
  "use strict";
9941
- TRIGGERS_PATH = path19.join(os11.homedir(), ".exe-os", "triggers.json");
10723
+ TRIGGERS_PATH = path20.join(os12.homedir(), ".exe-os", "triggers.json");
9942
10724
  GRAPH_API_VERSION = "v21.0";
9943
10725
  }
9944
10726
  });
@@ -10002,8 +10784,8 @@ var init_crm_webhook = __esm({
10002
10784
 
10003
10785
  // src/bin/exe-gateway.ts
10004
10786
  import { existsSync as existsSync16, readFileSync as readFileSync14 } from "fs";
10005
- import path20 from "path";
10006
- import os12 from "os";
10787
+ import path21 from "path";
10788
+ import os13 from "os";
10007
10789
 
10008
10790
  // src/gateway/webhook-server.ts
10009
10791
  import {
@@ -10969,8 +11751,8 @@ var BotRegistry = class {
10969
11751
 
10970
11752
  // src/bin/exe-gateway.ts
10971
11753
  init_employees();
10972
- var CONFIG_DIR = path20.join(os12.homedir(), ".exe-os");
10973
- var CONFIG_PATH3 = path20.join(CONFIG_DIR, "gateway.json");
11754
+ var CONFIG_DIR = path21.join(os13.homedir(), ".exe-os");
11755
+ var CONFIG_PATH3 = path21.join(CONFIG_DIR, "gateway.json");
10974
11756
  var DEFAULT_PORT = 3100;
10975
11757
  function loadConfig2() {
10976
11758
  if (!existsSync16(CONFIG_PATH3)) {