@askexenow/exe-os 0.9.6 → 0.9.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/dist/bin/backfill-conversations.js +754 -79
  2. package/dist/bin/backfill-responses.js +752 -77
  3. package/dist/bin/backfill-vectors.js +752 -77
  4. package/dist/bin/cleanup-stale-review-tasks.js +668 -37
  5. package/dist/bin/cli.js +1399 -607
  6. package/dist/bin/exe-agent-config.js +123 -95
  7. package/dist/bin/exe-agent.js +41 -25
  8. package/dist/bin/exe-assign.js +732 -57
  9. package/dist/bin/exe-boot.js +795 -155
  10. package/dist/bin/exe-call.js +209 -138
  11. package/dist/bin/exe-cloud.js +35 -12
  12. package/dist/bin/exe-dispatch.js +703 -72
  13. package/dist/bin/exe-doctor.js +648 -26
  14. package/dist/bin/exe-export-behaviors.js +650 -20
  15. package/dist/bin/exe-forget.js +635 -13
  16. package/dist/bin/exe-gateway.js +1064 -273
  17. package/dist/bin/exe-heartbeat.js +676 -45
  18. package/dist/bin/exe-kill.js +646 -16
  19. package/dist/bin/exe-launch-agent.js +887 -97
  20. package/dist/bin/exe-link.js +658 -43
  21. package/dist/bin/exe-new-employee.js +378 -177
  22. package/dist/bin/exe-pending-messages.js +656 -34
  23. package/dist/bin/exe-pending-notifications.js +635 -13
  24. package/dist/bin/exe-pending-reviews.js +659 -37
  25. package/dist/bin/exe-rename.js +645 -30
  26. package/dist/bin/exe-review.js +635 -13
  27. package/dist/bin/exe-search.js +771 -88
  28. package/dist/bin/exe-session-cleanup.js +845 -152
  29. package/dist/bin/exe-settings.js +127 -91
  30. package/dist/bin/exe-start-codex.js +729 -94
  31. package/dist/bin/exe-start-opencode.js +717 -82
  32. package/dist/bin/exe-status.js +668 -37
  33. package/dist/bin/exe-team.js +635 -13
  34. package/dist/bin/git-sweep.js +731 -91
  35. package/dist/bin/graph-backfill.js +643 -13
  36. package/dist/bin/graph-export.js +646 -16
  37. package/dist/bin/install.js +596 -193
  38. package/dist/bin/scan-tasks.js +735 -95
  39. package/dist/bin/setup.js +1038 -210
  40. package/dist/bin/shard-migrate.js +645 -15
  41. package/dist/bin/wiki-sync.js +646 -16
  42. package/dist/gateway/index.js +1038 -247
  43. package/dist/hooks/bug-report-worker.js +902 -172
  44. package/dist/hooks/commit-complete.js +729 -89
  45. package/dist/hooks/error-recall.js +776 -93
  46. package/dist/hooks/exe-heartbeat-hook.js +85 -71
  47. package/dist/hooks/ingest-worker.js +851 -158
  48. package/dist/hooks/ingest.js +90 -73
  49. package/dist/hooks/instructions-loaded.js +669 -38
  50. package/dist/hooks/notification.js +661 -30
  51. package/dist/hooks/post-compact.js +685 -45
  52. package/dist/hooks/pre-compact.js +729 -89
  53. package/dist/hooks/pre-tool-use.js +883 -127
  54. package/dist/hooks/prompt-ingest-worker.js +758 -83
  55. package/dist/hooks/prompt-submit.js +1071 -321
  56. package/dist/hooks/response-ingest-worker.js +758 -83
  57. package/dist/hooks/session-end.js +732 -92
  58. package/dist/hooks/session-start.js +1042 -209
  59. package/dist/hooks/stop.js +691 -51
  60. package/dist/hooks/subagent-stop.js +685 -45
  61. package/dist/hooks/summary-worker.js +827 -134
  62. package/dist/index.js +1026 -234
  63. package/dist/lib/cloud-sync.js +663 -48
  64. package/dist/lib/consolidation.js +26 -3
  65. package/dist/lib/database.js +626 -18
  66. package/dist/lib/db.js +2261 -0
  67. package/dist/lib/device-registry.js +640 -25
  68. package/dist/lib/embedder.js +96 -43
  69. package/dist/lib/employee-templates.js +16 -0
  70. package/dist/lib/employees.js +259 -83
  71. package/dist/lib/exe-daemon-client.js +101 -63
  72. package/dist/lib/exe-daemon.js +905 -164
  73. package/dist/lib/hybrid-search.js +771 -88
  74. package/dist/lib/identity.js +27 -7
  75. package/dist/lib/messaging.js +66 -30
  76. package/dist/lib/reminders.js +21 -1
  77. package/dist/lib/schedules.js +636 -14
  78. package/dist/lib/skill-learning.js +21 -1
  79. package/dist/lib/store.js +643 -13
  80. package/dist/lib/task-router.js +82 -71
  81. package/dist/lib/tasks.js +109 -73
  82. package/dist/lib/tmux-routing.js +98 -62
  83. package/dist/lib/token-spend.js +26 -6
  84. package/dist/mcp/server.js +1807 -472
  85. package/dist/mcp/tools/complete-reminder.js +21 -1
  86. package/dist/mcp/tools/create-reminder.js +21 -1
  87. package/dist/mcp/tools/create-task.js +301 -166
  88. package/dist/mcp/tools/deactivate-behavior.js +24 -4
  89. package/dist/mcp/tools/list-reminders.js +21 -1
  90. package/dist/mcp/tools/list-tasks.js +206 -40
  91. package/dist/mcp/tools/send-message.js +69 -33
  92. package/dist/mcp/tools/update-task.js +86 -50
  93. package/dist/runtime/index.js +731 -91
  94. package/dist/tui/App.js +864 -125
  95. package/package.json +3 -2
package/dist/tui/App.js CHANGED
@@ -623,6 +623,18 @@ var init_runtime_table = __esm({
623
623
  });
624
624
 
625
625
  // src/lib/agent-config.ts
626
+ var agent_config_exports = {};
627
+ __export(agent_config_exports, {
628
+ AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
629
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
630
+ KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
631
+ RUNTIME_LABELS: () => RUNTIME_LABELS,
632
+ clearAgentRuntime: () => clearAgentRuntime,
633
+ getAgentRuntime: () => getAgentRuntime,
634
+ loadAgentConfig: () => loadAgentConfig,
635
+ saveAgentConfig: () => saveAgentConfig,
636
+ setAgentRuntime: () => setAgentRuntime
637
+ });
626
638
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
627
639
  import path3 from "path";
628
640
  function loadAgentConfig() {
@@ -633,6 +645,11 @@ function loadAgentConfig() {
633
645
  return {};
634
646
  }
635
647
  }
648
+ function saveAgentConfig(config) {
649
+ const dir = path3.dirname(AGENT_CONFIG_PATH);
650
+ if (!existsSync4(dir)) mkdirSync2(dir, { recursive: true });
651
+ writeFileSync2(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
652
+ }
636
653
  function getAgentRuntime(agentId) {
637
654
  const config = loadAgentConfig();
638
655
  const entry = config[agentId];
@@ -641,13 +658,47 @@ function getAgentRuntime(agentId) {
641
658
  if (orgDefault) return orgDefault;
642
659
  return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
643
660
  }
644
- var AGENT_CONFIG_PATH, DEFAULT_MODELS;
661
+ function setAgentRuntime(agentId, runtime, model) {
662
+ const knownModels = KNOWN_RUNTIMES[runtime];
663
+ if (!knownModels) {
664
+ return {
665
+ ok: false,
666
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
667
+ };
668
+ }
669
+ if (!knownModels.includes(model)) {
670
+ return {
671
+ ok: false,
672
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
673
+ };
674
+ }
675
+ const config = loadAgentConfig();
676
+ config[agentId] = { runtime, model };
677
+ saveAgentConfig(config);
678
+ return { ok: true };
679
+ }
680
+ function clearAgentRuntime(agentId) {
681
+ const config = loadAgentConfig();
682
+ delete config[agentId];
683
+ saveAgentConfig(config);
684
+ }
685
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
645
686
  var init_agent_config = __esm({
646
687
  "src/lib/agent-config.ts"() {
647
688
  "use strict";
648
689
  init_config();
649
690
  init_runtime_table();
650
691
  AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
692
+ KNOWN_RUNTIMES = {
693
+ claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
694
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
695
+ opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
696
+ };
697
+ RUNTIME_LABELS = {
698
+ claude: "Claude Code (Anthropic)",
699
+ codex: "Codex (OpenAI)",
700
+ opencode: "OpenCode (open source)"
701
+ };
651
702
  DEFAULT_MODELS = {
652
703
  claude: "claude-opus-4",
653
704
  codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
@@ -852,6 +903,7 @@ __export(employees_exports, {
852
903
  getEmployeeByRole: () => getEmployeeByRole,
853
904
  getEmployeeNamesByRole: () => getEmployeeNamesByRole,
854
905
  hasRole: () => hasRole,
906
+ hireEmployee: () => hireEmployee,
855
907
  isCoordinatorName: () => isCoordinatorName,
856
908
  isCoordinatorRole: () => isCoordinatorRole,
857
909
  isMultiInstance: () => isMultiInstance,
@@ -962,6 +1014,52 @@ function addEmployee(employees, employee) {
962
1014
  }
963
1015
  return [...employees, normalized];
964
1016
  }
1017
+ function appendToCoordinatorTeam(employee) {
1018
+ const coordinator = getCoordinatorEmployee(loadEmployeesSync());
1019
+ if (!coordinator) return;
1020
+ const idPath = path5.join(IDENTITY_DIR, `${coordinator.name}.md`);
1021
+ if (!existsSync6(idPath)) return;
1022
+ const content = readFileSync6(idPath, "utf-8");
1023
+ if (content.includes(`**${capitalize(employee.name)}`)) return;
1024
+ const teamMatch = content.match(TEAM_SECTION_RE);
1025
+ if (!teamMatch || teamMatch.index === void 0) return;
1026
+ const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
1027
+ const nextHeading = afterTeam.match(/\n## /);
1028
+ const entry = `
1029
+ **${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
1030
+ `;
1031
+ let updated;
1032
+ if (nextHeading && nextHeading.index !== void 0) {
1033
+ const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
1034
+ updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
1035
+ } else {
1036
+ updated = content.trimEnd() + "\n" + entry;
1037
+ }
1038
+ writeFileSync4(idPath, updated, "utf-8");
1039
+ }
1040
+ function capitalize(s) {
1041
+ return s.charAt(0).toUpperCase() + s.slice(1);
1042
+ }
1043
+ async function hireEmployee(employee) {
1044
+ const employees = await loadEmployees();
1045
+ const updated = addEmployee(employees, employee);
1046
+ await saveEmployees(updated);
1047
+ try {
1048
+ appendToCoordinatorTeam(employee);
1049
+ } catch {
1050
+ }
1051
+ try {
1052
+ const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
1053
+ const config = loadAgentConfig2();
1054
+ const name = employee.name.toLowerCase();
1055
+ if (!config[name] && config["default"]) {
1056
+ config[name] = { ...config["default"] };
1057
+ saveAgentConfig2(config);
1058
+ }
1059
+ } catch {
1060
+ }
1061
+ return updated;
1062
+ }
965
1063
  async function normalizeRosterCase(rosterPath) {
966
1064
  const employees = await loadEmployees(rosterPath);
967
1065
  let changed = false;
@@ -1032,7 +1130,7 @@ function registerBinSymlinks(name) {
1032
1130
  }
1033
1131
  return { created, skipped, errors };
1034
1132
  }
1035
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
1133
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
1036
1134
  var init_employees = __esm({
1037
1135
  "src/lib/employees.ts"() {
1038
1136
  "use strict";
@@ -1041,16 +1139,602 @@ var init_employees = __esm({
1041
1139
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
1042
1140
  COORDINATOR_ROLE = "COO";
1043
1141
  MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
1142
+ IDENTITY_DIR = path5.join(EXE_AI_DIR, "identity");
1143
+ TEAM_SECTION_RE = /^## Team\b.*$/m;
1144
+ }
1145
+ });
1146
+
1147
+ // src/lib/database-adapter.ts
1148
+ import os5 from "os";
1149
+ import path6 from "path";
1150
+ import { createRequire } from "module";
1151
+ import { pathToFileURL } from "url";
1152
+ function quotedIdentifier(identifier) {
1153
+ return `"${identifier.replace(/"/g, '""')}"`;
1154
+ }
1155
+ function unqualifiedTableName(name) {
1156
+ const raw = name.trim().replace(/^"|"$/g, "");
1157
+ const parts = raw.split(".");
1158
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
1159
+ }
1160
+ function stripTrailingSemicolon(sql) {
1161
+ return sql.trim().replace(/;+\s*$/u, "");
1162
+ }
1163
+ function appendClause(sql, clause) {
1164
+ const trimmed = stripTrailingSemicolon(sql);
1165
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
1166
+ if (!returningMatch) {
1167
+ return `${trimmed}${clause}`;
1168
+ }
1169
+ const idx = returningMatch.index;
1170
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
1171
+ }
1172
+ function normalizeStatement(stmt) {
1173
+ if (typeof stmt === "string") {
1174
+ return { kind: "positional", sql: stmt, args: [] };
1175
+ }
1176
+ const sql = stmt.sql;
1177
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
1178
+ return { kind: "positional", sql, args: stmt.args ?? [] };
1179
+ }
1180
+ return { kind: "named", sql, args: stmt.args };
1181
+ }
1182
+ function rewriteBooleanLiterals(sql) {
1183
+ let out = sql;
1184
+ for (const column of BOOLEAN_COLUMN_NAMES) {
1185
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
1186
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
1187
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
1188
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
1189
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
1190
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
1191
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
1192
+ }
1193
+ return out;
1194
+ }
1195
+ function rewriteInsertOrIgnore(sql) {
1196
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
1197
+ return sql;
1198
+ }
1199
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
1200
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
1201
+ }
1202
+ function rewriteInsertOrReplace(sql) {
1203
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
1204
+ if (!match) {
1205
+ return sql;
1206
+ }
1207
+ const rawTable = match[1];
1208
+ const rawColumns = match[2];
1209
+ const remainder = match[3];
1210
+ const tableName = unqualifiedTableName(rawTable);
1211
+ const conflictKeys = UPSERT_KEYS[tableName];
1212
+ if (!conflictKeys?.length) {
1213
+ return sql;
1214
+ }
1215
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
1216
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
1217
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
1218
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
1219
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
1220
+ }
1221
+ function rewriteSql(sql) {
1222
+ let out = sql;
1223
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
1224
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
1225
+ out = rewriteBooleanLiterals(out);
1226
+ out = rewriteInsertOrReplace(out);
1227
+ out = rewriteInsertOrIgnore(out);
1228
+ return stripTrailingSemicolon(out);
1229
+ }
1230
+ function toBoolean(value) {
1231
+ if (value === null || value === void 0) return value;
1232
+ if (typeof value === "boolean") return value;
1233
+ if (typeof value === "number") return value !== 0;
1234
+ if (typeof value === "bigint") return value !== 0n;
1235
+ if (typeof value === "string") {
1236
+ const normalized = value.trim().toLowerCase();
1237
+ if (normalized === "0" || normalized === "false") return false;
1238
+ if (normalized === "1" || normalized === "true") return true;
1239
+ }
1240
+ return Boolean(value);
1241
+ }
1242
+ function countQuestionMarks(sql, end) {
1243
+ let count = 0;
1244
+ let inSingle = false;
1245
+ let inDouble = false;
1246
+ let inLineComment = false;
1247
+ let inBlockComment = false;
1248
+ for (let i = 0; i < end; i++) {
1249
+ const ch = sql[i];
1250
+ const next = sql[i + 1];
1251
+ if (inLineComment) {
1252
+ if (ch === "\n") inLineComment = false;
1253
+ continue;
1254
+ }
1255
+ if (inBlockComment) {
1256
+ if (ch === "*" && next === "/") {
1257
+ inBlockComment = false;
1258
+ i += 1;
1259
+ }
1260
+ continue;
1261
+ }
1262
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1263
+ inLineComment = true;
1264
+ i += 1;
1265
+ continue;
1266
+ }
1267
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1268
+ inBlockComment = true;
1269
+ i += 1;
1270
+ continue;
1271
+ }
1272
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1273
+ inSingle = !inSingle;
1274
+ continue;
1275
+ }
1276
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1277
+ inDouble = !inDouble;
1278
+ continue;
1279
+ }
1280
+ if (!inSingle && !inDouble && ch === "?") {
1281
+ count += 1;
1282
+ }
1283
+ }
1284
+ return count;
1285
+ }
1286
+ function findBooleanPlaceholderIndexes(sql) {
1287
+ const indexes = /* @__PURE__ */ new Set();
1288
+ for (const column of BOOLEAN_COLUMN_NAMES) {
1289
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
1290
+ for (const match of sql.matchAll(pattern)) {
1291
+ const matchText = match[0];
1292
+ const qIndex = match.index + matchText.lastIndexOf("?");
1293
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
1294
+ }
1295
+ }
1296
+ return indexes;
1297
+ }
1298
+ function coerceInsertBooleanArgs(sql, args) {
1299
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
1300
+ if (!match) return;
1301
+ const rawTable = match[1];
1302
+ const rawColumns = match[2];
1303
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
1304
+ if (!boolColumns?.size) return;
1305
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
1306
+ for (const [index, column] of columns.entries()) {
1307
+ if (boolColumns.has(column) && index < args.length) {
1308
+ args[index] = toBoolean(args[index]);
1309
+ }
1310
+ }
1311
+ }
1312
+ function coerceUpdateBooleanArgs(sql, args) {
1313
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
1314
+ if (!match) return;
1315
+ const rawTable = match[1];
1316
+ const setClause = match[2];
1317
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
1318
+ if (!boolColumns?.size) return;
1319
+ const assignments = setClause.split(",");
1320
+ let placeholderIndex = 0;
1321
+ for (const assignment of assignments) {
1322
+ if (!assignment.includes("?")) continue;
1323
+ placeholderIndex += 1;
1324
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
1325
+ if (colMatch && boolColumns.has(colMatch[1])) {
1326
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
1327
+ }
1328
+ }
1329
+ }
1330
+ function coerceBooleanArgs(sql, args) {
1331
+ const nextArgs = [...args];
1332
+ coerceInsertBooleanArgs(sql, nextArgs);
1333
+ coerceUpdateBooleanArgs(sql, nextArgs);
1334
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
1335
+ for (const index of placeholderIndexes) {
1336
+ if (index > 0 && index <= nextArgs.length) {
1337
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
1338
+ }
1339
+ }
1340
+ return nextArgs;
1341
+ }
1342
+ function convertQuestionMarksToDollarParams(sql) {
1343
+ let out = "";
1344
+ let placeholder = 0;
1345
+ let inSingle = false;
1346
+ let inDouble = false;
1347
+ let inLineComment = false;
1348
+ let inBlockComment = false;
1349
+ for (let i = 0; i < sql.length; i++) {
1350
+ const ch = sql[i];
1351
+ const next = sql[i + 1];
1352
+ if (inLineComment) {
1353
+ out += ch;
1354
+ if (ch === "\n") inLineComment = false;
1355
+ continue;
1356
+ }
1357
+ if (inBlockComment) {
1358
+ out += ch;
1359
+ if (ch === "*" && next === "/") {
1360
+ out += next;
1361
+ inBlockComment = false;
1362
+ i += 1;
1363
+ }
1364
+ continue;
1365
+ }
1366
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1367
+ out += ch + next;
1368
+ inLineComment = true;
1369
+ i += 1;
1370
+ continue;
1371
+ }
1372
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1373
+ out += ch + next;
1374
+ inBlockComment = true;
1375
+ i += 1;
1376
+ continue;
1377
+ }
1378
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1379
+ inSingle = !inSingle;
1380
+ out += ch;
1381
+ continue;
1382
+ }
1383
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1384
+ inDouble = !inDouble;
1385
+ out += ch;
1386
+ continue;
1387
+ }
1388
+ if (!inSingle && !inDouble && ch === "?") {
1389
+ placeholder += 1;
1390
+ out += `$${placeholder}`;
1391
+ continue;
1392
+ }
1393
+ out += ch;
1394
+ }
1395
+ return out;
1396
+ }
1397
+ function translateStatementForPostgres(stmt) {
1398
+ const normalized = normalizeStatement(stmt);
1399
+ if (normalized.kind === "named") {
1400
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
1401
+ }
1402
+ const rewrittenSql = rewriteSql(normalized.sql);
1403
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
1404
+ return {
1405
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
1406
+ args: coercedArgs
1407
+ };
1408
+ }
1409
+ function shouldBypassPostgres(stmt) {
1410
+ const normalized = normalizeStatement(stmt);
1411
+ if (normalized.kind === "named") {
1412
+ return true;
1413
+ }
1414
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
1415
+ }
1416
+ function shouldFallbackOnError(error) {
1417
+ const message = error instanceof Error ? error.message : String(error);
1418
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
1419
+ }
1420
+ function isReadQuery(sql) {
1421
+ const trimmed = sql.trimStart();
1422
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
1423
+ }
1424
+ function buildRow(row, columns) {
1425
+ const values = columns.map((column) => row[column]);
1426
+ return Object.assign(values, row);
1427
+ }
1428
+ function buildResultSet(rows, rowsAffected = 0) {
1429
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
1430
+ const resultRows = rows.map((row) => buildRow(row, columns));
1431
+ return {
1432
+ columns,
1433
+ columnTypes: columns.map(() => ""),
1434
+ rows: resultRows,
1435
+ rowsAffected,
1436
+ lastInsertRowid: void 0,
1437
+ toJSON() {
1438
+ return {
1439
+ columns,
1440
+ columnTypes: columns.map(() => ""),
1441
+ rows,
1442
+ rowsAffected,
1443
+ lastInsertRowid: void 0
1444
+ };
1445
+ }
1446
+ };
1447
+ }
1448
+ async function loadPrismaClient() {
1449
+ if (!prismaClientPromise) {
1450
+ prismaClientPromise = (async () => {
1451
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
1452
+ if (explicitPath) {
1453
+ const module2 = await import(pathToFileURL(explicitPath).href);
1454
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
1455
+ if (!PrismaClient2) {
1456
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
1457
+ }
1458
+ return new PrismaClient2();
1459
+ }
1460
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os5.homedir(), "exe-db");
1461
+ const requireFromExeDb = createRequire(path6.join(exeDbRoot, "package.json"));
1462
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
1463
+ const module = await import(pathToFileURL(prismaEntry).href);
1464
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
1465
+ if (!PrismaClient) {
1466
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
1467
+ }
1468
+ return new PrismaClient();
1469
+ })();
1470
+ }
1471
+ return prismaClientPromise;
1472
+ }
1473
+ async function ensureCompatibilityViews(prisma) {
1474
+ if (!compatibilityBootstrapPromise) {
1475
+ compatibilityBootstrapPromise = (async () => {
1476
+ for (const mapping of VIEW_MAPPINGS) {
1477
+ const relation = mapping.source.replace(/"/g, "");
1478
+ const rows = await prisma.$queryRawUnsafe(
1479
+ "SELECT to_regclass($1) AS regclass",
1480
+ relation
1481
+ );
1482
+ if (!rows[0]?.regclass) {
1483
+ continue;
1484
+ }
1485
+ await prisma.$executeRawUnsafe(
1486
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
1487
+ );
1488
+ }
1489
+ })();
1490
+ }
1491
+ return compatibilityBootstrapPromise;
1492
+ }
1493
+ async function executeOnPrisma(executor, stmt) {
1494
+ const translated = translateStatementForPostgres(stmt);
1495
+ if (isReadQuery(translated.sql)) {
1496
+ const rows = await executor.$queryRawUnsafe(
1497
+ translated.sql,
1498
+ ...translated.args
1499
+ );
1500
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
1501
+ }
1502
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
1503
+ return buildResultSet([], rowsAffected);
1504
+ }
1505
+ function splitSqlStatements(sql) {
1506
+ const parts = [];
1507
+ let current = "";
1508
+ let inSingle = false;
1509
+ let inDouble = false;
1510
+ let inLineComment = false;
1511
+ let inBlockComment = false;
1512
+ for (let i = 0; i < sql.length; i++) {
1513
+ const ch = sql[i];
1514
+ const next = sql[i + 1];
1515
+ if (inLineComment) {
1516
+ current += ch;
1517
+ if (ch === "\n") inLineComment = false;
1518
+ continue;
1519
+ }
1520
+ if (inBlockComment) {
1521
+ current += ch;
1522
+ if (ch === "*" && next === "/") {
1523
+ current += next;
1524
+ inBlockComment = false;
1525
+ i += 1;
1526
+ }
1527
+ continue;
1528
+ }
1529
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1530
+ current += ch + next;
1531
+ inLineComment = true;
1532
+ i += 1;
1533
+ continue;
1534
+ }
1535
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1536
+ current += ch + next;
1537
+ inBlockComment = true;
1538
+ i += 1;
1539
+ continue;
1540
+ }
1541
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1542
+ inSingle = !inSingle;
1543
+ current += ch;
1544
+ continue;
1545
+ }
1546
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1547
+ inDouble = !inDouble;
1548
+ current += ch;
1549
+ continue;
1550
+ }
1551
+ if (!inSingle && !inDouble && ch === ";") {
1552
+ if (current.trim()) {
1553
+ parts.push(current.trim());
1554
+ }
1555
+ current = "";
1556
+ continue;
1557
+ }
1558
+ current += ch;
1559
+ }
1560
+ if (current.trim()) {
1561
+ parts.push(current.trim());
1562
+ }
1563
+ return parts;
1564
+ }
1565
+ async function createPrismaDbAdapter(fallbackClient) {
1566
+ const prisma = await loadPrismaClient();
1567
+ await ensureCompatibilityViews(prisma);
1568
+ let closed = false;
1569
+ let adapter;
1570
+ const fallbackExecute = async (stmt, error) => {
1571
+ if (!fallbackClient) {
1572
+ if (error) throw error;
1573
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
1574
+ }
1575
+ if (error) {
1576
+ process.stderr.write(
1577
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
1578
+ `
1579
+ );
1580
+ }
1581
+ return fallbackClient.execute(stmt);
1582
+ };
1583
+ adapter = {
1584
+ async execute(stmt) {
1585
+ if (shouldBypassPostgres(stmt)) {
1586
+ return fallbackExecute(stmt);
1587
+ }
1588
+ try {
1589
+ return await executeOnPrisma(prisma, stmt);
1590
+ } catch (error) {
1591
+ if (shouldFallbackOnError(error)) {
1592
+ return fallbackExecute(stmt, error);
1593
+ }
1594
+ throw error;
1595
+ }
1596
+ },
1597
+ async batch(stmts, mode) {
1598
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
1599
+ if (!fallbackClient) {
1600
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
1601
+ }
1602
+ return fallbackClient.batch(stmts, mode);
1603
+ }
1604
+ try {
1605
+ if (prisma.$transaction) {
1606
+ return await prisma.$transaction(async (tx) => {
1607
+ const results2 = [];
1608
+ for (const stmt of stmts) {
1609
+ results2.push(await executeOnPrisma(tx, stmt));
1610
+ }
1611
+ return results2;
1612
+ });
1613
+ }
1614
+ const results = [];
1615
+ for (const stmt of stmts) {
1616
+ results.push(await executeOnPrisma(prisma, stmt));
1617
+ }
1618
+ return results;
1619
+ } catch (error) {
1620
+ if (fallbackClient && shouldFallbackOnError(error)) {
1621
+ process.stderr.write(
1622
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
1623
+ `
1624
+ );
1625
+ return fallbackClient.batch(stmts, mode);
1626
+ }
1627
+ throw error;
1628
+ }
1629
+ },
1630
+ async migrate(stmts) {
1631
+ if (fallbackClient) {
1632
+ return fallbackClient.migrate(stmts);
1633
+ }
1634
+ return adapter.batch(stmts, "deferred");
1635
+ },
1636
+ async transaction(mode) {
1637
+ if (!fallbackClient) {
1638
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
1639
+ }
1640
+ return fallbackClient.transaction(mode);
1641
+ },
1642
+ async executeMultiple(sql) {
1643
+ if (fallbackClient && shouldBypassPostgres(sql)) {
1644
+ return fallbackClient.executeMultiple(sql);
1645
+ }
1646
+ for (const statement of splitSqlStatements(sql)) {
1647
+ await adapter.execute(statement);
1648
+ }
1649
+ },
1650
+ async sync() {
1651
+ if (fallbackClient) {
1652
+ return fallbackClient.sync();
1653
+ }
1654
+ return { frame_no: 0, frames_synced: 0 };
1655
+ },
1656
+ close() {
1657
+ closed = true;
1658
+ prismaClientPromise = null;
1659
+ compatibilityBootstrapPromise = null;
1660
+ void prisma.$disconnect?.();
1661
+ },
1662
+ get closed() {
1663
+ return closed;
1664
+ },
1665
+ get protocol() {
1666
+ return "prisma-postgres";
1667
+ }
1668
+ };
1669
+ return adapter;
1670
+ }
1671
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
1672
+ var init_database_adapter = __esm({
1673
+ "src/lib/database-adapter.ts"() {
1674
+ "use strict";
1675
+ VIEW_MAPPINGS = [
1676
+ { view: "memories", source: "memory.memory_records" },
1677
+ { view: "tasks", source: "memory.tasks" },
1678
+ { view: "behaviors", source: "memory.behaviors" },
1679
+ { view: "entities", source: "memory.entities" },
1680
+ { view: "relationships", source: "memory.relationships" },
1681
+ { view: "entity_memories", source: "memory.entity_memories" },
1682
+ { view: "entity_aliases", source: "memory.entity_aliases" },
1683
+ { view: "notifications", source: "memory.notifications" },
1684
+ { view: "messages", source: "memory.messages" },
1685
+ { view: "users", source: "wiki.users" },
1686
+ { view: "workspaces", source: "wiki.workspaces" },
1687
+ { view: "workspace_users", source: "wiki.workspace_users" },
1688
+ { view: "documents", source: "wiki.workspace_documents" },
1689
+ { view: "chats", source: "wiki.workspace_chats" }
1690
+ ];
1691
+ UPSERT_KEYS = {
1692
+ memories: ["id"],
1693
+ tasks: ["id"],
1694
+ behaviors: ["id"],
1695
+ entities: ["id"],
1696
+ relationships: ["id"],
1697
+ entity_aliases: ["alias"],
1698
+ notifications: ["id"],
1699
+ messages: ["id"],
1700
+ users: ["id"],
1701
+ workspaces: ["id"],
1702
+ workspace_users: ["id"],
1703
+ documents: ["id"],
1704
+ chats: ["id"]
1705
+ };
1706
+ BOOLEAN_COLUMNS_BY_TABLE = {
1707
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
1708
+ behaviors: /* @__PURE__ */ new Set(["active"]),
1709
+ notifications: /* @__PURE__ */ new Set(["read"]),
1710
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
1711
+ };
1712
+ BOOLEAN_COLUMN_NAMES = new Set(
1713
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
1714
+ );
1715
+ IMMEDIATE_FALLBACK_PATTERNS = [
1716
+ /\bPRAGMA\b/i,
1717
+ /\bsqlite_master\b/i,
1718
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
1719
+ /\bMATCH\b/i,
1720
+ /\bvector_distance_cos\s*\(/i,
1721
+ /\bjson_extract\s*\(/i,
1722
+ /\bjulianday\s*\(/i,
1723
+ /\bstrftime\s*\(/i,
1724
+ /\blast_insert_rowid\s*\(/i
1725
+ ];
1726
+ prismaClientPromise = null;
1727
+ compatibilityBootstrapPromise = null;
1044
1728
  }
1045
1729
  });
1046
1730
 
1047
1731
  // src/lib/exe-daemon-client.ts
1048
1732
  import net from "net";
1049
- import os5 from "os";
1733
+ import os6 from "os";
1050
1734
  import { spawn } from "child_process";
1051
1735
  import { randomUUID } from "crypto";
1052
1736
  import { existsSync as existsSync7, unlinkSync as unlinkSync2, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
1053
- import path6 from "path";
1737
+ import path7 from "path";
1054
1738
  import { fileURLToPath } from "url";
1055
1739
  function handleData(chunk) {
1056
1740
  _buffer += chunk.toString();
@@ -1101,17 +1785,17 @@ function cleanupStaleFiles() {
1101
1785
  }
1102
1786
  }
1103
1787
  function findPackageRoot() {
1104
- let dir = path6.dirname(fileURLToPath(import.meta.url));
1105
- const { root } = path6.parse(dir);
1788
+ let dir = path7.dirname(fileURLToPath(import.meta.url));
1789
+ const { root } = path7.parse(dir);
1106
1790
  while (dir !== root) {
1107
- if (existsSync7(path6.join(dir, "package.json"))) return dir;
1108
- dir = path6.dirname(dir);
1791
+ if (existsSync7(path7.join(dir, "package.json"))) return dir;
1792
+ dir = path7.dirname(dir);
1109
1793
  }
1110
1794
  return null;
1111
1795
  }
1112
1796
  function spawnDaemon() {
1113
- const freeGB = os5.freemem() / (1024 * 1024 * 1024);
1114
- const totalGB = os5.totalmem() / (1024 * 1024 * 1024);
1797
+ const freeGB = os6.freemem() / (1024 * 1024 * 1024);
1798
+ const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
1115
1799
  if (totalGB <= 8) {
1116
1800
  process.stderr.write(
1117
1801
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
@@ -1131,7 +1815,7 @@ function spawnDaemon() {
1131
1815
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
1132
1816
  return;
1133
1817
  }
1134
- const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1818
+ const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1135
1819
  if (!existsSync7(daemonPath)) {
1136
1820
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1137
1821
  `);
@@ -1140,7 +1824,7 @@ function spawnDaemon() {
1140
1824
  const resolvedPath = daemonPath;
1141
1825
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1142
1826
  `);
1143
- const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
1827
+ const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
1144
1828
  let stderrFd = "ignore";
1145
1829
  try {
1146
1830
  stderrFd = openSync(logPath, "a");
@@ -1287,9 +1971,9 @@ var init_exe_daemon_client = __esm({
1287
1971
  "src/lib/exe-daemon-client.ts"() {
1288
1972
  "use strict";
1289
1973
  init_config();
1290
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
1291
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
1292
- SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
1974
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
1975
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
1976
+ SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
1293
1977
  SPAWN_LOCK_STALE_MS = 3e4;
1294
1978
  CONNECT_TIMEOUT_MS = 15e3;
1295
1979
  REQUEST_TIMEOUT_MS = 3e4;
@@ -1371,7 +2055,7 @@ __export(db_daemon_client_exports, {
1371
2055
  createDaemonDbClient: () => createDaemonDbClient,
1372
2056
  initDaemonDbClient: () => initDaemonDbClient
1373
2057
  });
1374
- function normalizeStatement(stmt) {
2058
+ function normalizeStatement2(stmt) {
1375
2059
  if (typeof stmt === "string") {
1376
2060
  return { sql: stmt, args: [] };
1377
2061
  }
@@ -1395,7 +2079,7 @@ function createDaemonDbClient(fallbackClient) {
1395
2079
  if (!_useDaemon || !isClientConnected()) {
1396
2080
  return fallbackClient.execute(stmt);
1397
2081
  }
1398
- const { sql, args } = normalizeStatement(stmt);
2082
+ const { sql, args } = normalizeStatement2(stmt);
1399
2083
  const response = await sendDaemonRequest({
1400
2084
  type: "db-execute",
1401
2085
  sql,
@@ -1420,7 +2104,7 @@ function createDaemonDbClient(fallbackClient) {
1420
2104
  if (!_useDaemon || !isClientConnected()) {
1421
2105
  return fallbackClient.batch(stmts, mode);
1422
2106
  }
1423
- const statements = stmts.map(normalizeStatement);
2107
+ const statements = stmts.map(normalizeStatement2);
1424
2108
  const response = await sendDaemonRequest({
1425
2109
  type: "db-batch",
1426
2110
  statements,
@@ -1515,6 +2199,18 @@ __export(database_exports, {
1515
2199
  });
1516
2200
  import { createClient } from "@libsql/client";
1517
2201
  async function initDatabase(config) {
2202
+ if (_walCheckpointTimer) {
2203
+ clearInterval(_walCheckpointTimer);
2204
+ _walCheckpointTimer = null;
2205
+ }
2206
+ if (_daemonClient) {
2207
+ _daemonClient.close();
2208
+ _daemonClient = null;
2209
+ }
2210
+ if (_adapterClient && _adapterClient !== _resilientClient) {
2211
+ _adapterClient.close();
2212
+ }
2213
+ _adapterClient = null;
1518
2214
  if (_client) {
1519
2215
  _client.close();
1520
2216
  _client = null;
@@ -1528,6 +2224,7 @@ async function initDatabase(config) {
1528
2224
  }
1529
2225
  _client = createClient(opts);
1530
2226
  _resilientClient = wrapWithRetry(_client);
2227
+ _adapterClient = _resilientClient;
1531
2228
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
1532
2229
  });
1533
2230
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -1538,14 +2235,20 @@ async function initDatabase(config) {
1538
2235
  });
1539
2236
  }, 3e4);
1540
2237
  _walCheckpointTimer.unref();
2238
+ if (process.env.DATABASE_URL) {
2239
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
2240
+ }
1541
2241
  }
1542
2242
  function isInitialized() {
1543
- return _client !== null;
2243
+ return _adapterClient !== null || _client !== null;
1544
2244
  }
1545
2245
  function getClient() {
1546
- if (!_resilientClient) {
2246
+ if (!_adapterClient) {
1547
2247
  throw new Error("Database client not initialized. Call initDatabase() first.");
1548
2248
  }
2249
+ if (process.env.DATABASE_URL) {
2250
+ return _adapterClient;
2251
+ }
1549
2252
  if (process.env.EXE_IS_DAEMON === "1") {
1550
2253
  return _resilientClient;
1551
2254
  }
@@ -1555,6 +2258,7 @@ function getClient() {
1555
2258
  return _resilientClient;
1556
2259
  }
1557
2260
  async function initDaemonClient() {
2261
+ if (process.env.DATABASE_URL) return;
1558
2262
  if (process.env.EXE_IS_DAEMON === "1") return;
1559
2263
  if (!_resilientClient) return;
1560
2264
  try {
@@ -2499,26 +3203,36 @@ async function ensureSchema() {
2499
3203
  }
2500
3204
  }
2501
3205
  async function disposeDatabase() {
3206
+ if (_walCheckpointTimer) {
3207
+ clearInterval(_walCheckpointTimer);
3208
+ _walCheckpointTimer = null;
3209
+ }
2502
3210
  if (_daemonClient) {
2503
3211
  _daemonClient.close();
2504
3212
  _daemonClient = null;
2505
3213
  }
3214
+ if (_adapterClient && _adapterClient !== _resilientClient) {
3215
+ _adapterClient.close();
3216
+ }
3217
+ _adapterClient = null;
2506
3218
  if (_client) {
2507
3219
  _client.close();
2508
3220
  _client = null;
2509
3221
  _resilientClient = null;
2510
3222
  }
2511
3223
  }
2512
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
3224
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
2513
3225
  var init_database = __esm({
2514
3226
  "src/lib/database.ts"() {
2515
3227
  "use strict";
2516
3228
  init_db_retry();
2517
3229
  init_employees();
3230
+ init_database_adapter();
2518
3231
  _client = null;
2519
3232
  _resilientClient = null;
2520
3233
  _walCheckpointTimer = null;
2521
3234
  _daemonClient = null;
3235
+ _adapterClient = null;
2522
3236
  initTurso = initDatabase;
2523
3237
  disposeTurso = disposeDatabase;
2524
3238
  }
@@ -2543,7 +3257,7 @@ __export(license_exports, {
2543
3257
  });
2544
3258
  import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
2545
3259
  import { randomUUID as randomUUID2 } from "crypto";
2546
- import path7 from "path";
3260
+ import path8 from "path";
2547
3261
  import { jwtVerify, importSPKI } from "jose";
2548
3262
  async function fetchRetry(url, init) {
2549
3263
  try {
@@ -2554,7 +3268,7 @@ async function fetchRetry(url, init) {
2554
3268
  }
2555
3269
  }
2556
3270
  function loadDeviceId() {
2557
- const deviceJsonPath = path7.join(EXE_AI_DIR, "device.json");
3271
+ const deviceJsonPath = path8.join(EXE_AI_DIR, "device.json");
2558
3272
  try {
2559
3273
  if (existsSync8(deviceJsonPath)) {
2560
3274
  const data = JSON.parse(readFileSync8(deviceJsonPath, "utf8"));
@@ -2719,7 +3433,7 @@ async function checkLicense() {
2719
3433
  let key = loadLicense();
2720
3434
  if (!key) {
2721
3435
  try {
2722
- const configPath = path7.join(EXE_AI_DIR, "config.json");
3436
+ const configPath = path8.join(EXE_AI_DIR, "config.json");
2723
3437
  if (existsSync8(configPath)) {
2724
3438
  const raw = JSON.parse(readFileSync8(configPath, "utf8"));
2725
3439
  const cloud = raw.cloud;
@@ -2880,9 +3594,9 @@ var init_license = __esm({
2880
3594
  "src/lib/license.ts"() {
2881
3595
  "use strict";
2882
3596
  init_config();
2883
- LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
2884
- CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
2885
- DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
3597
+ LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
3598
+ CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
3599
+ DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
2886
3600
  API_BASE = "https://askexe.com/cloud";
2887
3601
  RETRY_DELAY_MS = 500;
2888
3602
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
@@ -2913,7 +3627,7 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
2913
3627
 
2914
3628
  // src/lib/plan-limits.ts
2915
3629
  import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
2916
- import path8 from "path";
3630
+ import path9 from "path";
2917
3631
  function getLicenseSync() {
2918
3632
  try {
2919
3633
  if (!existsSync9(CACHE_PATH2)) return freeLicense();
@@ -2985,14 +3699,14 @@ var init_plan_limits = __esm({
2985
3699
  this.name = "PlanLimitError";
2986
3700
  }
2987
3701
  };
2988
- CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
3702
+ CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
2989
3703
  }
2990
3704
  });
2991
3705
 
2992
3706
  // src/lib/notifications.ts
2993
3707
  import crypto from "crypto";
2994
- import path9 from "path";
2995
- import os6 from "os";
3708
+ import path10 from "path";
3709
+ import os7 from "os";
2996
3710
  import {
2997
3711
  readFileSync as readFileSync10,
2998
3712
  readdirSync,
@@ -3148,8 +3862,8 @@ __export(tasks_crud_exports, {
3148
3862
  writeCheckpoint: () => writeCheckpoint
3149
3863
  });
3150
3864
  import crypto3 from "crypto";
3151
- import path10 from "path";
3152
- import os7 from "os";
3865
+ import path11 from "path";
3866
+ import os8 from "os";
3153
3867
  import { execSync as execSync5 } from "child_process";
3154
3868
  import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
3155
3869
  import { existsSync as existsSync11, readFileSync as readFileSync11 } from "fs";
@@ -3327,8 +4041,8 @@ ${laneWarning}` : laneWarning;
3327
4041
  }
3328
4042
  if (input.baseDir) {
3329
4043
  try {
3330
- await mkdir3(path10.join(input.baseDir, "exe", "output"), { recursive: true });
3331
- await mkdir3(path10.join(input.baseDir, "exe", "research"), { recursive: true });
4044
+ await mkdir3(path11.join(input.baseDir, "exe", "output"), { recursive: true });
4045
+ await mkdir3(path11.join(input.baseDir, "exe", "research"), { recursive: true });
3332
4046
  await ensureArchitectureDoc(input.baseDir, input.projectName);
3333
4047
  await ensureGitignoreExe(input.baseDir);
3334
4048
  } catch {
@@ -3364,9 +4078,9 @@ ${laneWarning}` : laneWarning;
3364
4078
  });
3365
4079
  if (input.baseDir) {
3366
4080
  try {
3367
- const EXE_OS_DIR = path10.join(os7.homedir(), ".exe-os");
3368
- const mdPath = path10.join(EXE_OS_DIR, taskFile);
3369
- const mdDir = path10.dirname(mdPath);
4081
+ const EXE_OS_DIR = path11.join(os8.homedir(), ".exe-os");
4082
+ const mdPath = path11.join(EXE_OS_DIR, taskFile);
4083
+ const mdDir = path11.dirname(mdPath);
3370
4084
  if (!existsSync11(mdDir)) await mkdir3(mdDir, { recursive: true });
3371
4085
  const reviewer = input.reviewer ?? input.assignedBy;
3372
4086
  const mdContent = `# ${input.title}
@@ -3667,7 +4381,7 @@ async function deleteTaskCore(taskId, _baseDir) {
3667
4381
  return { taskFile, assignedTo, assignedBy, taskSlug };
3668
4382
  }
3669
4383
  async function ensureArchitectureDoc(baseDir, projectName) {
3670
- const archPath = path10.join(baseDir, "exe", "ARCHITECTURE.md");
4384
+ const archPath = path11.join(baseDir, "exe", "ARCHITECTURE.md");
3671
4385
  try {
3672
4386
  if (existsSync11(archPath)) return;
3673
4387
  const template = [
@@ -3702,7 +4416,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
3702
4416
  }
3703
4417
  }
3704
4418
  async function ensureGitignoreExe(baseDir) {
3705
- const gitignorePath = path10.join(baseDir, ".gitignore");
4419
+ const gitignorePath = path11.join(baseDir, ".gitignore");
3706
4420
  try {
3707
4421
  if (existsSync11(gitignorePath)) {
3708
4422
  const content = readFileSync11(gitignorePath, "utf-8");
@@ -3736,13 +4450,13 @@ var init_tasks_crud = __esm({
3736
4450
  });
3737
4451
 
3738
4452
  // src/lib/tasks-review.ts
3739
- import path11 from "path";
4453
+ import path12 from "path";
3740
4454
  import { existsSync as existsSync12, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
3741
4455
  async function countPendingReviews(sessionScope) {
3742
4456
  const client = getClient();
3743
4457
  if (sessionScope) {
3744
4458
  const result2 = await client.execute({
3745
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND (session_scope = ? OR session_scope IS NULL)",
4459
+ sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
3746
4460
  args: [sessionScope]
3747
4461
  });
3748
4462
  return Number(result2.rows[0]?.cnt) || 0;
@@ -3918,11 +4632,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
3918
4632
  );
3919
4633
  }
3920
4634
  try {
3921
- const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
4635
+ const cacheDir = path12.join(EXE_AI_DIR, "session-cache");
3922
4636
  if (existsSync12(cacheDir)) {
3923
4637
  for (const f of readdirSync2(cacheDir)) {
3924
4638
  if (f.startsWith("review-notified-")) {
3925
- unlinkSync4(path11.join(cacheDir, f));
4639
+ unlinkSync4(path12.join(cacheDir, f));
3926
4640
  }
3927
4641
  }
3928
4642
  }
@@ -3943,7 +4657,7 @@ var init_tasks_review = __esm({
3943
4657
  });
3944
4658
 
3945
4659
  // src/lib/tasks-chain.ts
3946
- import path12 from "path";
4660
+ import path13 from "path";
3947
4661
  import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
3948
4662
  async function cascadeUnblock(taskId, baseDir, now) {
3949
4663
  const client = getClient();
@@ -3960,7 +4674,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
3960
4674
  });
3961
4675
  for (const ur of unblockedRows.rows) {
3962
4676
  try {
3963
- const ubFile = path12.join(baseDir, String(ur.task_file));
4677
+ const ubFile = path13.join(baseDir, String(ur.task_file));
3964
4678
  let ubContent = await readFile3(ubFile, "utf-8");
3965
4679
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
3966
4680
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -4029,7 +4743,7 @@ var init_tasks_chain = __esm({
4029
4743
 
4030
4744
  // src/lib/project-name.ts
4031
4745
  import { execSync as execSync6 } from "child_process";
4032
- import path13 from "path";
4746
+ import path14 from "path";
4033
4747
  function getProjectName(cwd2) {
4034
4748
  const dir = cwd2 ?? process.cwd();
4035
4749
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -4042,7 +4756,7 @@ function getProjectName(cwd2) {
4042
4756
  timeout: 2e3,
4043
4757
  stdio: ["pipe", "pipe", "pipe"]
4044
4758
  }).trim();
4045
- repoRoot = path13.dirname(gitCommonDir);
4759
+ repoRoot = path14.dirname(gitCommonDir);
4046
4760
  } catch {
4047
4761
  repoRoot = execSync6("git rev-parse --show-toplevel", {
4048
4762
  cwd: dir,
@@ -4051,11 +4765,11 @@ function getProjectName(cwd2) {
4051
4765
  stdio: ["pipe", "pipe", "pipe"]
4052
4766
  }).trim();
4053
4767
  }
4054
- _cached2 = path13.basename(repoRoot);
4768
+ _cached2 = path14.basename(repoRoot);
4055
4769
  _cachedCwd = dir;
4056
4770
  return _cached2;
4057
4771
  } catch {
4058
- _cached2 = path13.basename(dir);
4772
+ _cached2 = path14.basename(dir);
4059
4773
  _cachedCwd = dir;
4060
4774
  return _cached2;
4061
4775
  }
@@ -4528,7 +5242,7 @@ __export(tasks_exports, {
4528
5242
  updateTaskStatus: () => updateTaskStatus,
4529
5243
  writeCheckpoint: () => writeCheckpoint
4530
5244
  });
4531
- import path14 from "path";
5245
+ import path15 from "path";
4532
5246
  import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
4533
5247
  async function createTask(input) {
4534
5248
  const result = await createTaskCore(input);
@@ -4548,8 +5262,8 @@ async function updateTask(input) {
4548
5262
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
4549
5263
  try {
4550
5264
  const agent = String(row.assigned_to);
4551
- const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
4552
- const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
5265
+ const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
5266
+ const cachePath = path15.join(cacheDir, `current-task-${agent}.json`);
4553
5267
  if (input.status === "in_progress") {
4554
5268
  mkdirSync5(cacheDir, { recursive: true });
4555
5269
  writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
@@ -5020,12 +5734,12 @@ __export(tmux_routing_exports, {
5020
5734
  });
5021
5735
  import { execFileSync as execFileSync3, execSync as execSync7 } from "child_process";
5022
5736
  import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync13, appendFileSync, readdirSync as readdirSync3 } from "fs";
5023
- import path15 from "path";
5024
- import os8 from "os";
5737
+ import path16 from "path";
5738
+ import os9 from "os";
5025
5739
  import { fileURLToPath as fileURLToPath2 } from "url";
5026
5740
  import { unlinkSync as unlinkSync6 } from "fs";
5027
5741
  function spawnLockPath(sessionName) {
5028
- return path15.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
5742
+ return path16.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
5029
5743
  }
5030
5744
  function isProcessAlive(pid) {
5031
5745
  try {
@@ -5062,8 +5776,8 @@ function releaseSpawnLock2(sessionName) {
5062
5776
  function resolveBehaviorsExporterScript() {
5063
5777
  try {
5064
5778
  const thisFile = fileURLToPath2(import.meta.url);
5065
- const scriptPath = path15.join(
5066
- path15.dirname(thisFile),
5779
+ const scriptPath = path16.join(
5780
+ path16.dirname(thisFile),
5067
5781
  "..",
5068
5782
  "bin",
5069
5783
  "exe-export-behaviors.js"
@@ -5138,7 +5852,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
5138
5852
  mkdirSync6(SESSION_CACHE, { recursive: true });
5139
5853
  }
5140
5854
  const rootExe = extractRootExe(parentExe) ?? parentExe;
5141
- const filePath = path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
5855
+ const filePath = path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
5142
5856
  writeFileSync7(filePath, JSON.stringify({
5143
5857
  parentExe: rootExe,
5144
5858
  dispatchedBy: dispatchedBy || rootExe,
@@ -5147,7 +5861,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
5147
5861
  }
5148
5862
  function getParentExe(sessionKey) {
5149
5863
  try {
5150
- const data = JSON.parse(readFileSync12(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
5864
+ const data = JSON.parse(readFileSync12(path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
5151
5865
  return data.parentExe || null;
5152
5866
  } catch {
5153
5867
  return null;
@@ -5156,7 +5870,7 @@ function getParentExe(sessionKey) {
5156
5870
  function getDispatchedBy(sessionKey) {
5157
5871
  try {
5158
5872
  const data = JSON.parse(readFileSync12(
5159
- path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
5873
+ path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
5160
5874
  "utf8"
5161
5875
  ));
5162
5876
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -5167,15 +5881,24 @@ function getDispatchedBy(sessionKey) {
5167
5881
  function resolveExeSession() {
5168
5882
  const mySession = getMySession();
5169
5883
  if (!mySession) return null;
5884
+ const fromSessionName = extractRootExe(mySession);
5170
5885
  try {
5171
5886
  const key = getSessionKey();
5172
5887
  const parentExe = getParentExe(key);
5173
5888
  if (parentExe) {
5174
- return extractRootExe(parentExe) ?? parentExe;
5889
+ const fromCache = extractRootExe(parentExe) ?? parentExe;
5890
+ if (fromSessionName && fromCache !== fromSessionName) {
5891
+ process.stderr.write(
5892
+ `[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
5893
+ `
5894
+ );
5895
+ return fromSessionName;
5896
+ }
5897
+ return fromCache;
5175
5898
  }
5176
5899
  } catch {
5177
5900
  }
5178
- return extractRootExe(mySession) ?? mySession;
5901
+ return fromSessionName ?? mySession;
5179
5902
  }
5180
5903
  function isEmployeeAlive(sessionName) {
5181
5904
  return getTransport().isAlive(sessionName);
@@ -5333,7 +6056,7 @@ function sendIntercom(targetSession) {
5333
6056
  try {
5334
6057
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
5335
6058
  const agent = baseAgentName(rawAgent);
5336
- const markerPath = path15.join(SESSION_CACHE, `current-task-${agent}.json`);
6059
+ const markerPath = path16.join(SESSION_CACHE, `current-task-${agent}.json`);
5337
6060
  if (existsSync13(markerPath)) {
5338
6061
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
5339
6062
  return "debounced";
@@ -5343,7 +6066,7 @@ function sendIntercom(targetSession) {
5343
6066
  try {
5344
6067
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
5345
6068
  const agent = baseAgentName(rawAgent);
5346
- const taskDir = path15.join(process.cwd(), "exe", agent);
6069
+ const taskDir = path16.join(process.cwd(), "exe", agent);
5347
6070
  if (existsSync13(taskDir)) {
5348
6071
  const files = readdirSync3(taskDir).filter(
5349
6072
  (f) => f.endsWith(".md") && f !== "DONE.txt"
@@ -5477,8 +6200,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5477
6200
  const transport = getTransport();
5478
6201
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
5479
6202
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
5480
- const logDir = path15.join(os8.homedir(), ".exe-os", "session-logs");
5481
- const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
6203
+ const logDir = path16.join(os9.homedir(), ".exe-os", "session-logs");
6204
+ const logFile = path16.join(logDir, `${instanceLabel}-${Date.now()}.log`);
5482
6205
  if (!existsSync13(logDir)) {
5483
6206
  mkdirSync6(logDir, { recursive: true });
5484
6207
  }
@@ -5486,14 +6209,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5486
6209
  let cleanupSuffix = "";
5487
6210
  try {
5488
6211
  const thisFile = fileURLToPath2(import.meta.url);
5489
- const cleanupScript = path15.join(path15.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
6212
+ const cleanupScript = path16.join(path16.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
5490
6213
  if (existsSync13(cleanupScript)) {
5491
6214
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
5492
6215
  }
5493
6216
  } catch {
5494
6217
  }
5495
6218
  try {
5496
- const claudeJsonPath = path15.join(os8.homedir(), ".claude.json");
6219
+ const claudeJsonPath = path16.join(os9.homedir(), ".claude.json");
5497
6220
  let claudeJson = {};
5498
6221
  try {
5499
6222
  claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
@@ -5508,10 +6231,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5508
6231
  } catch {
5509
6232
  }
5510
6233
  try {
5511
- const settingsDir = path15.join(os8.homedir(), ".claude", "projects");
6234
+ const settingsDir = path16.join(os9.homedir(), ".claude", "projects");
5512
6235
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
5513
- const projSettingsDir = path15.join(settingsDir, normalizedKey);
5514
- const settingsPath = path15.join(projSettingsDir, "settings.json");
6236
+ const projSettingsDir = path16.join(settingsDir, normalizedKey);
6237
+ const settingsPath = path16.join(projSettingsDir, "settings.json");
5515
6238
  let settings = {};
5516
6239
  try {
5517
6240
  settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
@@ -5558,8 +6281,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5558
6281
  let behaviorsFlag = "";
5559
6282
  let legacyFallbackWarned = false;
5560
6283
  if (!useExeAgent && !useBinSymlink) {
5561
- const identityPath = path15.join(
5562
- os8.homedir(),
6284
+ const identityPath = path16.join(
6285
+ os9.homedir(),
5563
6286
  ".exe-os",
5564
6287
  "identity",
5565
6288
  `${employeeName}.md`
@@ -5574,7 +6297,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5574
6297
  }
5575
6298
  const behaviorsFile = exportBehaviorsSync(
5576
6299
  employeeName,
5577
- path15.basename(spawnCwd),
6300
+ path16.basename(spawnCwd),
5578
6301
  sessionName
5579
6302
  );
5580
6303
  if (behaviorsFile) {
@@ -5589,9 +6312,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5589
6312
  }
5590
6313
  let sessionContextFlag = "";
5591
6314
  try {
5592
- const ctxDir = path15.join(os8.homedir(), ".exe-os", "session-cache");
6315
+ const ctxDir = path16.join(os9.homedir(), ".exe-os", "session-cache");
5593
6316
  mkdirSync6(ctxDir, { recursive: true });
5594
- const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
6317
+ const ctxFile = path16.join(ctxDir, `session-context-${sessionName}.md`);
5595
6318
  const ctxContent = [
5596
6319
  `## Session Context`,
5597
6320
  `You are running in tmux session: ${sessionName}.`,
@@ -5675,7 +6398,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5675
6398
  transport.pipeLog(sessionName, logFile);
5676
6399
  try {
5677
6400
  const mySession = getMySession();
5678
- const dispatchInfo = path15.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
6401
+ const dispatchInfo = path16.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5679
6402
  writeFileSync7(dispatchInfo, JSON.stringify({
5680
6403
  dispatchedBy: mySession,
5681
6404
  rootExe: exeSession,
@@ -5750,15 +6473,15 @@ var init_tmux_routing = __esm({
5750
6473
  init_intercom_queue();
5751
6474
  init_plan_limits();
5752
6475
  init_employees();
5753
- SPAWN_LOCK_DIR = path15.join(os8.homedir(), ".exe-os", "spawn-locks");
5754
- SESSION_CACHE = path15.join(os8.homedir(), ".exe-os", "session-cache");
6476
+ SPAWN_LOCK_DIR = path16.join(os9.homedir(), ".exe-os", "spawn-locks");
6477
+ SESSION_CACHE = path16.join(os9.homedir(), ".exe-os", "session-cache");
5755
6478
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
5756
6479
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
5757
6480
  VERIFY_PANE_LINES = 200;
5758
6481
  INTERCOM_DEBOUNCE_MS = 3e4;
5759
6482
  CODEX_DEBOUNCE_MS = 12e4;
5760
- INTERCOM_LOG2 = path15.join(os8.homedir(), ".exe-os", "intercom.log");
5761
- DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
6483
+ INTERCOM_LOG2 = path16.join(os9.homedir(), ".exe-os", "intercom.log");
6484
+ DEBOUNCE_FILE = path16.join(SESSION_CACHE, "intercom-debounce.json");
5762
6485
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
5763
6486
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
5764
6487
  }
@@ -7644,10 +8367,10 @@ var init_hooks = __esm({
7644
8367
  });
7645
8368
 
7646
8369
  // src/runtime/safety-checks.ts
7647
- import path16 from "path";
7648
- import os9 from "os";
8370
+ import path17 from "path";
8371
+ import os10 from "os";
7649
8372
  function checkPathSafety(filePath) {
7650
- const resolved = path16.resolve(filePath);
8373
+ const resolved = path17.resolve(filePath);
7651
8374
  for (const { pattern, reason } of BYPASS_IMMUNE_PATTERNS) {
7652
8375
  const matches = typeof pattern === "function" ? pattern(resolved) : pattern.test(resolved);
7653
8376
  if (matches) {
@@ -7657,7 +8380,7 @@ function checkPathSafety(filePath) {
7657
8380
  return { safe: true, bypassImmune: true };
7658
8381
  }
7659
8382
  function checkReadPathSafety(filePath) {
7660
- const resolved = path16.resolve(filePath);
8383
+ const resolved = path17.resolve(filePath);
7661
8384
  const credPatterns = BYPASS_IMMUNE_PATTERNS.filter(
7662
8385
  (p) => typeof p.pattern !== "function" && (p.reason.includes("secrets") || p.reason.includes("Private key") || p.reason.includes("Credential"))
7663
8386
  );
@@ -7672,7 +8395,7 @@ var HOME, BYPASS_IMMUNE_PATTERNS;
7672
8395
  var init_safety_checks = __esm({
7673
8396
  "src/runtime/safety-checks.ts"() {
7674
8397
  "use strict";
7675
- HOME = os9.homedir();
8398
+ HOME = os10.homedir();
7676
8399
  BYPASS_IMMUNE_PATTERNS = [
7677
8400
  {
7678
8401
  pattern: /\/\.git\/hooks\//,
@@ -7683,11 +8406,11 @@ var init_safety_checks = __esm({
7683
8406
  reason: "Git config can set hooks and command execution"
7684
8407
  },
7685
8408
  {
7686
- pattern: (p) => p.startsWith(path16.join(HOME, ".claude")),
8409
+ pattern: (p) => p.startsWith(path17.join(HOME, ".claude")),
7687
8410
  reason: "Claude configuration files are protected"
7688
8411
  },
7689
8412
  {
7690
- pattern: (p) => p.startsWith(path16.join(HOME, ".exe-os")),
8413
+ pattern: (p) => p.startsWith(path17.join(HOME, ".exe-os")),
7691
8414
  reason: "exe-os configuration files are protected"
7692
8415
  },
7693
8416
  {
@@ -7704,7 +8427,7 @@ var init_safety_checks = __esm({
7704
8427
  },
7705
8428
  {
7706
8429
  pattern: (p) => {
7707
- const name = path16.basename(p);
8430
+ const name = path17.basename(p);
7708
8431
  return [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile", ".zshenv"].includes(name);
7709
8432
  },
7710
8433
  reason: "Shell configuration files can execute arbitrary code on login"
@@ -7731,7 +8454,7 @@ __export(file_read_exports, {
7731
8454
  FileReadTool: () => FileReadTool
7732
8455
  });
7733
8456
  import fs3 from "fs/promises";
7734
- import path17 from "path";
8457
+ import path18 from "path";
7735
8458
  import { z } from "zod";
7736
8459
  function isBinary(buf) {
7737
8460
  for (let i = 0; i < buf.length; i++) {
@@ -7767,7 +8490,7 @@ var init_file_read = __esm({
7767
8490
  return { behavior: "allow" };
7768
8491
  },
7769
8492
  async call(input, context) {
7770
- const filePath = path17.isAbsolute(input.file_path) ? input.file_path : path17.resolve(context.cwd, input.file_path);
8493
+ const filePath = path18.isAbsolute(input.file_path) ? input.file_path : path18.resolve(context.cwd, input.file_path);
7771
8494
  let stat;
7772
8495
  try {
7773
8496
  stat = await fs3.stat(filePath);
@@ -7807,7 +8530,7 @@ __export(glob_exports, {
7807
8530
  GlobTool: () => GlobTool
7808
8531
  });
7809
8532
  import fs4 from "fs/promises";
7810
- import path18 from "path";
8533
+ import path19 from "path";
7811
8534
  import { z as z2 } from "zod";
7812
8535
  async function walkDir(dir, maxDepth = 10) {
7813
8536
  const results = [];
@@ -7823,7 +8546,7 @@ async function walkDir(dir, maxDepth = 10) {
7823
8546
  if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
7824
8547
  continue;
7825
8548
  }
7826
- const fullPath = path18.join(current, entry.name);
8549
+ const fullPath = path19.join(current, entry.name);
7827
8550
  if (entry.isDirectory()) {
7828
8551
  await walk(fullPath, depth + 1);
7829
8552
  } else {
@@ -7857,11 +8580,11 @@ var init_glob = __esm({
7857
8580
  inputSchema: inputSchema2,
7858
8581
  isReadOnly: true,
7859
8582
  async call(input, context) {
7860
- const baseDir = input.path ? path18.isAbsolute(input.path) ? input.path : path18.resolve(context.cwd, input.path) : context.cwd;
8583
+ const baseDir = input.path ? path19.isAbsolute(input.path) ? input.path : path19.resolve(context.cwd, input.path) : context.cwd;
7861
8584
  try {
7862
8585
  const entries = await walkDir(baseDir);
7863
8586
  const matched = entries.filter(
7864
- (e) => simpleGlobMatch(path18.relative(baseDir, e.path), input.pattern)
8587
+ (e) => simpleGlobMatch(path19.relative(baseDir, e.path), input.pattern)
7865
8588
  );
7866
8589
  matched.sort((a, b) => b.mtime - a.mtime);
7867
8590
  if (matched.length === 0) {
@@ -7887,7 +8610,7 @@ __export(grep_exports, {
7887
8610
  });
7888
8611
  import { spawn as spawn2 } from "child_process";
7889
8612
  import fs5 from "fs/promises";
7890
- import path19 from "path";
8613
+ import path20 from "path";
7891
8614
  import { z as z3 } from "zod";
7892
8615
  function runRipgrep(input, searchPath, context) {
7893
8616
  return new Promise((resolve, reject) => {
@@ -7941,7 +8664,7 @@ async function nodeGrep(input, searchPath) {
7941
8664
  }
7942
8665
  for (const entry of entries) {
7943
8666
  if (entry.name === "node_modules" || entry.name === ".git") continue;
7944
- const fullPath = path19.join(dir, entry.name);
8667
+ const fullPath = path20.join(dir, entry.name);
7945
8668
  if (entry.isDirectory()) {
7946
8669
  await walk(fullPath);
7947
8670
  } else {
@@ -7987,7 +8710,7 @@ var init_grep = __esm({
7987
8710
  inputSchema: inputSchema3,
7988
8711
  isReadOnly: true,
7989
8712
  async call(input, context) {
7990
- const searchPath = input.path ? path19.isAbsolute(input.path) ? input.path : path19.resolve(context.cwd, input.path) : context.cwd;
8713
+ const searchPath = input.path ? path20.isAbsolute(input.path) ? input.path : path20.resolve(context.cwd, input.path) : context.cwd;
7991
8714
  try {
7992
8715
  const result = await runRipgrep(input, searchPath, context);
7993
8716
  return result;
@@ -8012,7 +8735,7 @@ __export(file_write_exports, {
8012
8735
  FileWriteTool: () => FileWriteTool
8013
8736
  });
8014
8737
  import fs6 from "fs/promises";
8015
- import path20 from "path";
8738
+ import path21 from "path";
8016
8739
  import { z as z4 } from "zod";
8017
8740
  var inputSchema4, FileWriteTool;
8018
8741
  var init_file_write = __esm({
@@ -8040,8 +8763,8 @@ var init_file_write = __esm({
8040
8763
  return { behavior: "allow" };
8041
8764
  },
8042
8765
  async call(input, context) {
8043
- const filePath = path20.isAbsolute(input.file_path) ? input.file_path : path20.resolve(context.cwd, input.file_path);
8044
- const dir = path20.dirname(filePath);
8766
+ const filePath = path21.isAbsolute(input.file_path) ? input.file_path : path21.resolve(context.cwd, input.file_path);
8767
+ const dir = path21.dirname(filePath);
8045
8768
  await fs6.mkdir(dir, { recursive: true });
8046
8769
  await fs6.writeFile(filePath, input.content, "utf-8");
8047
8770
  return {
@@ -8059,7 +8782,7 @@ __export(file_edit_exports, {
8059
8782
  FileEditTool: () => FileEditTool
8060
8783
  });
8061
8784
  import fs7 from "fs/promises";
8062
- import path21 from "path";
8785
+ import path22 from "path";
8063
8786
  import { z as z5 } from "zod";
8064
8787
  function countOccurrences(haystack, needle) {
8065
8788
  let count = 0;
@@ -8100,7 +8823,7 @@ var init_file_edit = __esm({
8100
8823
  return { behavior: "allow" };
8101
8824
  },
8102
8825
  async call(input, context) {
8103
- const filePath = path21.isAbsolute(input.file_path) ? input.file_path : path21.resolve(context.cwd, input.file_path);
8826
+ const filePath = path22.isAbsolute(input.file_path) ? input.file_path : path22.resolve(context.cwd, input.file_path);
8104
8827
  let content;
8105
8828
  try {
8106
8829
  content = await fs7.readFile(filePath, "utf-8");
@@ -8766,13 +9489,13 @@ __export(keychain_exports, {
8766
9489
  });
8767
9490
  import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
8768
9491
  import { existsSync as existsSync14 } from "fs";
8769
- import path24 from "path";
8770
- import os10 from "os";
9492
+ import path25 from "path";
9493
+ import os11 from "os";
8771
9494
  function getKeyDir() {
8772
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path24.join(os10.homedir(), ".exe-os");
9495
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path25.join(os11.homedir(), ".exe-os");
8773
9496
  }
8774
9497
  function getKeyPath() {
8775
- return path24.join(getKeyDir(), "master.key");
9498
+ return path25.join(getKeyDir(), "master.key");
8776
9499
  }
8777
9500
  async function tryKeytar() {
8778
9501
  try {
@@ -8795,7 +9518,7 @@ async function getMasterKey() {
8795
9518
  const keyPath = getKeyPath();
8796
9519
  if (!existsSync14(keyPath)) {
8797
9520
  process.stderr.write(
8798
- `[keychain] Key not found at ${keyPath} (HOME=${os10.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
9521
+ `[keychain] Key not found at ${keyPath} (HOME=${os11.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
8799
9522
  `
8800
9523
  );
8801
9524
  return null;
@@ -9136,8 +9859,8 @@ __export(wiki_client_exports, {
9136
9859
  listDocuments: () => listDocuments,
9137
9860
  listWorkspaces: () => listWorkspaces
9138
9861
  });
9139
- async function wikiFetch(config, path26, method = "GET", body) {
9140
- const url = `${config.baseUrl}/api/v1${path26}`;
9862
+ async function wikiFetch(config, path27, method = "GET", body) {
9863
+ const url = `${config.baseUrl}/api/v1${path27}`;
9141
9864
  const headers = {
9142
9865
  Authorization: `Bearer ${config.apiKey}`,
9143
9866
  "Content-Type": "application/json"
@@ -9170,7 +9893,7 @@ async function wikiFetch(config, path26, method = "GET", body) {
9170
9893
  }
9171
9894
  }
9172
9895
  if (!response.ok) {
9173
- throw new Error(`Wiki API ${method} ${path26}: ${response.status} ${response.statusText}`);
9896
+ throw new Error(`Wiki API ${method} ${path27}: ${response.status} ${response.statusText}`);
9174
9897
  }
9175
9898
  return response.json();
9176
9899
  } finally {
@@ -9291,7 +10014,7 @@ __export(shard_manager_exports, {
9291
10014
  listShards: () => listShards,
9292
10015
  shardExists: () => shardExists
9293
10016
  });
9294
- import path25 from "path";
10017
+ import path26 from "path";
9295
10018
  import { existsSync as existsSync15, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
9296
10019
  import { createClient as createClient2 } from "@libsql/client";
9297
10020
  function initShardManager(encryptionKey) {
@@ -9317,7 +10040,7 @@ function getShardClient(projectName) {
9317
10040
  }
9318
10041
  const cached = _shards.get(safeName);
9319
10042
  if (cached) return cached;
9320
- const dbPath = path25.join(SHARDS_DIR, `${safeName}.db`);
10043
+ const dbPath = path26.join(SHARDS_DIR, `${safeName}.db`);
9321
10044
  const client = createClient2({
9322
10045
  url: `file:${dbPath}`,
9323
10046
  encryptionKey: _encryptionKey
@@ -9327,7 +10050,7 @@ function getShardClient(projectName) {
9327
10050
  }
9328
10051
  function shardExists(projectName) {
9329
10052
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
9330
- return existsSync15(path25.join(SHARDS_DIR, `${safeName}.db`));
10053
+ return existsSync15(path26.join(SHARDS_DIR, `${safeName}.db`));
9331
10054
  }
9332
10055
  function listShards() {
9333
10056
  if (!existsSync15(SHARDS_DIR)) return [];
@@ -9404,7 +10127,23 @@ async function ensureShardSchema(client) {
9404
10127
  // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
9405
10128
  "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
9406
10129
  "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
9407
- "ALTER TABLE memories ADD COLUMN trajectory TEXT"
10130
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT",
10131
+ // Metadata enrichment columns (must match database.ts)
10132
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
10133
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
10134
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
10135
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
10136
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
10137
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
10138
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
10139
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
10140
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
10141
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
10142
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
10143
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
10144
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
10145
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
10146
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
9408
10147
  ]) {
9409
10148
  try {
9410
10149
  await client.execute(col);
@@ -9516,7 +10255,7 @@ var init_shard_manager = __esm({
9516
10255
  "src/lib/shard-manager.ts"() {
9517
10256
  "use strict";
9518
10257
  init_config();
9519
- SHARDS_DIR = path25.join(EXE_AI_DIR, "shards");
10258
+ SHARDS_DIR = path26.join(EXE_AI_DIR, "shards");
9520
10259
  _shards = /* @__PURE__ */ new Map();
9521
10260
  _encryptionKey = null;
9522
10261
  _shardingEnabled = false;
@@ -14088,8 +14827,8 @@ function Text({ color, backgroundColor, dimColor = false, bold = false, italic =
14088
14827
  }
14089
14828
 
14090
14829
  // src/tui/ink/components/ErrorOverview.js
14091
- var cleanupPath = (path26) => {
14092
- return path26?.replace(`file://${cwd()}/`, "");
14830
+ var cleanupPath = (path27) => {
14831
+ return path27?.replace(`file://${cwd()}/`, "");
14093
14832
  };
14094
14833
  var stackUtils = new StackUtils({
14095
14834
  cwd: cwd(),
@@ -16107,7 +16846,7 @@ function Footer() {
16107
16846
  // src/tui/views/CommandCenter.tsx
16108
16847
  import { useState as useState6, useEffect as useEffect8, useMemo as useMemo4, useCallback as useCallback4, useRef as useRef4 } from "react";
16109
16848
  import TextInput from "ink-text-input";
16110
- import path22 from "path";
16849
+ import path23 from "path";
16111
16850
  import { homedir } from "os";
16112
16851
 
16113
16852
  // src/tui/components/StatusDot.tsx
@@ -17108,7 +17847,7 @@ function CommandCenterView({
17108
17847
  const demoEntries = DEMO_PROJECTS.map((p) => ({
17109
17848
  projectName: p.projectName,
17110
17849
  exeSession: p.exeSession,
17111
- projectDir: path22.join(homedir(), p.projectName),
17850
+ projectDir: path23.join(homedir(), p.projectName),
17112
17851
  employeeCount: p.employees.length,
17113
17852
  activeCount: p.employees.filter((e) => e.status === "active").length,
17114
17853
  memoryCount: p.employees.length * 4e3,
@@ -17457,7 +18196,7 @@ function ChatMessageRow({ msg }) {
17457
18196
 
17458
18197
  // src/tui/views/Sessions.tsx
17459
18198
  import React19, { useState as useState9, useEffect as useEffect11, useCallback as useCallback6 } from "react";
17460
- import path23 from "path";
18199
+ import path24 from "path";
17461
18200
  import { homedir as homedir2 } from "os";
17462
18201
 
17463
18202
  // src/tui/components/TmuxPane.tsx
@@ -17761,7 +18500,7 @@ function SessionsView({
17761
18500
  if (demo) {
17762
18501
  setProjects(DEMO_PROJECTS.map((p) => ({
17763
18502
  ...p,
17764
- projectDir: path23.join(homedir2(), p.projectName),
18503
+ projectDir: path24.join(homedir2(), p.projectName),
17765
18504
  employees: p.employees.map((e) => ({ ...e, attached: e.status === "active" }))
17766
18505
  })));
17767
18506
  return;
@@ -19882,8 +20621,8 @@ function SettingsView({ onBack }) {
19882
20621
  let version = "unknown";
19883
20622
  try {
19884
20623
  const { readFileSync: readFileSync13 } = await import("fs");
19885
- const { createRequire } = await import("module");
19886
- const require2 = createRequire(import.meta.url);
20624
+ const { createRequire: createRequire2 } = await import("module");
20625
+ const require2 = createRequire2(import.meta.url);
19887
20626
  const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
19888
20627
  const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
19889
20628
  version = pkg.version;