@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
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;
@@ -5342,7 +6056,7 @@ function sendIntercom(targetSession) {
5342
6056
  try {
5343
6057
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
5344
6058
  const agent = baseAgentName(rawAgent);
5345
- const markerPath = path15.join(SESSION_CACHE, `current-task-${agent}.json`);
6059
+ const markerPath = path16.join(SESSION_CACHE, `current-task-${agent}.json`);
5346
6060
  if (existsSync13(markerPath)) {
5347
6061
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
5348
6062
  return "debounced";
@@ -5352,7 +6066,7 @@ function sendIntercom(targetSession) {
5352
6066
  try {
5353
6067
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
5354
6068
  const agent = baseAgentName(rawAgent);
5355
- const taskDir = path15.join(process.cwd(), "exe", agent);
6069
+ const taskDir = path16.join(process.cwd(), "exe", agent);
5356
6070
  if (existsSync13(taskDir)) {
5357
6071
  const files = readdirSync3(taskDir).filter(
5358
6072
  (f) => f.endsWith(".md") && f !== "DONE.txt"
@@ -5486,8 +6200,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5486
6200
  const transport = getTransport();
5487
6201
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
5488
6202
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
5489
- const logDir = path15.join(os8.homedir(), ".exe-os", "session-logs");
5490
- 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`);
5491
6205
  if (!existsSync13(logDir)) {
5492
6206
  mkdirSync6(logDir, { recursive: true });
5493
6207
  }
@@ -5495,14 +6209,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5495
6209
  let cleanupSuffix = "";
5496
6210
  try {
5497
6211
  const thisFile = fileURLToPath2(import.meta.url);
5498
- 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");
5499
6213
  if (existsSync13(cleanupScript)) {
5500
6214
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
5501
6215
  }
5502
6216
  } catch {
5503
6217
  }
5504
6218
  try {
5505
- const claudeJsonPath = path15.join(os8.homedir(), ".claude.json");
6219
+ const claudeJsonPath = path16.join(os9.homedir(), ".claude.json");
5506
6220
  let claudeJson = {};
5507
6221
  try {
5508
6222
  claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
@@ -5517,10 +6231,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5517
6231
  } catch {
5518
6232
  }
5519
6233
  try {
5520
- const settingsDir = path15.join(os8.homedir(), ".claude", "projects");
6234
+ const settingsDir = path16.join(os9.homedir(), ".claude", "projects");
5521
6235
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
5522
- const projSettingsDir = path15.join(settingsDir, normalizedKey);
5523
- const settingsPath = path15.join(projSettingsDir, "settings.json");
6236
+ const projSettingsDir = path16.join(settingsDir, normalizedKey);
6237
+ const settingsPath = path16.join(projSettingsDir, "settings.json");
5524
6238
  let settings = {};
5525
6239
  try {
5526
6240
  settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
@@ -5567,8 +6281,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5567
6281
  let behaviorsFlag = "";
5568
6282
  let legacyFallbackWarned = false;
5569
6283
  if (!useExeAgent && !useBinSymlink) {
5570
- const identityPath = path15.join(
5571
- os8.homedir(),
6284
+ const identityPath = path16.join(
6285
+ os9.homedir(),
5572
6286
  ".exe-os",
5573
6287
  "identity",
5574
6288
  `${employeeName}.md`
@@ -5583,7 +6297,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5583
6297
  }
5584
6298
  const behaviorsFile = exportBehaviorsSync(
5585
6299
  employeeName,
5586
- path15.basename(spawnCwd),
6300
+ path16.basename(spawnCwd),
5587
6301
  sessionName
5588
6302
  );
5589
6303
  if (behaviorsFile) {
@@ -5598,9 +6312,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5598
6312
  }
5599
6313
  let sessionContextFlag = "";
5600
6314
  try {
5601
- const ctxDir = path15.join(os8.homedir(), ".exe-os", "session-cache");
6315
+ const ctxDir = path16.join(os9.homedir(), ".exe-os", "session-cache");
5602
6316
  mkdirSync6(ctxDir, { recursive: true });
5603
- const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
6317
+ const ctxFile = path16.join(ctxDir, `session-context-${sessionName}.md`);
5604
6318
  const ctxContent = [
5605
6319
  `## Session Context`,
5606
6320
  `You are running in tmux session: ${sessionName}.`,
@@ -5684,7 +6398,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5684
6398
  transport.pipeLog(sessionName, logFile);
5685
6399
  try {
5686
6400
  const mySession = getMySession();
5687
- const dispatchInfo = path15.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
6401
+ const dispatchInfo = path16.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5688
6402
  writeFileSync7(dispatchInfo, JSON.stringify({
5689
6403
  dispatchedBy: mySession,
5690
6404
  rootExe: exeSession,
@@ -5759,15 +6473,15 @@ var init_tmux_routing = __esm({
5759
6473
  init_intercom_queue();
5760
6474
  init_plan_limits();
5761
6475
  init_employees();
5762
- SPAWN_LOCK_DIR = path15.join(os8.homedir(), ".exe-os", "spawn-locks");
5763
- 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");
5764
6478
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
5765
6479
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
5766
6480
  VERIFY_PANE_LINES = 200;
5767
6481
  INTERCOM_DEBOUNCE_MS = 3e4;
5768
6482
  CODEX_DEBOUNCE_MS = 12e4;
5769
- INTERCOM_LOG2 = path15.join(os8.homedir(), ".exe-os", "intercom.log");
5770
- 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");
5771
6485
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
5772
6486
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
5773
6487
  }
@@ -7653,10 +8367,10 @@ var init_hooks = __esm({
7653
8367
  });
7654
8368
 
7655
8369
  // src/runtime/safety-checks.ts
7656
- import path16 from "path";
7657
- import os9 from "os";
8370
+ import path17 from "path";
8371
+ import os10 from "os";
7658
8372
  function checkPathSafety(filePath) {
7659
- const resolved = path16.resolve(filePath);
8373
+ const resolved = path17.resolve(filePath);
7660
8374
  for (const { pattern, reason } of BYPASS_IMMUNE_PATTERNS) {
7661
8375
  const matches = typeof pattern === "function" ? pattern(resolved) : pattern.test(resolved);
7662
8376
  if (matches) {
@@ -7666,7 +8380,7 @@ function checkPathSafety(filePath) {
7666
8380
  return { safe: true, bypassImmune: true };
7667
8381
  }
7668
8382
  function checkReadPathSafety(filePath) {
7669
- const resolved = path16.resolve(filePath);
8383
+ const resolved = path17.resolve(filePath);
7670
8384
  const credPatterns = BYPASS_IMMUNE_PATTERNS.filter(
7671
8385
  (p) => typeof p.pattern !== "function" && (p.reason.includes("secrets") || p.reason.includes("Private key") || p.reason.includes("Credential"))
7672
8386
  );
@@ -7681,7 +8395,7 @@ var HOME, BYPASS_IMMUNE_PATTERNS;
7681
8395
  var init_safety_checks = __esm({
7682
8396
  "src/runtime/safety-checks.ts"() {
7683
8397
  "use strict";
7684
- HOME = os9.homedir();
8398
+ HOME = os10.homedir();
7685
8399
  BYPASS_IMMUNE_PATTERNS = [
7686
8400
  {
7687
8401
  pattern: /\/\.git\/hooks\//,
@@ -7692,11 +8406,11 @@ var init_safety_checks = __esm({
7692
8406
  reason: "Git config can set hooks and command execution"
7693
8407
  },
7694
8408
  {
7695
- pattern: (p) => p.startsWith(path16.join(HOME, ".claude")),
8409
+ pattern: (p) => p.startsWith(path17.join(HOME, ".claude")),
7696
8410
  reason: "Claude configuration files are protected"
7697
8411
  },
7698
8412
  {
7699
- pattern: (p) => p.startsWith(path16.join(HOME, ".exe-os")),
8413
+ pattern: (p) => p.startsWith(path17.join(HOME, ".exe-os")),
7700
8414
  reason: "exe-os configuration files are protected"
7701
8415
  },
7702
8416
  {
@@ -7713,7 +8427,7 @@ var init_safety_checks = __esm({
7713
8427
  },
7714
8428
  {
7715
8429
  pattern: (p) => {
7716
- const name = path16.basename(p);
8430
+ const name = path17.basename(p);
7717
8431
  return [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile", ".zshenv"].includes(name);
7718
8432
  },
7719
8433
  reason: "Shell configuration files can execute arbitrary code on login"
@@ -7740,7 +8454,7 @@ __export(file_read_exports, {
7740
8454
  FileReadTool: () => FileReadTool
7741
8455
  });
7742
8456
  import fs3 from "fs/promises";
7743
- import path17 from "path";
8457
+ import path18 from "path";
7744
8458
  import { z } from "zod";
7745
8459
  function isBinary(buf) {
7746
8460
  for (let i = 0; i < buf.length; i++) {
@@ -7776,7 +8490,7 @@ var init_file_read = __esm({
7776
8490
  return { behavior: "allow" };
7777
8491
  },
7778
8492
  async call(input, context) {
7779
- 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);
7780
8494
  let stat;
7781
8495
  try {
7782
8496
  stat = await fs3.stat(filePath);
@@ -7816,7 +8530,7 @@ __export(glob_exports, {
7816
8530
  GlobTool: () => GlobTool
7817
8531
  });
7818
8532
  import fs4 from "fs/promises";
7819
- import path18 from "path";
8533
+ import path19 from "path";
7820
8534
  import { z as z2 } from "zod";
7821
8535
  async function walkDir(dir, maxDepth = 10) {
7822
8536
  const results = [];
@@ -7832,7 +8546,7 @@ async function walkDir(dir, maxDepth = 10) {
7832
8546
  if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
7833
8547
  continue;
7834
8548
  }
7835
- const fullPath = path18.join(current, entry.name);
8549
+ const fullPath = path19.join(current, entry.name);
7836
8550
  if (entry.isDirectory()) {
7837
8551
  await walk(fullPath, depth + 1);
7838
8552
  } else {
@@ -7866,11 +8580,11 @@ var init_glob = __esm({
7866
8580
  inputSchema: inputSchema2,
7867
8581
  isReadOnly: true,
7868
8582
  async call(input, context) {
7869
- 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;
7870
8584
  try {
7871
8585
  const entries = await walkDir(baseDir);
7872
8586
  const matched = entries.filter(
7873
- (e) => simpleGlobMatch(path18.relative(baseDir, e.path), input.pattern)
8587
+ (e) => simpleGlobMatch(path19.relative(baseDir, e.path), input.pattern)
7874
8588
  );
7875
8589
  matched.sort((a, b) => b.mtime - a.mtime);
7876
8590
  if (matched.length === 0) {
@@ -7896,7 +8610,7 @@ __export(grep_exports, {
7896
8610
  });
7897
8611
  import { spawn as spawn2 } from "child_process";
7898
8612
  import fs5 from "fs/promises";
7899
- import path19 from "path";
8613
+ import path20 from "path";
7900
8614
  import { z as z3 } from "zod";
7901
8615
  function runRipgrep(input, searchPath, context) {
7902
8616
  return new Promise((resolve, reject) => {
@@ -7950,7 +8664,7 @@ async function nodeGrep(input, searchPath) {
7950
8664
  }
7951
8665
  for (const entry of entries) {
7952
8666
  if (entry.name === "node_modules" || entry.name === ".git") continue;
7953
- const fullPath = path19.join(dir, entry.name);
8667
+ const fullPath = path20.join(dir, entry.name);
7954
8668
  if (entry.isDirectory()) {
7955
8669
  await walk(fullPath);
7956
8670
  } else {
@@ -7996,7 +8710,7 @@ var init_grep = __esm({
7996
8710
  inputSchema: inputSchema3,
7997
8711
  isReadOnly: true,
7998
8712
  async call(input, context) {
7999
- 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;
8000
8714
  try {
8001
8715
  const result = await runRipgrep(input, searchPath, context);
8002
8716
  return result;
@@ -8021,7 +8735,7 @@ __export(file_write_exports, {
8021
8735
  FileWriteTool: () => FileWriteTool
8022
8736
  });
8023
8737
  import fs6 from "fs/promises";
8024
- import path20 from "path";
8738
+ import path21 from "path";
8025
8739
  import { z as z4 } from "zod";
8026
8740
  var inputSchema4, FileWriteTool;
8027
8741
  var init_file_write = __esm({
@@ -8049,8 +8763,8 @@ var init_file_write = __esm({
8049
8763
  return { behavior: "allow" };
8050
8764
  },
8051
8765
  async call(input, context) {
8052
- const filePath = path20.isAbsolute(input.file_path) ? input.file_path : path20.resolve(context.cwd, input.file_path);
8053
- 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);
8054
8768
  await fs6.mkdir(dir, { recursive: true });
8055
8769
  await fs6.writeFile(filePath, input.content, "utf-8");
8056
8770
  return {
@@ -8068,7 +8782,7 @@ __export(file_edit_exports, {
8068
8782
  FileEditTool: () => FileEditTool
8069
8783
  });
8070
8784
  import fs7 from "fs/promises";
8071
- import path21 from "path";
8785
+ import path22 from "path";
8072
8786
  import { z as z5 } from "zod";
8073
8787
  function countOccurrences(haystack, needle) {
8074
8788
  let count = 0;
@@ -8109,7 +8823,7 @@ var init_file_edit = __esm({
8109
8823
  return { behavior: "allow" };
8110
8824
  },
8111
8825
  async call(input, context) {
8112
- 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);
8113
8827
  let content;
8114
8828
  try {
8115
8829
  content = await fs7.readFile(filePath, "utf-8");
@@ -8775,13 +9489,13 @@ __export(keychain_exports, {
8775
9489
  });
8776
9490
  import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
8777
9491
  import { existsSync as existsSync14 } from "fs";
8778
- import path24 from "path";
8779
- import os10 from "os";
9492
+ import path25 from "path";
9493
+ import os11 from "os";
8780
9494
  function getKeyDir() {
8781
- 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");
8782
9496
  }
8783
9497
  function getKeyPath() {
8784
- return path24.join(getKeyDir(), "master.key");
9498
+ return path25.join(getKeyDir(), "master.key");
8785
9499
  }
8786
9500
  async function tryKeytar() {
8787
9501
  try {
@@ -8804,7 +9518,7 @@ async function getMasterKey() {
8804
9518
  const keyPath = getKeyPath();
8805
9519
  if (!existsSync14(keyPath)) {
8806
9520
  process.stderr.write(
8807
- `[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"})
8808
9522
  `
8809
9523
  );
8810
9524
  return null;
@@ -9145,8 +9859,8 @@ __export(wiki_client_exports, {
9145
9859
  listDocuments: () => listDocuments,
9146
9860
  listWorkspaces: () => listWorkspaces
9147
9861
  });
9148
- async function wikiFetch(config, path26, method = "GET", body) {
9149
- const url = `${config.baseUrl}/api/v1${path26}`;
9862
+ async function wikiFetch(config, path27, method = "GET", body) {
9863
+ const url = `${config.baseUrl}/api/v1${path27}`;
9150
9864
  const headers = {
9151
9865
  Authorization: `Bearer ${config.apiKey}`,
9152
9866
  "Content-Type": "application/json"
@@ -9179,7 +9893,7 @@ async function wikiFetch(config, path26, method = "GET", body) {
9179
9893
  }
9180
9894
  }
9181
9895
  if (!response.ok) {
9182
- throw new Error(`Wiki API ${method} ${path26}: ${response.status} ${response.statusText}`);
9896
+ throw new Error(`Wiki API ${method} ${path27}: ${response.status} ${response.statusText}`);
9183
9897
  }
9184
9898
  return response.json();
9185
9899
  } finally {
@@ -9300,7 +10014,7 @@ __export(shard_manager_exports, {
9300
10014
  listShards: () => listShards,
9301
10015
  shardExists: () => shardExists
9302
10016
  });
9303
- import path25 from "path";
10017
+ import path26 from "path";
9304
10018
  import { existsSync as existsSync15, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
9305
10019
  import { createClient as createClient2 } from "@libsql/client";
9306
10020
  function initShardManager(encryptionKey) {
@@ -9326,7 +10040,7 @@ function getShardClient(projectName) {
9326
10040
  }
9327
10041
  const cached = _shards.get(safeName);
9328
10042
  if (cached) return cached;
9329
- const dbPath = path25.join(SHARDS_DIR, `${safeName}.db`);
10043
+ const dbPath = path26.join(SHARDS_DIR, `${safeName}.db`);
9330
10044
  const client = createClient2({
9331
10045
  url: `file:${dbPath}`,
9332
10046
  encryptionKey: _encryptionKey
@@ -9336,7 +10050,7 @@ function getShardClient(projectName) {
9336
10050
  }
9337
10051
  function shardExists(projectName) {
9338
10052
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
9339
- return existsSync15(path25.join(SHARDS_DIR, `${safeName}.db`));
10053
+ return existsSync15(path26.join(SHARDS_DIR, `${safeName}.db`));
9340
10054
  }
9341
10055
  function listShards() {
9342
10056
  if (!existsSync15(SHARDS_DIR)) return [];
@@ -9413,7 +10127,23 @@ async function ensureShardSchema(client) {
9413
10127
  // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
9414
10128
  "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
9415
10129
  "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
9416
- "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"
9417
10147
  ]) {
9418
10148
  try {
9419
10149
  await client.execute(col);
@@ -9525,7 +10255,7 @@ var init_shard_manager = __esm({
9525
10255
  "src/lib/shard-manager.ts"() {
9526
10256
  "use strict";
9527
10257
  init_config();
9528
- SHARDS_DIR = path25.join(EXE_AI_DIR, "shards");
10258
+ SHARDS_DIR = path26.join(EXE_AI_DIR, "shards");
9529
10259
  _shards = /* @__PURE__ */ new Map();
9530
10260
  _encryptionKey = null;
9531
10261
  _shardingEnabled = false;
@@ -14097,8 +14827,8 @@ function Text({ color, backgroundColor, dimColor = false, bold = false, italic =
14097
14827
  }
14098
14828
 
14099
14829
  // src/tui/ink/components/ErrorOverview.js
14100
- var cleanupPath = (path26) => {
14101
- return path26?.replace(`file://${cwd()}/`, "");
14830
+ var cleanupPath = (path27) => {
14831
+ return path27?.replace(`file://${cwd()}/`, "");
14102
14832
  };
14103
14833
  var stackUtils = new StackUtils({
14104
14834
  cwd: cwd(),
@@ -16116,7 +16846,7 @@ function Footer() {
16116
16846
  // src/tui/views/CommandCenter.tsx
16117
16847
  import { useState as useState6, useEffect as useEffect8, useMemo as useMemo4, useCallback as useCallback4, useRef as useRef4 } from "react";
16118
16848
  import TextInput from "ink-text-input";
16119
- import path22 from "path";
16849
+ import path23 from "path";
16120
16850
  import { homedir } from "os";
16121
16851
 
16122
16852
  // src/tui/components/StatusDot.tsx
@@ -17117,7 +17847,7 @@ function CommandCenterView({
17117
17847
  const demoEntries = DEMO_PROJECTS.map((p) => ({
17118
17848
  projectName: p.projectName,
17119
17849
  exeSession: p.exeSession,
17120
- projectDir: path22.join(homedir(), p.projectName),
17850
+ projectDir: path23.join(homedir(), p.projectName),
17121
17851
  employeeCount: p.employees.length,
17122
17852
  activeCount: p.employees.filter((e) => e.status === "active").length,
17123
17853
  memoryCount: p.employees.length * 4e3,
@@ -17466,7 +18196,7 @@ function ChatMessageRow({ msg }) {
17466
18196
 
17467
18197
  // src/tui/views/Sessions.tsx
17468
18198
  import React19, { useState as useState9, useEffect as useEffect11, useCallback as useCallback6 } from "react";
17469
- import path23 from "path";
18199
+ import path24 from "path";
17470
18200
  import { homedir as homedir2 } from "os";
17471
18201
 
17472
18202
  // src/tui/components/TmuxPane.tsx
@@ -17770,7 +18500,7 @@ function SessionsView({
17770
18500
  if (demo) {
17771
18501
  setProjects(DEMO_PROJECTS.map((p) => ({
17772
18502
  ...p,
17773
- projectDir: path23.join(homedir2(), p.projectName),
18503
+ projectDir: path24.join(homedir2(), p.projectName),
17774
18504
  employees: p.employees.map((e) => ({ ...e, attached: e.status === "active" }))
17775
18505
  })));
17776
18506
  return;
@@ -19891,8 +20621,8 @@ function SettingsView({ onBack }) {
19891
20621
  let version = "unknown";
19892
20622
  try {
19893
20623
  const { readFileSync: readFileSync13 } = await import("fs");
19894
- const { createRequire } = await import("module");
19895
- const require2 = createRequire(import.meta.url);
20624
+ const { createRequire: createRequire2 } = await import("module");
20625
+ const require2 = createRequire2(import.meta.url);
19896
20626
  const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
19897
20627
  const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
19898
20628
  version = pkg.version;