@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
@@ -779,7 +779,7 @@ function isMultiInstance(agentName, employees) {
779
779
  if (!emp) return false;
780
780
  return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
781
781
  }
782
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
782
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR;
783
783
  var init_employees = __esm({
784
784
  "src/lib/employees.ts"() {
785
785
  "use strict";
@@ -788,16 +788,601 @@ var init_employees = __esm({
788
788
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
789
789
  COORDINATOR_ROLE = "COO";
790
790
  MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
791
+ IDENTITY_DIR = path5.join(EXE_AI_DIR, "identity");
792
+ }
793
+ });
794
+
795
+ // src/lib/database-adapter.ts
796
+ import os5 from "os";
797
+ import path6 from "path";
798
+ import { createRequire } from "module";
799
+ import { pathToFileURL } from "url";
800
+ function quotedIdentifier(identifier) {
801
+ return `"${identifier.replace(/"/g, '""')}"`;
802
+ }
803
+ function unqualifiedTableName(name) {
804
+ const raw = name.trim().replace(/^"|"$/g, "");
805
+ const parts = raw.split(".");
806
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
807
+ }
808
+ function stripTrailingSemicolon(sql) {
809
+ return sql.trim().replace(/;+\s*$/u, "");
810
+ }
811
+ function appendClause(sql, clause) {
812
+ const trimmed = stripTrailingSemicolon(sql);
813
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
814
+ if (!returningMatch) {
815
+ return `${trimmed}${clause}`;
816
+ }
817
+ const idx = returningMatch.index;
818
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
819
+ }
820
+ function normalizeStatement(stmt) {
821
+ if (typeof stmt === "string") {
822
+ return { kind: "positional", sql: stmt, args: [] };
823
+ }
824
+ const sql = stmt.sql;
825
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
826
+ return { kind: "positional", sql, args: stmt.args ?? [] };
827
+ }
828
+ return { kind: "named", sql, args: stmt.args };
829
+ }
830
+ function rewriteBooleanLiterals(sql) {
831
+ let out = sql;
832
+ for (const column of BOOLEAN_COLUMN_NAMES) {
833
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
834
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
835
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
836
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
837
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
838
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
839
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
840
+ }
841
+ return out;
842
+ }
843
+ function rewriteInsertOrIgnore(sql) {
844
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
845
+ return sql;
846
+ }
847
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
848
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
849
+ }
850
+ function rewriteInsertOrReplace(sql) {
851
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
852
+ if (!match) {
853
+ return sql;
854
+ }
855
+ const rawTable = match[1];
856
+ const rawColumns = match[2];
857
+ const remainder = match[3];
858
+ const tableName = unqualifiedTableName(rawTable);
859
+ const conflictKeys = UPSERT_KEYS[tableName];
860
+ if (!conflictKeys?.length) {
861
+ return sql;
862
+ }
863
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
864
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
865
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
866
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
867
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
868
+ }
869
+ function rewriteSql(sql) {
870
+ let out = sql;
871
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
872
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
873
+ out = rewriteBooleanLiterals(out);
874
+ out = rewriteInsertOrReplace(out);
875
+ out = rewriteInsertOrIgnore(out);
876
+ return stripTrailingSemicolon(out);
877
+ }
878
+ function toBoolean(value) {
879
+ if (value === null || value === void 0) return value;
880
+ if (typeof value === "boolean") return value;
881
+ if (typeof value === "number") return value !== 0;
882
+ if (typeof value === "bigint") return value !== 0n;
883
+ if (typeof value === "string") {
884
+ const normalized = value.trim().toLowerCase();
885
+ if (normalized === "0" || normalized === "false") return false;
886
+ if (normalized === "1" || normalized === "true") return true;
887
+ }
888
+ return Boolean(value);
889
+ }
890
+ function countQuestionMarks(sql, end) {
891
+ let count = 0;
892
+ let inSingle = false;
893
+ let inDouble = false;
894
+ let inLineComment = false;
895
+ let inBlockComment = false;
896
+ for (let i = 0; i < end; i++) {
897
+ const ch = sql[i];
898
+ const next = sql[i + 1];
899
+ if (inLineComment) {
900
+ if (ch === "\n") inLineComment = false;
901
+ continue;
902
+ }
903
+ if (inBlockComment) {
904
+ if (ch === "*" && next === "/") {
905
+ inBlockComment = false;
906
+ i += 1;
907
+ }
908
+ continue;
909
+ }
910
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
911
+ inLineComment = true;
912
+ i += 1;
913
+ continue;
914
+ }
915
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
916
+ inBlockComment = true;
917
+ i += 1;
918
+ continue;
919
+ }
920
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
921
+ inSingle = !inSingle;
922
+ continue;
923
+ }
924
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
925
+ inDouble = !inDouble;
926
+ continue;
927
+ }
928
+ if (!inSingle && !inDouble && ch === "?") {
929
+ count += 1;
930
+ }
931
+ }
932
+ return count;
933
+ }
934
+ function findBooleanPlaceholderIndexes(sql) {
935
+ const indexes = /* @__PURE__ */ new Set();
936
+ for (const column of BOOLEAN_COLUMN_NAMES) {
937
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
938
+ for (const match of sql.matchAll(pattern)) {
939
+ const matchText = match[0];
940
+ const qIndex = match.index + matchText.lastIndexOf("?");
941
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
942
+ }
943
+ }
944
+ return indexes;
945
+ }
946
+ function coerceInsertBooleanArgs(sql, args) {
947
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
948
+ if (!match) return;
949
+ const rawTable = match[1];
950
+ const rawColumns = match[2];
951
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
952
+ if (!boolColumns?.size) return;
953
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
954
+ for (const [index, column] of columns.entries()) {
955
+ if (boolColumns.has(column) && index < args.length) {
956
+ args[index] = toBoolean(args[index]);
957
+ }
958
+ }
959
+ }
960
+ function coerceUpdateBooleanArgs(sql, args) {
961
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
962
+ if (!match) return;
963
+ const rawTable = match[1];
964
+ const setClause = match[2];
965
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
966
+ if (!boolColumns?.size) return;
967
+ const assignments = setClause.split(",");
968
+ let placeholderIndex = 0;
969
+ for (const assignment of assignments) {
970
+ if (!assignment.includes("?")) continue;
971
+ placeholderIndex += 1;
972
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
973
+ if (colMatch && boolColumns.has(colMatch[1])) {
974
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
975
+ }
976
+ }
977
+ }
978
+ function coerceBooleanArgs(sql, args) {
979
+ const nextArgs = [...args];
980
+ coerceInsertBooleanArgs(sql, nextArgs);
981
+ coerceUpdateBooleanArgs(sql, nextArgs);
982
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
983
+ for (const index of placeholderIndexes) {
984
+ if (index > 0 && index <= nextArgs.length) {
985
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
986
+ }
987
+ }
988
+ return nextArgs;
989
+ }
990
+ function convertQuestionMarksToDollarParams(sql) {
991
+ let out = "";
992
+ let placeholder = 0;
993
+ let inSingle = false;
994
+ let inDouble = false;
995
+ let inLineComment = false;
996
+ let inBlockComment = false;
997
+ for (let i = 0; i < sql.length; i++) {
998
+ const ch = sql[i];
999
+ const next = sql[i + 1];
1000
+ if (inLineComment) {
1001
+ out += ch;
1002
+ if (ch === "\n") inLineComment = false;
1003
+ continue;
1004
+ }
1005
+ if (inBlockComment) {
1006
+ out += ch;
1007
+ if (ch === "*" && next === "/") {
1008
+ out += next;
1009
+ inBlockComment = false;
1010
+ i += 1;
1011
+ }
1012
+ continue;
1013
+ }
1014
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1015
+ out += ch + next;
1016
+ inLineComment = true;
1017
+ i += 1;
1018
+ continue;
1019
+ }
1020
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1021
+ out += ch + next;
1022
+ inBlockComment = true;
1023
+ i += 1;
1024
+ continue;
1025
+ }
1026
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1027
+ inSingle = !inSingle;
1028
+ out += ch;
1029
+ continue;
1030
+ }
1031
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1032
+ inDouble = !inDouble;
1033
+ out += ch;
1034
+ continue;
1035
+ }
1036
+ if (!inSingle && !inDouble && ch === "?") {
1037
+ placeholder += 1;
1038
+ out += `$${placeholder}`;
1039
+ continue;
1040
+ }
1041
+ out += ch;
1042
+ }
1043
+ return out;
1044
+ }
1045
+ function translateStatementForPostgres(stmt) {
1046
+ const normalized = normalizeStatement(stmt);
1047
+ if (normalized.kind === "named") {
1048
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
1049
+ }
1050
+ const rewrittenSql = rewriteSql(normalized.sql);
1051
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
1052
+ return {
1053
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
1054
+ args: coercedArgs
1055
+ };
1056
+ }
1057
+ function shouldBypassPostgres(stmt) {
1058
+ const normalized = normalizeStatement(stmt);
1059
+ if (normalized.kind === "named") {
1060
+ return true;
1061
+ }
1062
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
1063
+ }
1064
+ function shouldFallbackOnError(error) {
1065
+ const message = error instanceof Error ? error.message : String(error);
1066
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
1067
+ }
1068
+ function isReadQuery(sql) {
1069
+ const trimmed = sql.trimStart();
1070
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
1071
+ }
1072
+ function buildRow(row, columns) {
1073
+ const values = columns.map((column) => row[column]);
1074
+ return Object.assign(values, row);
1075
+ }
1076
+ function buildResultSet(rows, rowsAffected = 0) {
1077
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
1078
+ const resultRows = rows.map((row) => buildRow(row, columns));
1079
+ return {
1080
+ columns,
1081
+ columnTypes: columns.map(() => ""),
1082
+ rows: resultRows,
1083
+ rowsAffected,
1084
+ lastInsertRowid: void 0,
1085
+ toJSON() {
1086
+ return {
1087
+ columns,
1088
+ columnTypes: columns.map(() => ""),
1089
+ rows,
1090
+ rowsAffected,
1091
+ lastInsertRowid: void 0
1092
+ };
1093
+ }
1094
+ };
1095
+ }
1096
+ async function loadPrismaClient() {
1097
+ if (!prismaClientPromise) {
1098
+ prismaClientPromise = (async () => {
1099
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
1100
+ if (explicitPath) {
1101
+ const module2 = await import(pathToFileURL(explicitPath).href);
1102
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
1103
+ if (!PrismaClient2) {
1104
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
1105
+ }
1106
+ return new PrismaClient2();
1107
+ }
1108
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os5.homedir(), "exe-db");
1109
+ const requireFromExeDb = createRequire(path6.join(exeDbRoot, "package.json"));
1110
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
1111
+ const module = await import(pathToFileURL(prismaEntry).href);
1112
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
1113
+ if (!PrismaClient) {
1114
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
1115
+ }
1116
+ return new PrismaClient();
1117
+ })();
1118
+ }
1119
+ return prismaClientPromise;
1120
+ }
1121
+ async function ensureCompatibilityViews(prisma) {
1122
+ if (!compatibilityBootstrapPromise) {
1123
+ compatibilityBootstrapPromise = (async () => {
1124
+ for (const mapping of VIEW_MAPPINGS) {
1125
+ const relation = mapping.source.replace(/"/g, "");
1126
+ const rows = await prisma.$queryRawUnsafe(
1127
+ "SELECT to_regclass($1) AS regclass",
1128
+ relation
1129
+ );
1130
+ if (!rows[0]?.regclass) {
1131
+ continue;
1132
+ }
1133
+ await prisma.$executeRawUnsafe(
1134
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
1135
+ );
1136
+ }
1137
+ })();
1138
+ }
1139
+ return compatibilityBootstrapPromise;
1140
+ }
1141
+ async function executeOnPrisma(executor, stmt) {
1142
+ const translated = translateStatementForPostgres(stmt);
1143
+ if (isReadQuery(translated.sql)) {
1144
+ const rows = await executor.$queryRawUnsafe(
1145
+ translated.sql,
1146
+ ...translated.args
1147
+ );
1148
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
1149
+ }
1150
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
1151
+ return buildResultSet([], rowsAffected);
1152
+ }
1153
+ function splitSqlStatements(sql) {
1154
+ const parts = [];
1155
+ let current = "";
1156
+ let inSingle = false;
1157
+ let inDouble = false;
1158
+ let inLineComment = false;
1159
+ let inBlockComment = false;
1160
+ for (let i = 0; i < sql.length; i++) {
1161
+ const ch = sql[i];
1162
+ const next = sql[i + 1];
1163
+ if (inLineComment) {
1164
+ current += ch;
1165
+ if (ch === "\n") inLineComment = false;
1166
+ continue;
1167
+ }
1168
+ if (inBlockComment) {
1169
+ current += ch;
1170
+ if (ch === "*" && next === "/") {
1171
+ current += next;
1172
+ inBlockComment = false;
1173
+ i += 1;
1174
+ }
1175
+ continue;
1176
+ }
1177
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1178
+ current += ch + next;
1179
+ inLineComment = true;
1180
+ i += 1;
1181
+ continue;
1182
+ }
1183
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1184
+ current += ch + next;
1185
+ inBlockComment = true;
1186
+ i += 1;
1187
+ continue;
1188
+ }
1189
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1190
+ inSingle = !inSingle;
1191
+ current += ch;
1192
+ continue;
1193
+ }
1194
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1195
+ inDouble = !inDouble;
1196
+ current += ch;
1197
+ continue;
1198
+ }
1199
+ if (!inSingle && !inDouble && ch === ";") {
1200
+ if (current.trim()) {
1201
+ parts.push(current.trim());
1202
+ }
1203
+ current = "";
1204
+ continue;
1205
+ }
1206
+ current += ch;
1207
+ }
1208
+ if (current.trim()) {
1209
+ parts.push(current.trim());
1210
+ }
1211
+ return parts;
1212
+ }
1213
+ async function createPrismaDbAdapter(fallbackClient) {
1214
+ const prisma = await loadPrismaClient();
1215
+ await ensureCompatibilityViews(prisma);
1216
+ let closed = false;
1217
+ let adapter;
1218
+ const fallbackExecute = async (stmt, error) => {
1219
+ if (!fallbackClient) {
1220
+ if (error) throw error;
1221
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
1222
+ }
1223
+ if (error) {
1224
+ process.stderr.write(
1225
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
1226
+ `
1227
+ );
1228
+ }
1229
+ return fallbackClient.execute(stmt);
1230
+ };
1231
+ adapter = {
1232
+ async execute(stmt) {
1233
+ if (shouldBypassPostgres(stmt)) {
1234
+ return fallbackExecute(stmt);
1235
+ }
1236
+ try {
1237
+ return await executeOnPrisma(prisma, stmt);
1238
+ } catch (error) {
1239
+ if (shouldFallbackOnError(error)) {
1240
+ return fallbackExecute(stmt, error);
1241
+ }
1242
+ throw error;
1243
+ }
1244
+ },
1245
+ async batch(stmts, mode) {
1246
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
1247
+ if (!fallbackClient) {
1248
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
1249
+ }
1250
+ return fallbackClient.batch(stmts, mode);
1251
+ }
1252
+ try {
1253
+ if (prisma.$transaction) {
1254
+ return await prisma.$transaction(async (tx) => {
1255
+ const results2 = [];
1256
+ for (const stmt of stmts) {
1257
+ results2.push(await executeOnPrisma(tx, stmt));
1258
+ }
1259
+ return results2;
1260
+ });
1261
+ }
1262
+ const results = [];
1263
+ for (const stmt of stmts) {
1264
+ results.push(await executeOnPrisma(prisma, stmt));
1265
+ }
1266
+ return results;
1267
+ } catch (error) {
1268
+ if (fallbackClient && shouldFallbackOnError(error)) {
1269
+ process.stderr.write(
1270
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
1271
+ `
1272
+ );
1273
+ return fallbackClient.batch(stmts, mode);
1274
+ }
1275
+ throw error;
1276
+ }
1277
+ },
1278
+ async migrate(stmts) {
1279
+ if (fallbackClient) {
1280
+ return fallbackClient.migrate(stmts);
1281
+ }
1282
+ return adapter.batch(stmts, "deferred");
1283
+ },
1284
+ async transaction(mode) {
1285
+ if (!fallbackClient) {
1286
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
1287
+ }
1288
+ return fallbackClient.transaction(mode);
1289
+ },
1290
+ async executeMultiple(sql) {
1291
+ if (fallbackClient && shouldBypassPostgres(sql)) {
1292
+ return fallbackClient.executeMultiple(sql);
1293
+ }
1294
+ for (const statement of splitSqlStatements(sql)) {
1295
+ await adapter.execute(statement);
1296
+ }
1297
+ },
1298
+ async sync() {
1299
+ if (fallbackClient) {
1300
+ return fallbackClient.sync();
1301
+ }
1302
+ return { frame_no: 0, frames_synced: 0 };
1303
+ },
1304
+ close() {
1305
+ closed = true;
1306
+ prismaClientPromise = null;
1307
+ compatibilityBootstrapPromise = null;
1308
+ void prisma.$disconnect?.();
1309
+ },
1310
+ get closed() {
1311
+ return closed;
1312
+ },
1313
+ get protocol() {
1314
+ return "prisma-postgres";
1315
+ }
1316
+ };
1317
+ return adapter;
1318
+ }
1319
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
1320
+ var init_database_adapter = __esm({
1321
+ "src/lib/database-adapter.ts"() {
1322
+ "use strict";
1323
+ VIEW_MAPPINGS = [
1324
+ { view: "memories", source: "memory.memory_records" },
1325
+ { view: "tasks", source: "memory.tasks" },
1326
+ { view: "behaviors", source: "memory.behaviors" },
1327
+ { view: "entities", source: "memory.entities" },
1328
+ { view: "relationships", source: "memory.relationships" },
1329
+ { view: "entity_memories", source: "memory.entity_memories" },
1330
+ { view: "entity_aliases", source: "memory.entity_aliases" },
1331
+ { view: "notifications", source: "memory.notifications" },
1332
+ { view: "messages", source: "memory.messages" },
1333
+ { view: "users", source: "wiki.users" },
1334
+ { view: "workspaces", source: "wiki.workspaces" },
1335
+ { view: "workspace_users", source: "wiki.workspace_users" },
1336
+ { view: "documents", source: "wiki.workspace_documents" },
1337
+ { view: "chats", source: "wiki.workspace_chats" }
1338
+ ];
1339
+ UPSERT_KEYS = {
1340
+ memories: ["id"],
1341
+ tasks: ["id"],
1342
+ behaviors: ["id"],
1343
+ entities: ["id"],
1344
+ relationships: ["id"],
1345
+ entity_aliases: ["alias"],
1346
+ notifications: ["id"],
1347
+ messages: ["id"],
1348
+ users: ["id"],
1349
+ workspaces: ["id"],
1350
+ workspace_users: ["id"],
1351
+ documents: ["id"],
1352
+ chats: ["id"]
1353
+ };
1354
+ BOOLEAN_COLUMNS_BY_TABLE = {
1355
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
1356
+ behaviors: /* @__PURE__ */ new Set(["active"]),
1357
+ notifications: /* @__PURE__ */ new Set(["read"]),
1358
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
1359
+ };
1360
+ BOOLEAN_COLUMN_NAMES = new Set(
1361
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
1362
+ );
1363
+ IMMEDIATE_FALLBACK_PATTERNS = [
1364
+ /\bPRAGMA\b/i,
1365
+ /\bsqlite_master\b/i,
1366
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
1367
+ /\bMATCH\b/i,
1368
+ /\bvector_distance_cos\s*\(/i,
1369
+ /\bjson_extract\s*\(/i,
1370
+ /\bjulianday\s*\(/i,
1371
+ /\bstrftime\s*\(/i,
1372
+ /\blast_insert_rowid\s*\(/i
1373
+ ];
1374
+ prismaClientPromise = null;
1375
+ compatibilityBootstrapPromise = null;
791
1376
  }
792
1377
  });
793
1378
 
794
1379
  // src/lib/exe-daemon-client.ts
795
1380
  import net from "net";
796
- import os5 from "os";
1381
+ import os6 from "os";
797
1382
  import { spawn } from "child_process";
798
1383
  import { randomUUID } from "crypto";
799
1384
  import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
800
- import path6 from "path";
1385
+ import path7 from "path";
801
1386
  import { fileURLToPath } from "url";
802
1387
  function handleData(chunk) {
803
1388
  _buffer += chunk.toString();
@@ -848,17 +1433,17 @@ function cleanupStaleFiles() {
848
1433
  }
849
1434
  }
850
1435
  function findPackageRoot() {
851
- let dir = path6.dirname(fileURLToPath(import.meta.url));
852
- const { root } = path6.parse(dir);
1436
+ let dir = path7.dirname(fileURLToPath(import.meta.url));
1437
+ const { root } = path7.parse(dir);
853
1438
  while (dir !== root) {
854
- if (existsSync6(path6.join(dir, "package.json"))) return dir;
855
- dir = path6.dirname(dir);
1439
+ if (existsSync6(path7.join(dir, "package.json"))) return dir;
1440
+ dir = path7.dirname(dir);
856
1441
  }
857
1442
  return null;
858
1443
  }
859
1444
  function spawnDaemon() {
860
- const freeGB = os5.freemem() / (1024 * 1024 * 1024);
861
- const totalGB = os5.totalmem() / (1024 * 1024 * 1024);
1445
+ const freeGB = os6.freemem() / (1024 * 1024 * 1024);
1446
+ const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
862
1447
  if (totalGB <= 8) {
863
1448
  process.stderr.write(
864
1449
  `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
@@ -878,7 +1463,7 @@ function spawnDaemon() {
878
1463
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
879
1464
  return;
880
1465
  }
881
- const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1466
+ const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
882
1467
  if (!existsSync6(daemonPath)) {
883
1468
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
884
1469
  `);
@@ -887,7 +1472,7 @@ function spawnDaemon() {
887
1472
  const resolvedPath = daemonPath;
888
1473
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
889
1474
  `);
890
- const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
1475
+ const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
891
1476
  let stderrFd = "ignore";
892
1477
  try {
893
1478
  stderrFd = openSync(logPath, "a");
@@ -1034,9 +1619,9 @@ var init_exe_daemon_client = __esm({
1034
1619
  "src/lib/exe-daemon-client.ts"() {
1035
1620
  "use strict";
1036
1621
  init_config();
1037
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
1038
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
1039
- SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
1622
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
1623
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
1624
+ SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
1040
1625
  SPAWN_LOCK_STALE_MS = 3e4;
1041
1626
  CONNECT_TIMEOUT_MS = 15e3;
1042
1627
  REQUEST_TIMEOUT_MS = 3e4;
@@ -1118,7 +1703,7 @@ __export(db_daemon_client_exports, {
1118
1703
  createDaemonDbClient: () => createDaemonDbClient,
1119
1704
  initDaemonDbClient: () => initDaemonDbClient
1120
1705
  });
1121
- function normalizeStatement(stmt) {
1706
+ function normalizeStatement2(stmt) {
1122
1707
  if (typeof stmt === "string") {
1123
1708
  return { sql: stmt, args: [] };
1124
1709
  }
@@ -1142,7 +1727,7 @@ function createDaemonDbClient(fallbackClient) {
1142
1727
  if (!_useDaemon || !isClientConnected()) {
1143
1728
  return fallbackClient.execute(stmt);
1144
1729
  }
1145
- const { sql, args } = normalizeStatement(stmt);
1730
+ const { sql, args } = normalizeStatement2(stmt);
1146
1731
  const response = await sendDaemonRequest({
1147
1732
  type: "db-execute",
1148
1733
  sql,
@@ -1167,7 +1752,7 @@ function createDaemonDbClient(fallbackClient) {
1167
1752
  if (!_useDaemon || !isClientConnected()) {
1168
1753
  return fallbackClient.batch(stmts, mode);
1169
1754
  }
1170
- const statements = stmts.map(normalizeStatement);
1755
+ const statements = stmts.map(normalizeStatement2);
1171
1756
  const response = await sendDaemonRequest({
1172
1757
  type: "db-batch",
1173
1758
  statements,
@@ -1262,6 +1847,18 @@ __export(database_exports, {
1262
1847
  });
1263
1848
  import { createClient } from "@libsql/client";
1264
1849
  async function initDatabase(config) {
1850
+ if (_walCheckpointTimer) {
1851
+ clearInterval(_walCheckpointTimer);
1852
+ _walCheckpointTimer = null;
1853
+ }
1854
+ if (_daemonClient) {
1855
+ _daemonClient.close();
1856
+ _daemonClient = null;
1857
+ }
1858
+ if (_adapterClient && _adapterClient !== _resilientClient) {
1859
+ _adapterClient.close();
1860
+ }
1861
+ _adapterClient = null;
1265
1862
  if (_client) {
1266
1863
  _client.close();
1267
1864
  _client = null;
@@ -1275,6 +1872,7 @@ async function initDatabase(config) {
1275
1872
  }
1276
1873
  _client = createClient(opts);
1277
1874
  _resilientClient = wrapWithRetry(_client);
1875
+ _adapterClient = _resilientClient;
1278
1876
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
1279
1877
  });
1280
1878
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -1285,14 +1883,20 @@ async function initDatabase(config) {
1285
1883
  });
1286
1884
  }, 3e4);
1287
1885
  _walCheckpointTimer.unref();
1886
+ if (process.env.DATABASE_URL) {
1887
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
1888
+ }
1288
1889
  }
1289
1890
  function isInitialized() {
1290
- return _client !== null;
1891
+ return _adapterClient !== null || _client !== null;
1291
1892
  }
1292
1893
  function getClient() {
1293
- if (!_resilientClient) {
1894
+ if (!_adapterClient) {
1294
1895
  throw new Error("Database client not initialized. Call initDatabase() first.");
1295
1896
  }
1897
+ if (process.env.DATABASE_URL) {
1898
+ return _adapterClient;
1899
+ }
1296
1900
  if (process.env.EXE_IS_DAEMON === "1") {
1297
1901
  return _resilientClient;
1298
1902
  }
@@ -1302,6 +1906,7 @@ function getClient() {
1302
1906
  return _resilientClient;
1303
1907
  }
1304
1908
  async function initDaemonClient() {
1909
+ if (process.env.DATABASE_URL) return;
1305
1910
  if (process.env.EXE_IS_DAEMON === "1") return;
1306
1911
  if (!_resilientClient) return;
1307
1912
  try {
@@ -2246,26 +2851,36 @@ async function ensureSchema() {
2246
2851
  }
2247
2852
  }
2248
2853
  async function disposeDatabase() {
2854
+ if (_walCheckpointTimer) {
2855
+ clearInterval(_walCheckpointTimer);
2856
+ _walCheckpointTimer = null;
2857
+ }
2249
2858
  if (_daemonClient) {
2250
2859
  _daemonClient.close();
2251
2860
  _daemonClient = null;
2252
2861
  }
2862
+ if (_adapterClient && _adapterClient !== _resilientClient) {
2863
+ _adapterClient.close();
2864
+ }
2865
+ _adapterClient = null;
2253
2866
  if (_client) {
2254
2867
  _client.close();
2255
2868
  _client = null;
2256
2869
  _resilientClient = null;
2257
2870
  }
2258
2871
  }
2259
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
2872
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
2260
2873
  var init_database = __esm({
2261
2874
  "src/lib/database.ts"() {
2262
2875
  "use strict";
2263
2876
  init_db_retry();
2264
2877
  init_employees();
2878
+ init_database_adapter();
2265
2879
  _client = null;
2266
2880
  _resilientClient = null;
2267
2881
  _walCheckpointTimer = null;
2268
2882
  _daemonClient = null;
2883
+ _adapterClient = null;
2269
2884
  initTurso = initDatabase;
2270
2885
  disposeTurso = disposeDatabase;
2271
2886
  }
@@ -2274,16 +2889,16 @@ var init_database = __esm({
2274
2889
  // src/lib/license.ts
2275
2890
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
2276
2891
  import { randomUUID as randomUUID2 } from "crypto";
2277
- import path7 from "path";
2892
+ import path8 from "path";
2278
2893
  import { jwtVerify, importSPKI } from "jose";
2279
2894
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
2280
2895
  var init_license = __esm({
2281
2896
  "src/lib/license.ts"() {
2282
2897
  "use strict";
2283
2898
  init_config();
2284
- LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
2285
- CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
2286
- DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
2899
+ LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
2900
+ CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
2901
+ DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
2287
2902
  PLAN_LIMITS = {
2288
2903
  free: { devices: 1, employees: 1, memories: 5e3 },
2289
2904
  pro: { devices: 3, employees: 5, memories: 1e5 },
@@ -2296,7 +2911,7 @@ var init_license = __esm({
2296
2911
 
2297
2912
  // src/lib/plan-limits.ts
2298
2913
  import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
2299
- import path8 from "path";
2914
+ import path9 from "path";
2300
2915
  function getLicenseSync() {
2301
2916
  try {
2302
2917
  if (!existsSync8(CACHE_PATH2)) return freeLicense();
@@ -2368,14 +2983,14 @@ var init_plan_limits = __esm({
2368
2983
  this.name = "PlanLimitError";
2369
2984
  }
2370
2985
  };
2371
- CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
2986
+ CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
2372
2987
  }
2373
2988
  });
2374
2989
 
2375
2990
  // src/lib/notifications.ts
2376
2991
  import crypto from "crypto";
2377
- import path9 from "path";
2378
- import os6 from "os";
2992
+ import path10 from "path";
2993
+ import os7 from "os";
2379
2994
  import {
2380
2995
  readFileSync as readFileSync9,
2381
2996
  readdirSync,
@@ -2531,8 +3146,8 @@ __export(tasks_crud_exports, {
2531
3146
  writeCheckpoint: () => writeCheckpoint
2532
3147
  });
2533
3148
  import crypto3 from "crypto";
2534
- import path10 from "path";
2535
- import os7 from "os";
3149
+ import path11 from "path";
3150
+ import os8 from "os";
2536
3151
  import { execSync as execSync4 } from "child_process";
2537
3152
  import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
2538
3153
  import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
@@ -2710,8 +3325,8 @@ ${laneWarning}` : laneWarning;
2710
3325
  }
2711
3326
  if (input.baseDir) {
2712
3327
  try {
2713
- await mkdir3(path10.join(input.baseDir, "exe", "output"), { recursive: true });
2714
- await mkdir3(path10.join(input.baseDir, "exe", "research"), { recursive: true });
3328
+ await mkdir3(path11.join(input.baseDir, "exe", "output"), { recursive: true });
3329
+ await mkdir3(path11.join(input.baseDir, "exe", "research"), { recursive: true });
2715
3330
  await ensureArchitectureDoc(input.baseDir, input.projectName);
2716
3331
  await ensureGitignoreExe(input.baseDir);
2717
3332
  } catch {
@@ -2747,9 +3362,9 @@ ${laneWarning}` : laneWarning;
2747
3362
  });
2748
3363
  if (input.baseDir) {
2749
3364
  try {
2750
- const EXE_OS_DIR = path10.join(os7.homedir(), ".exe-os");
2751
- const mdPath = path10.join(EXE_OS_DIR, taskFile);
2752
- const mdDir = path10.dirname(mdPath);
3365
+ const EXE_OS_DIR = path11.join(os8.homedir(), ".exe-os");
3366
+ const mdPath = path11.join(EXE_OS_DIR, taskFile);
3367
+ const mdDir = path11.dirname(mdPath);
2753
3368
  if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
2754
3369
  const reviewer = input.reviewer ?? input.assignedBy;
2755
3370
  const mdContent = `# ${input.title}
@@ -3050,7 +3665,7 @@ async function deleteTaskCore(taskId, _baseDir) {
3050
3665
  return { taskFile, assignedTo, assignedBy, taskSlug };
3051
3666
  }
3052
3667
  async function ensureArchitectureDoc(baseDir, projectName) {
3053
- const archPath = path10.join(baseDir, "exe", "ARCHITECTURE.md");
3668
+ const archPath = path11.join(baseDir, "exe", "ARCHITECTURE.md");
3054
3669
  try {
3055
3670
  if (existsSync10(archPath)) return;
3056
3671
  const template = [
@@ -3085,7 +3700,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
3085
3700
  }
3086
3701
  }
3087
3702
  async function ensureGitignoreExe(baseDir) {
3088
- const gitignorePath = path10.join(baseDir, ".gitignore");
3703
+ const gitignorePath = path11.join(baseDir, ".gitignore");
3089
3704
  try {
3090
3705
  if (existsSync10(gitignorePath)) {
3091
3706
  const content = readFileSync10(gitignorePath, "utf-8");
@@ -3119,13 +3734,13 @@ var init_tasks_crud = __esm({
3119
3734
  });
3120
3735
 
3121
3736
  // src/lib/tasks-review.ts
3122
- import path11 from "path";
3737
+ import path12 from "path";
3123
3738
  import { existsSync as existsSync11, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
3124
3739
  async function countPendingReviews(sessionScope) {
3125
3740
  const client = getClient();
3126
3741
  if (sessionScope) {
3127
3742
  const result2 = await client.execute({
3128
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND (session_scope = ? OR session_scope IS NULL)",
3743
+ sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
3129
3744
  args: [sessionScope]
3130
3745
  });
3131
3746
  return Number(result2.rows[0]?.cnt) || 0;
@@ -3301,11 +3916,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
3301
3916
  );
3302
3917
  }
3303
3918
  try {
3304
- const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
3919
+ const cacheDir = path12.join(EXE_AI_DIR, "session-cache");
3305
3920
  if (existsSync11(cacheDir)) {
3306
3921
  for (const f of readdirSync2(cacheDir)) {
3307
3922
  if (f.startsWith("review-notified-")) {
3308
- unlinkSync4(path11.join(cacheDir, f));
3923
+ unlinkSync4(path12.join(cacheDir, f));
3309
3924
  }
3310
3925
  }
3311
3926
  }
@@ -3326,7 +3941,7 @@ var init_tasks_review = __esm({
3326
3941
  });
3327
3942
 
3328
3943
  // src/lib/tasks-chain.ts
3329
- import path12 from "path";
3944
+ import path13 from "path";
3330
3945
  import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
3331
3946
  async function cascadeUnblock(taskId, baseDir, now) {
3332
3947
  const client = getClient();
@@ -3343,7 +3958,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
3343
3958
  });
3344
3959
  for (const ur of unblockedRows.rows) {
3345
3960
  try {
3346
- const ubFile = path12.join(baseDir, String(ur.task_file));
3961
+ const ubFile = path13.join(baseDir, String(ur.task_file));
3347
3962
  let ubContent = await readFile3(ubFile, "utf-8");
3348
3963
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
3349
3964
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -3412,7 +4027,7 @@ var init_tasks_chain = __esm({
3412
4027
 
3413
4028
  // src/lib/project-name.ts
3414
4029
  import { execSync as execSync5 } from "child_process";
3415
- import path13 from "path";
4030
+ import path14 from "path";
3416
4031
  function getProjectName(cwd) {
3417
4032
  const dir = cwd ?? process.cwd();
3418
4033
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -3425,7 +4040,7 @@ function getProjectName(cwd) {
3425
4040
  timeout: 2e3,
3426
4041
  stdio: ["pipe", "pipe", "pipe"]
3427
4042
  }).trim();
3428
- repoRoot = path13.dirname(gitCommonDir);
4043
+ repoRoot = path14.dirname(gitCommonDir);
3429
4044
  } catch {
3430
4045
  repoRoot = execSync5("git rev-parse --show-toplevel", {
3431
4046
  cwd: dir,
@@ -3434,11 +4049,11 @@ function getProjectName(cwd) {
3434
4049
  stdio: ["pipe", "pipe", "pipe"]
3435
4050
  }).trim();
3436
4051
  }
3437
- _cached2 = path13.basename(repoRoot);
4052
+ _cached2 = path14.basename(repoRoot);
3438
4053
  _cachedCwd = dir;
3439
4054
  return _cached2;
3440
4055
  } catch {
3441
- _cached2 = path13.basename(dir);
4056
+ _cached2 = path14.basename(dir);
3442
4057
  _cachedCwd = dir;
3443
4058
  return _cached2;
3444
4059
  }
@@ -3911,7 +4526,7 @@ __export(tasks_exports, {
3911
4526
  updateTaskStatus: () => updateTaskStatus,
3912
4527
  writeCheckpoint: () => writeCheckpoint
3913
4528
  });
3914
- import path14 from "path";
4529
+ import path15 from "path";
3915
4530
  import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
3916
4531
  async function createTask(input) {
3917
4532
  const result = await createTaskCore(input);
@@ -3931,8 +4546,8 @@ async function updateTask(input) {
3931
4546
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
3932
4547
  try {
3933
4548
  const agent = String(row.assigned_to);
3934
- const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
3935
- const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
4549
+ const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
4550
+ const cachePath = path15.join(cacheDir, `current-task-${agent}.json`);
3936
4551
  if (input.status === "in_progress") {
3937
4552
  mkdirSync5(cacheDir, { recursive: true });
3938
4553
  writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
@@ -4403,12 +5018,12 @@ __export(tmux_routing_exports, {
4403
5018
  });
4404
5019
  import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
4405
5020
  import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync3 } from "fs";
4406
- import path15 from "path";
4407
- import os8 from "os";
5021
+ import path16 from "path";
5022
+ import os9 from "os";
4408
5023
  import { fileURLToPath as fileURLToPath2 } from "url";
4409
5024
  import { unlinkSync as unlinkSync6 } from "fs";
4410
5025
  function spawnLockPath(sessionName) {
4411
- return path15.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
5026
+ return path16.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
4412
5027
  }
4413
5028
  function isProcessAlive(pid) {
4414
5029
  try {
@@ -4445,8 +5060,8 @@ function releaseSpawnLock2(sessionName) {
4445
5060
  function resolveBehaviorsExporterScript() {
4446
5061
  try {
4447
5062
  const thisFile = fileURLToPath2(import.meta.url);
4448
- const scriptPath = path15.join(
4449
- path15.dirname(thisFile),
5063
+ const scriptPath = path16.join(
5064
+ path16.dirname(thisFile),
4450
5065
  "..",
4451
5066
  "bin",
4452
5067
  "exe-export-behaviors.js"
@@ -4521,7 +5136,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4521
5136
  mkdirSync6(SESSION_CACHE, { recursive: true });
4522
5137
  }
4523
5138
  const rootExe = extractRootExe(parentExe) ?? parentExe;
4524
- const filePath = path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
5139
+ const filePath = path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
4525
5140
  writeFileSync7(filePath, JSON.stringify({
4526
5141
  parentExe: rootExe,
4527
5142
  dispatchedBy: dispatchedBy || rootExe,
@@ -4530,7 +5145,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4530
5145
  }
4531
5146
  function getParentExe(sessionKey) {
4532
5147
  try {
4533
- const data = JSON.parse(readFileSync11(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
5148
+ const data = JSON.parse(readFileSync11(path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4534
5149
  return data.parentExe || null;
4535
5150
  } catch {
4536
5151
  return null;
@@ -4539,7 +5154,7 @@ function getParentExe(sessionKey) {
4539
5154
  function getDispatchedBy(sessionKey) {
4540
5155
  try {
4541
5156
  const data = JSON.parse(readFileSync11(
4542
- path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
5157
+ path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
4543
5158
  "utf8"
4544
5159
  ));
4545
5160
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -4725,7 +5340,7 @@ function sendIntercom(targetSession) {
4725
5340
  try {
4726
5341
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
4727
5342
  const agent = baseAgentName(rawAgent);
4728
- const markerPath = path15.join(SESSION_CACHE, `current-task-${agent}.json`);
5343
+ const markerPath = path16.join(SESSION_CACHE, `current-task-${agent}.json`);
4729
5344
  if (existsSync12(markerPath)) {
4730
5345
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
4731
5346
  return "debounced";
@@ -4735,7 +5350,7 @@ function sendIntercom(targetSession) {
4735
5350
  try {
4736
5351
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
4737
5352
  const agent = baseAgentName(rawAgent);
4738
- const taskDir = path15.join(process.cwd(), "exe", agent);
5353
+ const taskDir = path16.join(process.cwd(), "exe", agent);
4739
5354
  if (existsSync12(taskDir)) {
4740
5355
  const files = readdirSync3(taskDir).filter(
4741
5356
  (f) => f.endsWith(".md") && f !== "DONE.txt"
@@ -4869,8 +5484,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4869
5484
  const transport = getTransport();
4870
5485
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
4871
5486
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
4872
- const logDir = path15.join(os8.homedir(), ".exe-os", "session-logs");
4873
- const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
5487
+ const logDir = path16.join(os9.homedir(), ".exe-os", "session-logs");
5488
+ const logFile = path16.join(logDir, `${instanceLabel}-${Date.now()}.log`);
4874
5489
  if (!existsSync12(logDir)) {
4875
5490
  mkdirSync6(logDir, { recursive: true });
4876
5491
  }
@@ -4878,14 +5493,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4878
5493
  let cleanupSuffix = "";
4879
5494
  try {
4880
5495
  const thisFile = fileURLToPath2(import.meta.url);
4881
- const cleanupScript = path15.join(path15.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
5496
+ const cleanupScript = path16.join(path16.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
4882
5497
  if (existsSync12(cleanupScript)) {
4883
5498
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
4884
5499
  }
4885
5500
  } catch {
4886
5501
  }
4887
5502
  try {
4888
- const claudeJsonPath = path15.join(os8.homedir(), ".claude.json");
5503
+ const claudeJsonPath = path16.join(os9.homedir(), ".claude.json");
4889
5504
  let claudeJson = {};
4890
5505
  try {
4891
5506
  claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
@@ -4900,10 +5515,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4900
5515
  } catch {
4901
5516
  }
4902
5517
  try {
4903
- const settingsDir = path15.join(os8.homedir(), ".claude", "projects");
5518
+ const settingsDir = path16.join(os9.homedir(), ".claude", "projects");
4904
5519
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
4905
- const projSettingsDir = path15.join(settingsDir, normalizedKey);
4906
- const settingsPath = path15.join(projSettingsDir, "settings.json");
5520
+ const projSettingsDir = path16.join(settingsDir, normalizedKey);
5521
+ const settingsPath = path16.join(projSettingsDir, "settings.json");
4907
5522
  let settings = {};
4908
5523
  try {
4909
5524
  settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
@@ -4950,8 +5565,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4950
5565
  let behaviorsFlag = "";
4951
5566
  let legacyFallbackWarned = false;
4952
5567
  if (!useExeAgent && !useBinSymlink) {
4953
- const identityPath = path15.join(
4954
- os8.homedir(),
5568
+ const identityPath = path16.join(
5569
+ os9.homedir(),
4955
5570
  ".exe-os",
4956
5571
  "identity",
4957
5572
  `${employeeName}.md`
@@ -4966,7 +5581,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4966
5581
  }
4967
5582
  const behaviorsFile = exportBehaviorsSync(
4968
5583
  employeeName,
4969
- path15.basename(spawnCwd),
5584
+ path16.basename(spawnCwd),
4970
5585
  sessionName
4971
5586
  );
4972
5587
  if (behaviorsFile) {
@@ -4981,9 +5596,9 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4981
5596
  }
4982
5597
  let sessionContextFlag = "";
4983
5598
  try {
4984
- const ctxDir = path15.join(os8.homedir(), ".exe-os", "session-cache");
5599
+ const ctxDir = path16.join(os9.homedir(), ".exe-os", "session-cache");
4985
5600
  mkdirSync6(ctxDir, { recursive: true });
4986
- const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
5601
+ const ctxFile = path16.join(ctxDir, `session-context-${sessionName}.md`);
4987
5602
  const ctxContent = [
4988
5603
  `## Session Context`,
4989
5604
  `You are running in tmux session: ${sessionName}.`,
@@ -5067,7 +5682,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5067
5682
  transport.pipeLog(sessionName, logFile);
5068
5683
  try {
5069
5684
  const mySession = getMySession();
5070
- const dispatchInfo = path15.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5685
+ const dispatchInfo = path16.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5071
5686
  writeFileSync7(dispatchInfo, JSON.stringify({
5072
5687
  dispatchedBy: mySession,
5073
5688
  rootExe: exeSession,
@@ -5142,15 +5757,15 @@ var init_tmux_routing = __esm({
5142
5757
  init_intercom_queue();
5143
5758
  init_plan_limits();
5144
5759
  init_employees();
5145
- SPAWN_LOCK_DIR = path15.join(os8.homedir(), ".exe-os", "spawn-locks");
5146
- SESSION_CACHE = path15.join(os8.homedir(), ".exe-os", "session-cache");
5760
+ SPAWN_LOCK_DIR = path16.join(os9.homedir(), ".exe-os", "spawn-locks");
5761
+ SESSION_CACHE = path16.join(os9.homedir(), ".exe-os", "session-cache");
5147
5762
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
5148
5763
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
5149
5764
  VERIFY_PANE_LINES = 200;
5150
5765
  INTERCOM_DEBOUNCE_MS = 3e4;
5151
5766
  CODEX_DEBOUNCE_MS = 12e4;
5152
- INTERCOM_LOG2 = path15.join(os8.homedir(), ".exe-os", "intercom.log");
5153
- DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
5767
+ INTERCOM_LOG2 = path16.join(os9.homedir(), ".exe-os", "intercom.log");
5768
+ DEBOUNCE_FILE = path16.join(SESSION_CACHE, "intercom-debounce.json");
5154
5769
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
5155
5770
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
5156
5771
  }
@@ -5192,13 +5807,13 @@ var init_memory = __esm({
5192
5807
  // src/lib/keychain.ts
5193
5808
  import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
5194
5809
  import { existsSync as existsSync13 } from "fs";
5195
- import path16 from "path";
5196
- import os9 from "os";
5810
+ import path17 from "path";
5811
+ import os10 from "os";
5197
5812
  function getKeyDir() {
5198
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path16.join(os9.homedir(), ".exe-os");
5813
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path17.join(os10.homedir(), ".exe-os");
5199
5814
  }
5200
5815
  function getKeyPath() {
5201
- return path16.join(getKeyDir(), "master.key");
5816
+ return path17.join(getKeyDir(), "master.key");
5202
5817
  }
5203
5818
  async function tryKeytar() {
5204
5819
  try {
@@ -5221,7 +5836,7 @@ async function getMasterKey() {
5221
5836
  const keyPath = getKeyPath();
5222
5837
  if (!existsSync13(keyPath)) {
5223
5838
  process.stderr.write(
5224
- `[keychain] Key not found at ${keyPath} (HOME=${os9.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
5839
+ `[keychain] Key not found at ${keyPath} (HOME=${os10.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
5225
5840
  `
5226
5841
  );
5227
5842
  return null;
@@ -5259,7 +5874,7 @@ __export(shard_manager_exports, {
5259
5874
  listShards: () => listShards,
5260
5875
  shardExists: () => shardExists
5261
5876
  });
5262
- import path17 from "path";
5877
+ import path18 from "path";
5263
5878
  import { existsSync as existsSync14, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
5264
5879
  import { createClient as createClient2 } from "@libsql/client";
5265
5880
  function initShardManager(encryptionKey) {
@@ -5285,7 +5900,7 @@ function getShardClient(projectName) {
5285
5900
  }
5286
5901
  const cached = _shards.get(safeName);
5287
5902
  if (cached) return cached;
5288
- const dbPath = path17.join(SHARDS_DIR, `${safeName}.db`);
5903
+ const dbPath = path18.join(SHARDS_DIR, `${safeName}.db`);
5289
5904
  const client = createClient2({
5290
5905
  url: `file:${dbPath}`,
5291
5906
  encryptionKey: _encryptionKey
@@ -5295,7 +5910,7 @@ function getShardClient(projectName) {
5295
5910
  }
5296
5911
  function shardExists(projectName) {
5297
5912
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
5298
- return existsSync14(path17.join(SHARDS_DIR, `${safeName}.db`));
5913
+ return existsSync14(path18.join(SHARDS_DIR, `${safeName}.db`));
5299
5914
  }
5300
5915
  function listShards() {
5301
5916
  if (!existsSync14(SHARDS_DIR)) return [];
@@ -5372,7 +5987,23 @@ async function ensureShardSchema(client) {
5372
5987
  // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
5373
5988
  "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
5374
5989
  "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
5375
- "ALTER TABLE memories ADD COLUMN trajectory TEXT"
5990
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT",
5991
+ // Metadata enrichment columns (must match database.ts)
5992
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
5993
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
5994
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
5995
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
5996
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
5997
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
5998
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
5999
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
6000
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
6001
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
6002
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
6003
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
6004
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
6005
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
6006
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
5376
6007
  ]) {
5377
6008
  try {
5378
6009
  await client.execute(col);
@@ -5484,7 +6115,7 @@ var init_shard_manager = __esm({
5484
6115
  "src/lib/shard-manager.ts"() {
5485
6116
  "use strict";
5486
6117
  init_config();
5487
- SHARDS_DIR = path17.join(EXE_AI_DIR, "shards");
6118
+ SHARDS_DIR = path18.join(EXE_AI_DIR, "shards");
5488
6119
  _shards = /* @__PURE__ */ new Map();
5489
6120
  _encryptionKey = null;
5490
6121
  _shardingEnabled = false;