@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
@@ -780,7 +780,7 @@ function isMultiInstance(agentName, employees) {
780
780
  if (!emp) return false;
781
781
  return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
782
782
  }
783
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
783
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR;
784
784
  var init_employees = __esm({
785
785
  "src/lib/employees.ts"() {
786
786
  "use strict";
@@ -789,12 +789,609 @@ var init_employees = __esm({
789
789
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
790
790
  COORDINATOR_ROLE = "COO";
791
791
  MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
792
+ IDENTITY_DIR = path5.join(EXE_AI_DIR, "identity");
793
+ }
794
+ });
795
+
796
+ // src/lib/database-adapter.ts
797
+ import os5 from "os";
798
+ import path6 from "path";
799
+ import { createRequire } from "module";
800
+ import { pathToFileURL } from "url";
801
+ function quotedIdentifier(identifier) {
802
+ return `"${identifier.replace(/"/g, '""')}"`;
803
+ }
804
+ function unqualifiedTableName(name) {
805
+ const raw = name.trim().replace(/^"|"$/g, "");
806
+ const parts = raw.split(".");
807
+ return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
808
+ }
809
+ function stripTrailingSemicolon(sql) {
810
+ return sql.trim().replace(/;+\s*$/u, "");
811
+ }
812
+ function appendClause(sql, clause) {
813
+ const trimmed = stripTrailingSemicolon(sql);
814
+ const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
815
+ if (!returningMatch) {
816
+ return `${trimmed}${clause}`;
817
+ }
818
+ const idx = returningMatch.index;
819
+ return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
820
+ }
821
+ function normalizeStatement(stmt) {
822
+ if (typeof stmt === "string") {
823
+ return { kind: "positional", sql: stmt, args: [] };
824
+ }
825
+ const sql = stmt.sql;
826
+ if (Array.isArray(stmt.args) || stmt.args === void 0) {
827
+ return { kind: "positional", sql, args: stmt.args ?? [] };
828
+ }
829
+ return { kind: "named", sql, args: stmt.args };
830
+ }
831
+ function rewriteBooleanLiterals(sql) {
832
+ let out = sql;
833
+ for (const column of BOOLEAN_COLUMN_NAMES) {
834
+ const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
835
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
836
+ out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
837
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
838
+ out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
839
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
840
+ out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
841
+ }
842
+ return out;
843
+ }
844
+ function rewriteInsertOrIgnore(sql) {
845
+ if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
846
+ return sql;
847
+ }
848
+ const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
849
+ return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
850
+ }
851
+ function rewriteInsertOrReplace(sql) {
852
+ const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
853
+ if (!match) {
854
+ return sql;
855
+ }
856
+ const rawTable = match[1];
857
+ const rawColumns = match[2];
858
+ const remainder = match[3];
859
+ const tableName = unqualifiedTableName(rawTable);
860
+ const conflictKeys = UPSERT_KEYS[tableName];
861
+ if (!conflictKeys?.length) {
862
+ return sql;
863
+ }
864
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
865
+ const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
866
+ const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
867
+ const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
868
+ return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
869
+ }
870
+ function rewriteSql(sql) {
871
+ let out = sql;
872
+ out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
873
+ out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
874
+ out = rewriteBooleanLiterals(out);
875
+ out = rewriteInsertOrReplace(out);
876
+ out = rewriteInsertOrIgnore(out);
877
+ return stripTrailingSemicolon(out);
878
+ }
879
+ function toBoolean(value) {
880
+ if (value === null || value === void 0) return value;
881
+ if (typeof value === "boolean") return value;
882
+ if (typeof value === "number") return value !== 0;
883
+ if (typeof value === "bigint") return value !== 0n;
884
+ if (typeof value === "string") {
885
+ const normalized = value.trim().toLowerCase();
886
+ if (normalized === "0" || normalized === "false") return false;
887
+ if (normalized === "1" || normalized === "true") return true;
888
+ }
889
+ return Boolean(value);
890
+ }
891
+ function countQuestionMarks(sql, end) {
892
+ let count = 0;
893
+ let inSingle = false;
894
+ let inDouble = false;
895
+ let inLineComment = false;
896
+ let inBlockComment = false;
897
+ for (let i = 0; i < end; i++) {
898
+ const ch = sql[i];
899
+ const next = sql[i + 1];
900
+ if (inLineComment) {
901
+ if (ch === "\n") inLineComment = false;
902
+ continue;
903
+ }
904
+ if (inBlockComment) {
905
+ if (ch === "*" && next === "/") {
906
+ inBlockComment = false;
907
+ i += 1;
908
+ }
909
+ continue;
910
+ }
911
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
912
+ inLineComment = true;
913
+ i += 1;
914
+ continue;
915
+ }
916
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
917
+ inBlockComment = true;
918
+ i += 1;
919
+ continue;
920
+ }
921
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
922
+ inSingle = !inSingle;
923
+ continue;
924
+ }
925
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
926
+ inDouble = !inDouble;
927
+ continue;
928
+ }
929
+ if (!inSingle && !inDouble && ch === "?") {
930
+ count += 1;
931
+ }
932
+ }
933
+ return count;
934
+ }
935
+ function findBooleanPlaceholderIndexes(sql) {
936
+ const indexes = /* @__PURE__ */ new Set();
937
+ for (const column of BOOLEAN_COLUMN_NAMES) {
938
+ const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
939
+ for (const match of sql.matchAll(pattern)) {
940
+ const matchText = match[0];
941
+ const qIndex = match.index + matchText.lastIndexOf("?");
942
+ indexes.add(countQuestionMarks(sql, qIndex + 1));
943
+ }
944
+ }
945
+ return indexes;
946
+ }
947
+ function coerceInsertBooleanArgs(sql, args) {
948
+ const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
949
+ if (!match) return;
950
+ const rawTable = match[1];
951
+ const rawColumns = match[2];
952
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
953
+ if (!boolColumns?.size) return;
954
+ const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
955
+ for (const [index, column] of columns.entries()) {
956
+ if (boolColumns.has(column) && index < args.length) {
957
+ args[index] = toBoolean(args[index]);
958
+ }
959
+ }
960
+ }
961
+ function coerceUpdateBooleanArgs(sql, args) {
962
+ const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
963
+ if (!match) return;
964
+ const rawTable = match[1];
965
+ const setClause = match[2];
966
+ const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
967
+ if (!boolColumns?.size) return;
968
+ const assignments = setClause.split(",");
969
+ let placeholderIndex = 0;
970
+ for (const assignment of assignments) {
971
+ if (!assignment.includes("?")) continue;
972
+ placeholderIndex += 1;
973
+ const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
974
+ if (colMatch && boolColumns.has(colMatch[1])) {
975
+ args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
976
+ }
977
+ }
978
+ }
979
+ function coerceBooleanArgs(sql, args) {
980
+ const nextArgs = [...args];
981
+ coerceInsertBooleanArgs(sql, nextArgs);
982
+ coerceUpdateBooleanArgs(sql, nextArgs);
983
+ const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
984
+ for (const index of placeholderIndexes) {
985
+ if (index > 0 && index <= nextArgs.length) {
986
+ nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
987
+ }
988
+ }
989
+ return nextArgs;
990
+ }
991
+ function convertQuestionMarksToDollarParams(sql) {
992
+ let out = "";
993
+ let placeholder = 0;
994
+ let inSingle = false;
995
+ let inDouble = false;
996
+ let inLineComment = false;
997
+ let inBlockComment = false;
998
+ for (let i = 0; i < sql.length; i++) {
999
+ const ch = sql[i];
1000
+ const next = sql[i + 1];
1001
+ if (inLineComment) {
1002
+ out += ch;
1003
+ if (ch === "\n") inLineComment = false;
1004
+ continue;
1005
+ }
1006
+ if (inBlockComment) {
1007
+ out += ch;
1008
+ if (ch === "*" && next === "/") {
1009
+ out += next;
1010
+ inBlockComment = false;
1011
+ i += 1;
1012
+ }
1013
+ continue;
1014
+ }
1015
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1016
+ out += ch + next;
1017
+ inLineComment = true;
1018
+ i += 1;
1019
+ continue;
1020
+ }
1021
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1022
+ out += ch + next;
1023
+ inBlockComment = true;
1024
+ i += 1;
1025
+ continue;
1026
+ }
1027
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1028
+ inSingle = !inSingle;
1029
+ out += ch;
1030
+ continue;
1031
+ }
1032
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1033
+ inDouble = !inDouble;
1034
+ out += ch;
1035
+ continue;
1036
+ }
1037
+ if (!inSingle && !inDouble && ch === "?") {
1038
+ placeholder += 1;
1039
+ out += `$${placeholder}`;
1040
+ continue;
1041
+ }
1042
+ out += ch;
1043
+ }
1044
+ return out;
1045
+ }
1046
+ function translateStatementForPostgres(stmt) {
1047
+ const normalized = normalizeStatement(stmt);
1048
+ if (normalized.kind === "named") {
1049
+ throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
1050
+ }
1051
+ const rewrittenSql = rewriteSql(normalized.sql);
1052
+ const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
1053
+ return {
1054
+ sql: convertQuestionMarksToDollarParams(rewrittenSql),
1055
+ args: coercedArgs
1056
+ };
1057
+ }
1058
+ function shouldBypassPostgres(stmt) {
1059
+ const normalized = normalizeStatement(stmt);
1060
+ if (normalized.kind === "named") {
1061
+ return true;
1062
+ }
1063
+ return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
1064
+ }
1065
+ function shouldFallbackOnError(error) {
1066
+ const message = error instanceof Error ? error.message : String(error);
1067
+ return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
1068
+ }
1069
+ function isReadQuery(sql) {
1070
+ const trimmed = sql.trimStart();
1071
+ return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
1072
+ }
1073
+ function buildRow(row, columns) {
1074
+ const values = columns.map((column) => row[column]);
1075
+ return Object.assign(values, row);
1076
+ }
1077
+ function buildResultSet(rows, rowsAffected = 0) {
1078
+ const columns = rows[0] ? Object.keys(rows[0]) : [];
1079
+ const resultRows = rows.map((row) => buildRow(row, columns));
1080
+ return {
1081
+ columns,
1082
+ columnTypes: columns.map(() => ""),
1083
+ rows: resultRows,
1084
+ rowsAffected,
1085
+ lastInsertRowid: void 0,
1086
+ toJSON() {
1087
+ return {
1088
+ columns,
1089
+ columnTypes: columns.map(() => ""),
1090
+ rows,
1091
+ rowsAffected,
1092
+ lastInsertRowid: void 0
1093
+ };
1094
+ }
1095
+ };
1096
+ }
1097
+ async function loadPrismaClient() {
1098
+ if (!prismaClientPromise) {
1099
+ prismaClientPromise = (async () => {
1100
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
1101
+ if (explicitPath) {
1102
+ const module2 = await import(pathToFileURL(explicitPath).href);
1103
+ const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
1104
+ if (!PrismaClient2) {
1105
+ throw new Error(`No PrismaClient export found at ${explicitPath}`);
1106
+ }
1107
+ return new PrismaClient2();
1108
+ }
1109
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os5.homedir(), "exe-db");
1110
+ const requireFromExeDb = createRequire(path6.join(exeDbRoot, "package.json"));
1111
+ const prismaEntry = requireFromExeDb.resolve("@prisma/client");
1112
+ const module = await import(pathToFileURL(prismaEntry).href);
1113
+ const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
1114
+ if (!PrismaClient) {
1115
+ throw new Error(`No PrismaClient export found in ${prismaEntry}`);
1116
+ }
1117
+ return new PrismaClient();
1118
+ })();
1119
+ }
1120
+ return prismaClientPromise;
1121
+ }
1122
+ async function ensureCompatibilityViews(prisma) {
1123
+ if (!compatibilityBootstrapPromise) {
1124
+ compatibilityBootstrapPromise = (async () => {
1125
+ for (const mapping of VIEW_MAPPINGS) {
1126
+ const relation = mapping.source.replace(/"/g, "");
1127
+ const rows = await prisma.$queryRawUnsafe(
1128
+ "SELECT to_regclass($1) AS regclass",
1129
+ relation
1130
+ );
1131
+ if (!rows[0]?.regclass) {
1132
+ continue;
1133
+ }
1134
+ await prisma.$executeRawUnsafe(
1135
+ `CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
1136
+ );
1137
+ }
1138
+ })();
1139
+ }
1140
+ return compatibilityBootstrapPromise;
1141
+ }
1142
+ async function executeOnPrisma(executor, stmt) {
1143
+ const translated = translateStatementForPostgres(stmt);
1144
+ if (isReadQuery(translated.sql)) {
1145
+ const rows = await executor.$queryRawUnsafe(
1146
+ translated.sql,
1147
+ ...translated.args
1148
+ );
1149
+ return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
1150
+ }
1151
+ const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
1152
+ return buildResultSet([], rowsAffected);
1153
+ }
1154
+ function splitSqlStatements(sql) {
1155
+ const parts = [];
1156
+ let current = "";
1157
+ let inSingle = false;
1158
+ let inDouble = false;
1159
+ let inLineComment = false;
1160
+ let inBlockComment = false;
1161
+ for (let i = 0; i < sql.length; i++) {
1162
+ const ch = sql[i];
1163
+ const next = sql[i + 1];
1164
+ if (inLineComment) {
1165
+ current += ch;
1166
+ if (ch === "\n") inLineComment = false;
1167
+ continue;
1168
+ }
1169
+ if (inBlockComment) {
1170
+ current += ch;
1171
+ if (ch === "*" && next === "/") {
1172
+ current += next;
1173
+ inBlockComment = false;
1174
+ i += 1;
1175
+ }
1176
+ continue;
1177
+ }
1178
+ if (!inSingle && !inDouble && ch === "-" && next === "-") {
1179
+ current += ch + next;
1180
+ inLineComment = true;
1181
+ i += 1;
1182
+ continue;
1183
+ }
1184
+ if (!inSingle && !inDouble && ch === "/" && next === "*") {
1185
+ current += ch + next;
1186
+ inBlockComment = true;
1187
+ i += 1;
1188
+ continue;
1189
+ }
1190
+ if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
1191
+ inSingle = !inSingle;
1192
+ current += ch;
1193
+ continue;
1194
+ }
1195
+ if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
1196
+ inDouble = !inDouble;
1197
+ current += ch;
1198
+ continue;
1199
+ }
1200
+ if (!inSingle && !inDouble && ch === ";") {
1201
+ if (current.trim()) {
1202
+ parts.push(current.trim());
1203
+ }
1204
+ current = "";
1205
+ continue;
1206
+ }
1207
+ current += ch;
1208
+ }
1209
+ if (current.trim()) {
1210
+ parts.push(current.trim());
1211
+ }
1212
+ return parts;
1213
+ }
1214
+ async function createPrismaDbAdapter(fallbackClient) {
1215
+ const prisma = await loadPrismaClient();
1216
+ await ensureCompatibilityViews(prisma);
1217
+ let closed = false;
1218
+ let adapter;
1219
+ const fallbackExecute = async (stmt, error) => {
1220
+ if (!fallbackClient) {
1221
+ if (error) throw error;
1222
+ throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
1223
+ }
1224
+ if (error) {
1225
+ process.stderr.write(
1226
+ `[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
1227
+ `
1228
+ );
1229
+ }
1230
+ return fallbackClient.execute(stmt);
1231
+ };
1232
+ adapter = {
1233
+ async execute(stmt) {
1234
+ if (shouldBypassPostgres(stmt)) {
1235
+ return fallbackExecute(stmt);
1236
+ }
1237
+ try {
1238
+ return await executeOnPrisma(prisma, stmt);
1239
+ } catch (error) {
1240
+ if (shouldFallbackOnError(error)) {
1241
+ return fallbackExecute(stmt, error);
1242
+ }
1243
+ throw error;
1244
+ }
1245
+ },
1246
+ async batch(stmts, mode) {
1247
+ if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
1248
+ if (!fallbackClient) {
1249
+ throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
1250
+ }
1251
+ return fallbackClient.batch(stmts, mode);
1252
+ }
1253
+ try {
1254
+ if (prisma.$transaction) {
1255
+ return await prisma.$transaction(async (tx) => {
1256
+ const results2 = [];
1257
+ for (const stmt of stmts) {
1258
+ results2.push(await executeOnPrisma(tx, stmt));
1259
+ }
1260
+ return results2;
1261
+ });
1262
+ }
1263
+ const results = [];
1264
+ for (const stmt of stmts) {
1265
+ results.push(await executeOnPrisma(prisma, stmt));
1266
+ }
1267
+ return results;
1268
+ } catch (error) {
1269
+ if (fallbackClient && shouldFallbackOnError(error)) {
1270
+ process.stderr.write(
1271
+ `[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
1272
+ `
1273
+ );
1274
+ return fallbackClient.batch(stmts, mode);
1275
+ }
1276
+ throw error;
1277
+ }
1278
+ },
1279
+ async migrate(stmts) {
1280
+ if (fallbackClient) {
1281
+ return fallbackClient.migrate(stmts);
1282
+ }
1283
+ return adapter.batch(stmts, "deferred");
1284
+ },
1285
+ async transaction(mode) {
1286
+ if (!fallbackClient) {
1287
+ throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
1288
+ }
1289
+ return fallbackClient.transaction(mode);
1290
+ },
1291
+ async executeMultiple(sql) {
1292
+ if (fallbackClient && shouldBypassPostgres(sql)) {
1293
+ return fallbackClient.executeMultiple(sql);
1294
+ }
1295
+ for (const statement of splitSqlStatements(sql)) {
1296
+ await adapter.execute(statement);
1297
+ }
1298
+ },
1299
+ async sync() {
1300
+ if (fallbackClient) {
1301
+ return fallbackClient.sync();
1302
+ }
1303
+ return { frame_no: 0, frames_synced: 0 };
1304
+ },
1305
+ close() {
1306
+ closed = true;
1307
+ prismaClientPromise = null;
1308
+ compatibilityBootstrapPromise = null;
1309
+ void prisma.$disconnect?.();
1310
+ },
1311
+ get closed() {
1312
+ return closed;
1313
+ },
1314
+ get protocol() {
1315
+ return "prisma-postgres";
1316
+ }
1317
+ };
1318
+ return adapter;
1319
+ }
1320
+ var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
1321
+ var init_database_adapter = __esm({
1322
+ "src/lib/database-adapter.ts"() {
1323
+ "use strict";
1324
+ VIEW_MAPPINGS = [
1325
+ { view: "memories", source: "memory.memory_records" },
1326
+ { view: "tasks", source: "memory.tasks" },
1327
+ { view: "behaviors", source: "memory.behaviors" },
1328
+ { view: "entities", source: "memory.entities" },
1329
+ { view: "relationships", source: "memory.relationships" },
1330
+ { view: "entity_memories", source: "memory.entity_memories" },
1331
+ { view: "entity_aliases", source: "memory.entity_aliases" },
1332
+ { view: "notifications", source: "memory.notifications" },
1333
+ { view: "messages", source: "memory.messages" },
1334
+ { view: "users", source: "wiki.users" },
1335
+ { view: "workspaces", source: "wiki.workspaces" },
1336
+ { view: "workspace_users", source: "wiki.workspace_users" },
1337
+ { view: "documents", source: "wiki.workspace_documents" },
1338
+ { view: "chats", source: "wiki.workspace_chats" }
1339
+ ];
1340
+ UPSERT_KEYS = {
1341
+ memories: ["id"],
1342
+ tasks: ["id"],
1343
+ behaviors: ["id"],
1344
+ entities: ["id"],
1345
+ relationships: ["id"],
1346
+ entity_aliases: ["alias"],
1347
+ notifications: ["id"],
1348
+ messages: ["id"],
1349
+ users: ["id"],
1350
+ workspaces: ["id"],
1351
+ workspace_users: ["id"],
1352
+ documents: ["id"],
1353
+ chats: ["id"]
1354
+ };
1355
+ BOOLEAN_COLUMNS_BY_TABLE = {
1356
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
1357
+ behaviors: /* @__PURE__ */ new Set(["active"]),
1358
+ notifications: /* @__PURE__ */ new Set(["read"]),
1359
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
1360
+ };
1361
+ BOOLEAN_COLUMN_NAMES = new Set(
1362
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
1363
+ );
1364
+ IMMEDIATE_FALLBACK_PATTERNS = [
1365
+ /\bPRAGMA\b/i,
1366
+ /\bsqlite_master\b/i,
1367
+ /(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
1368
+ /\bMATCH\b/i,
1369
+ /\bvector_distance_cos\s*\(/i,
1370
+ /\bjson_extract\s*\(/i,
1371
+ /\bjulianday\s*\(/i,
1372
+ /\bstrftime\s*\(/i,
1373
+ /\blast_insert_rowid\s*\(/i
1374
+ ];
1375
+ prismaClientPromise = null;
1376
+ compatibilityBootstrapPromise = null;
792
1377
  }
793
1378
  });
794
1379
 
795
1380
  // src/lib/database.ts
796
1381
  import { createClient } from "@libsql/client";
797
1382
  async function initDatabase(config) {
1383
+ if (_walCheckpointTimer) {
1384
+ clearInterval(_walCheckpointTimer);
1385
+ _walCheckpointTimer = null;
1386
+ }
1387
+ if (_daemonClient) {
1388
+ _daemonClient.close();
1389
+ _daemonClient = null;
1390
+ }
1391
+ if (_adapterClient && _adapterClient !== _resilientClient) {
1392
+ _adapterClient.close();
1393
+ }
1394
+ _adapterClient = null;
798
1395
  if (_client) {
799
1396
  _client.close();
800
1397
  _client = null;
@@ -808,6 +1405,7 @@ async function initDatabase(config) {
808
1405
  }
809
1406
  _client = createClient(opts);
810
1407
  _resilientClient = wrapWithRetry(_client);
1408
+ _adapterClient = _resilientClient;
811
1409
  _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
812
1410
  });
813
1411
  _client.execute("PRAGMA journal_mode = WAL").catch(() => {
@@ -818,11 +1416,17 @@ async function initDatabase(config) {
818
1416
  });
819
1417
  }, 3e4);
820
1418
  _walCheckpointTimer.unref();
1419
+ if (process.env.DATABASE_URL) {
1420
+ _adapterClient = await createPrismaDbAdapter(_resilientClient);
1421
+ }
821
1422
  }
822
1423
  function getClient() {
823
- if (!_resilientClient) {
1424
+ if (!_adapterClient) {
824
1425
  throw new Error("Database client not initialized. Call initDatabase() first.");
825
1426
  }
1427
+ if (process.env.DATABASE_URL) {
1428
+ return _adapterClient;
1429
+ }
826
1430
  if (process.env.EXE_IS_DAEMON === "1") {
827
1431
  return _resilientClient;
828
1432
  }
@@ -1762,16 +2366,18 @@ async function ensureSchema() {
1762
2366
  }
1763
2367
  }
1764
2368
  }
1765
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso;
2369
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso;
1766
2370
  var init_database = __esm({
1767
2371
  "src/lib/database.ts"() {
1768
2372
  "use strict";
1769
2373
  init_db_retry();
1770
2374
  init_employees();
2375
+ init_database_adapter();
1771
2376
  _client = null;
1772
2377
  _resilientClient = null;
1773
2378
  _walCheckpointTimer = null;
1774
2379
  _daemonClient = null;
2380
+ _adapterClient = null;
1775
2381
  initTurso = initDatabase;
1776
2382
  }
1777
2383
  });
@@ -1779,16 +2385,16 @@ var init_database = __esm({
1779
2385
  // src/lib/license.ts
1780
2386
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
1781
2387
  import { randomUUID } from "crypto";
1782
- import path6 from "path";
2388
+ import path7 from "path";
1783
2389
  import { jwtVerify, importSPKI } from "jose";
1784
2390
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
1785
2391
  var init_license = __esm({
1786
2392
  "src/lib/license.ts"() {
1787
2393
  "use strict";
1788
2394
  init_config();
1789
- LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
1790
- CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
1791
- DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
2395
+ LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
2396
+ CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
2397
+ DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
1792
2398
  PLAN_LIMITS = {
1793
2399
  free: { devices: 1, employees: 1, memories: 5e3 },
1794
2400
  pro: { devices: 3, employees: 5, memories: 1e5 },
@@ -1801,7 +2407,7 @@ var init_license = __esm({
1801
2407
 
1802
2408
  // src/lib/plan-limits.ts
1803
2409
  import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
1804
- import path7 from "path";
2410
+ import path8 from "path";
1805
2411
  function getLicenseSync() {
1806
2412
  try {
1807
2413
  if (!existsSync7(CACHE_PATH2)) return freeLicense();
@@ -1873,14 +2479,14 @@ var init_plan_limits = __esm({
1873
2479
  this.name = "PlanLimitError";
1874
2480
  }
1875
2481
  };
1876
- CACHE_PATH2 = path7.join(EXE_AI_DIR, "license-cache.json");
2482
+ CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
1877
2483
  }
1878
2484
  });
1879
2485
 
1880
2486
  // src/lib/notifications.ts
1881
2487
  import crypto from "crypto";
1882
- import path8 from "path";
1883
- import os5 from "os";
2488
+ import path9 from "path";
2489
+ import os6 from "os";
1884
2490
  import {
1885
2491
  readFileSync as readFileSync8,
1886
2492
  readdirSync,
@@ -2044,8 +2650,8 @@ var init_state_bus = __esm({
2044
2650
 
2045
2651
  // src/lib/tasks-crud.ts
2046
2652
  import crypto3 from "crypto";
2047
- import path9 from "path";
2048
- import os6 from "os";
2653
+ import path10 from "path";
2654
+ import os7 from "os";
2049
2655
  import { execSync as execSync4 } from "child_process";
2050
2656
  import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
2051
2657
  import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
@@ -2223,8 +2829,8 @@ ${laneWarning}` : laneWarning;
2223
2829
  }
2224
2830
  if (input.baseDir) {
2225
2831
  try {
2226
- await mkdir3(path9.join(input.baseDir, "exe", "output"), { recursive: true });
2227
- await mkdir3(path9.join(input.baseDir, "exe", "research"), { recursive: true });
2832
+ await mkdir3(path10.join(input.baseDir, "exe", "output"), { recursive: true });
2833
+ await mkdir3(path10.join(input.baseDir, "exe", "research"), { recursive: true });
2228
2834
  await ensureArchitectureDoc(input.baseDir, input.projectName);
2229
2835
  await ensureGitignoreExe(input.baseDir);
2230
2836
  } catch {
@@ -2260,9 +2866,9 @@ ${laneWarning}` : laneWarning;
2260
2866
  });
2261
2867
  if (input.baseDir) {
2262
2868
  try {
2263
- const EXE_OS_DIR = path9.join(os6.homedir(), ".exe-os");
2264
- const mdPath = path9.join(EXE_OS_DIR, taskFile);
2265
- const mdDir = path9.dirname(mdPath);
2869
+ const EXE_OS_DIR = path10.join(os7.homedir(), ".exe-os");
2870
+ const mdPath = path10.join(EXE_OS_DIR, taskFile);
2871
+ const mdDir = path10.dirname(mdPath);
2266
2872
  if (!existsSync9(mdDir)) await mkdir3(mdDir, { recursive: true });
2267
2873
  const reviewer = input.reviewer ?? input.assignedBy;
2268
2874
  const mdContent = `# ${input.title}
@@ -2563,7 +3169,7 @@ async function deleteTaskCore(taskId, _baseDir) {
2563
3169
  return { taskFile, assignedTo, assignedBy, taskSlug };
2564
3170
  }
2565
3171
  async function ensureArchitectureDoc(baseDir, projectName) {
2566
- const archPath = path9.join(baseDir, "exe", "ARCHITECTURE.md");
3172
+ const archPath = path10.join(baseDir, "exe", "ARCHITECTURE.md");
2567
3173
  try {
2568
3174
  if (existsSync9(archPath)) return;
2569
3175
  const template = [
@@ -2598,7 +3204,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
2598
3204
  }
2599
3205
  }
2600
3206
  async function ensureGitignoreExe(baseDir) {
2601
- const gitignorePath = path9.join(baseDir, ".gitignore");
3207
+ const gitignorePath = path10.join(baseDir, ".gitignore");
2602
3208
  try {
2603
3209
  if (existsSync9(gitignorePath)) {
2604
3210
  const content = readFileSync9(gitignorePath, "utf-8");
@@ -2632,13 +3238,13 @@ var init_tasks_crud = __esm({
2632
3238
  });
2633
3239
 
2634
3240
  // src/lib/tasks-review.ts
2635
- import path10 from "path";
3241
+ import path11 from "path";
2636
3242
  import { existsSync as existsSync10, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
2637
3243
  async function countPendingReviews(sessionScope) {
2638
3244
  const client = getClient();
2639
3245
  if (sessionScope) {
2640
3246
  const result3 = await client.execute({
2641
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND (session_scope = ? OR session_scope IS NULL)",
3247
+ sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
2642
3248
  args: [sessionScope]
2643
3249
  });
2644
3250
  return Number(result3.rows[0]?.cnt) || 0;
@@ -2814,11 +3420,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
2814
3420
  );
2815
3421
  }
2816
3422
  try {
2817
- const cacheDir = path10.join(EXE_AI_DIR, "session-cache");
3423
+ const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
2818
3424
  if (existsSync10(cacheDir)) {
2819
3425
  for (const f of readdirSync2(cacheDir)) {
2820
3426
  if (f.startsWith("review-notified-")) {
2821
- unlinkSync3(path10.join(cacheDir, f));
3427
+ unlinkSync3(path11.join(cacheDir, f));
2822
3428
  }
2823
3429
  }
2824
3430
  }
@@ -2839,7 +3445,7 @@ var init_tasks_review = __esm({
2839
3445
  });
2840
3446
 
2841
3447
  // src/lib/tasks-chain.ts
2842
- import path11 from "path";
3448
+ import path12 from "path";
2843
3449
  import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
2844
3450
  async function cascadeUnblock(taskId, baseDir, now) {
2845
3451
  const client = getClient();
@@ -2856,7 +3462,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
2856
3462
  });
2857
3463
  for (const ur of unblockedRows.rows) {
2858
3464
  try {
2859
- const ubFile = path11.join(baseDir, String(ur.task_file));
3465
+ const ubFile = path12.join(baseDir, String(ur.task_file));
2860
3466
  let ubContent = await readFile3(ubFile, "utf-8");
2861
3467
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
2862
3468
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -2925,7 +3531,7 @@ var init_tasks_chain = __esm({
2925
3531
 
2926
3532
  // src/lib/project-name.ts
2927
3533
  import { execSync as execSync5 } from "child_process";
2928
- import path12 from "path";
3534
+ import path13 from "path";
2929
3535
  function getProjectName(cwd) {
2930
3536
  const dir = cwd ?? process.cwd();
2931
3537
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -2938,7 +3544,7 @@ function getProjectName(cwd) {
2938
3544
  timeout: 2e3,
2939
3545
  stdio: ["pipe", "pipe", "pipe"]
2940
3546
  }).trim();
2941
- repoRoot = path12.dirname(gitCommonDir);
3547
+ repoRoot = path13.dirname(gitCommonDir);
2942
3548
  } catch {
2943
3549
  repoRoot = execSync5("git rev-parse --show-toplevel", {
2944
3550
  cwd: dir,
@@ -2947,11 +3553,11 @@ function getProjectName(cwd) {
2947
3553
  stdio: ["pipe", "pipe", "pipe"]
2948
3554
  }).trim();
2949
3555
  }
2950
- _cached2 = path12.basename(repoRoot);
3556
+ _cached2 = path13.basename(repoRoot);
2951
3557
  _cachedCwd = dir;
2952
3558
  return _cached2;
2953
3559
  } catch {
2954
- _cached2 = path12.basename(dir);
3560
+ _cached2 = path13.basename(dir);
2955
3561
  _cachedCwd = dir;
2956
3562
  return _cached2;
2957
3563
  }
@@ -3424,7 +4030,7 @@ __export(tasks_exports, {
3424
4030
  updateTaskStatus: () => updateTaskStatus,
3425
4031
  writeCheckpoint: () => writeCheckpoint
3426
4032
  });
3427
- import path13 from "path";
4033
+ import path14 from "path";
3428
4034
  import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "fs";
3429
4035
  async function createTask(input) {
3430
4036
  const result2 = await createTaskCore(input);
@@ -3444,8 +4050,8 @@ async function updateTask(input) {
3444
4050
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
3445
4051
  try {
3446
4052
  const agent = String(row.assigned_to);
3447
- const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
3448
- const cachePath = path13.join(cacheDir, `current-task-${agent}.json`);
4053
+ const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
4054
+ const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
3449
4055
  if (input.status === "in_progress") {
3450
4056
  mkdirSync5(cacheDir, { recursive: true });
3451
4057
  writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
@@ -3916,12 +4522,12 @@ __export(tmux_routing_exports, {
3916
4522
  });
3917
4523
  import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
3918
4524
  import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync11, appendFileSync, readdirSync as readdirSync3 } from "fs";
3919
- import path14 from "path";
3920
- import os7 from "os";
4525
+ import path15 from "path";
4526
+ import os8 from "os";
3921
4527
  import { fileURLToPath } from "url";
3922
4528
  import { unlinkSync as unlinkSync5 } from "fs";
3923
4529
  function spawnLockPath(sessionName) {
3924
- return path14.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
4530
+ return path15.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
3925
4531
  }
3926
4532
  function isProcessAlive(pid) {
3927
4533
  try {
@@ -3958,8 +4564,8 @@ function releaseSpawnLock(sessionName) {
3958
4564
  function resolveBehaviorsExporterScript() {
3959
4565
  try {
3960
4566
  const thisFile = fileURLToPath(import.meta.url);
3961
- const scriptPath = path14.join(
3962
- path14.dirname(thisFile),
4567
+ const scriptPath = path15.join(
4568
+ path15.dirname(thisFile),
3963
4569
  "..",
3964
4570
  "bin",
3965
4571
  "exe-export-behaviors.js"
@@ -4034,7 +4640,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4034
4640
  mkdirSync6(SESSION_CACHE, { recursive: true });
4035
4641
  }
4036
4642
  const rootExe = extractRootExe(parentExe) ?? parentExe;
4037
- const filePath = path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
4643
+ const filePath = path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
4038
4644
  writeFileSync7(filePath, JSON.stringify({
4039
4645
  parentExe: rootExe,
4040
4646
  dispatchedBy: dispatchedBy || rootExe,
@@ -4043,7 +4649,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4043
4649
  }
4044
4650
  function getParentExe(sessionKey) {
4045
4651
  try {
4046
- const data = JSON.parse(readFileSync10(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4652
+ const data = JSON.parse(readFileSync10(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4047
4653
  return data.parentExe || null;
4048
4654
  } catch {
4049
4655
  return null;
@@ -4052,7 +4658,7 @@ function getParentExe(sessionKey) {
4052
4658
  function getDispatchedBy(sessionKey) {
4053
4659
  try {
4054
4660
  const data = JSON.parse(readFileSync10(
4055
- path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
4661
+ path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
4056
4662
  "utf8"
4057
4663
  ));
4058
4664
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -4238,7 +4844,7 @@ function sendIntercom(targetSession) {
4238
4844
  try {
4239
4845
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
4240
4846
  const agent = baseAgentName(rawAgent);
4241
- const markerPath = path14.join(SESSION_CACHE, `current-task-${agent}.json`);
4847
+ const markerPath = path15.join(SESSION_CACHE, `current-task-${agent}.json`);
4242
4848
  if (existsSync11(markerPath)) {
4243
4849
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
4244
4850
  return "debounced";
@@ -4248,7 +4854,7 @@ function sendIntercom(targetSession) {
4248
4854
  try {
4249
4855
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
4250
4856
  const agent = baseAgentName(rawAgent);
4251
- const taskDir = path14.join(process.cwd(), "exe", agent);
4857
+ const taskDir = path15.join(process.cwd(), "exe", agent);
4252
4858
  if (existsSync11(taskDir)) {
4253
4859
  const files = readdirSync3(taskDir).filter(
4254
4860
  (f) => f.endsWith(".md") && f !== "DONE.txt"
@@ -4382,8 +4988,8 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
4382
4988
  const transport = getTransport();
4383
4989
  const sessionName = employeeSessionName(employeeName2, exeSession2, opts?.instance);
4384
4990
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName2}${opts.instance}` : employeeName2;
4385
- const logDir = path14.join(os7.homedir(), ".exe-os", "session-logs");
4386
- const logFile = path14.join(logDir, `${instanceLabel}-${Date.now()}.log`);
4991
+ const logDir = path15.join(os8.homedir(), ".exe-os", "session-logs");
4992
+ const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
4387
4993
  if (!existsSync11(logDir)) {
4388
4994
  mkdirSync6(logDir, { recursive: true });
4389
4995
  }
@@ -4391,14 +4997,14 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
4391
4997
  let cleanupSuffix = "";
4392
4998
  try {
4393
4999
  const thisFile = fileURLToPath(import.meta.url);
4394
- const cleanupScript = path14.join(path14.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
5000
+ const cleanupScript = path15.join(path15.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
4395
5001
  if (existsSync11(cleanupScript)) {
4396
5002
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName2}" "${exeSession2}"`;
4397
5003
  }
4398
5004
  } catch {
4399
5005
  }
4400
5006
  try {
4401
- const claudeJsonPath = path14.join(os7.homedir(), ".claude.json");
5007
+ const claudeJsonPath = path15.join(os8.homedir(), ".claude.json");
4402
5008
  let claudeJson = {};
4403
5009
  try {
4404
5010
  claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
@@ -4413,10 +5019,10 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
4413
5019
  } catch {
4414
5020
  }
4415
5021
  try {
4416
- const settingsDir = path14.join(os7.homedir(), ".claude", "projects");
5022
+ const settingsDir = path15.join(os8.homedir(), ".claude", "projects");
4417
5023
  const normalizedKey = (opts?.cwd ?? projectDir2).replace(/\//g, "-").replace(/^-/, "");
4418
- const projSettingsDir = path14.join(settingsDir, normalizedKey);
4419
- const settingsPath = path14.join(projSettingsDir, "settings.json");
5024
+ const projSettingsDir = path15.join(settingsDir, normalizedKey);
5025
+ const settingsPath = path15.join(projSettingsDir, "settings.json");
4420
5026
  let settings = {};
4421
5027
  try {
4422
5028
  settings = JSON.parse(readFileSync10(settingsPath, "utf8"));
@@ -4463,8 +5069,8 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
4463
5069
  let behaviorsFlag = "";
4464
5070
  let legacyFallbackWarned = false;
4465
5071
  if (!useExeAgent && !useBinSymlink) {
4466
- const identityPath = path14.join(
4467
- os7.homedir(),
5072
+ const identityPath = path15.join(
5073
+ os8.homedir(),
4468
5074
  ".exe-os",
4469
5075
  "identity",
4470
5076
  `${employeeName2}.md`
@@ -4479,7 +5085,7 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
4479
5085
  }
4480
5086
  const behaviorsFile = exportBehaviorsSync(
4481
5087
  employeeName2,
4482
- path14.basename(spawnCwd),
5088
+ path15.basename(spawnCwd),
4483
5089
  sessionName
4484
5090
  );
4485
5091
  if (behaviorsFile) {
@@ -4494,9 +5100,9 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
4494
5100
  }
4495
5101
  let sessionContextFlag = "";
4496
5102
  try {
4497
- const ctxDir = path14.join(os7.homedir(), ".exe-os", "session-cache");
5103
+ const ctxDir = path15.join(os8.homedir(), ".exe-os", "session-cache");
4498
5104
  mkdirSync6(ctxDir, { recursive: true });
4499
- const ctxFile = path14.join(ctxDir, `session-context-${sessionName}.md`);
5105
+ const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
4500
5106
  const ctxContent = [
4501
5107
  `## Session Context`,
4502
5108
  `You are running in tmux session: ${sessionName}.`,
@@ -4580,7 +5186,7 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
4580
5186
  transport.pipeLog(sessionName, logFile);
4581
5187
  try {
4582
5188
  const mySession = getMySession();
4583
- const dispatchInfo = path14.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5189
+ const dispatchInfo = path15.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
4584
5190
  writeFileSync7(dispatchInfo, JSON.stringify({
4585
5191
  dispatchedBy: mySession,
4586
5192
  rootExe: exeSession2,
@@ -4655,15 +5261,15 @@ var init_tmux_routing = __esm({
4655
5261
  init_intercom_queue();
4656
5262
  init_plan_limits();
4657
5263
  init_employees();
4658
- SPAWN_LOCK_DIR = path14.join(os7.homedir(), ".exe-os", "spawn-locks");
4659
- SESSION_CACHE = path14.join(os7.homedir(), ".exe-os", "session-cache");
5264
+ SPAWN_LOCK_DIR = path15.join(os8.homedir(), ".exe-os", "spawn-locks");
5265
+ SESSION_CACHE = path15.join(os8.homedir(), ".exe-os", "session-cache");
4660
5266
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
4661
5267
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
4662
5268
  VERIFY_PANE_LINES = 200;
4663
5269
  INTERCOM_DEBOUNCE_MS = 3e4;
4664
5270
  CODEX_DEBOUNCE_MS = 12e4;
4665
- INTERCOM_LOG2 = path14.join(os7.homedir(), ".exe-os", "intercom.log");
4666
- DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
5271
+ INTERCOM_LOG2 = path15.join(os8.homedir(), ".exe-os", "intercom.log");
5272
+ DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
4667
5273
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
4668
5274
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
4669
5275
  }
@@ -4682,7 +5288,7 @@ __export(shard_manager_exports, {
4682
5288
  listShards: () => listShards,
4683
5289
  shardExists: () => shardExists
4684
5290
  });
4685
- import path16 from "path";
5291
+ import path17 from "path";
4686
5292
  import { existsSync as existsSync13, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
4687
5293
  import { createClient as createClient2 } from "@libsql/client";
4688
5294
  function initShardManager(encryptionKey) {
@@ -4708,7 +5314,7 @@ function getShardClient(projectName) {
4708
5314
  }
4709
5315
  const cached = _shards.get(safeName);
4710
5316
  if (cached) return cached;
4711
- const dbPath = path16.join(SHARDS_DIR, `${safeName}.db`);
5317
+ const dbPath = path17.join(SHARDS_DIR, `${safeName}.db`);
4712
5318
  const client = createClient2({
4713
5319
  url: `file:${dbPath}`,
4714
5320
  encryptionKey: _encryptionKey
@@ -4718,7 +5324,7 @@ function getShardClient(projectName) {
4718
5324
  }
4719
5325
  function shardExists(projectName) {
4720
5326
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
4721
- return existsSync13(path16.join(SHARDS_DIR, `${safeName}.db`));
5327
+ return existsSync13(path17.join(SHARDS_DIR, `${safeName}.db`));
4722
5328
  }
4723
5329
  function listShards() {
4724
5330
  if (!existsSync13(SHARDS_DIR)) return [];
@@ -4795,7 +5401,23 @@ async function ensureShardSchema(client) {
4795
5401
  // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
4796
5402
  "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
4797
5403
  "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
4798
- "ALTER TABLE memories ADD COLUMN trajectory TEXT"
5404
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT",
5405
+ // Metadata enrichment columns (must match database.ts)
5406
+ "ALTER TABLE memories ADD COLUMN intent TEXT",
5407
+ "ALTER TABLE memories ADD COLUMN outcome TEXT",
5408
+ "ALTER TABLE memories ADD COLUMN domain TEXT",
5409
+ "ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
5410
+ "ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
5411
+ "ALTER TABLE memories ADD COLUMN chain_position TEXT",
5412
+ "ALTER TABLE memories ADD COLUMN review_status TEXT",
5413
+ "ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
5414
+ "ALTER TABLE memories ADD COLUMN file_paths TEXT",
5415
+ "ALTER TABLE memories ADD COLUMN commit_hash TEXT",
5416
+ "ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
5417
+ "ALTER TABLE memories ADD COLUMN token_cost REAL",
5418
+ "ALTER TABLE memories ADD COLUMN audience TEXT",
5419
+ "ALTER TABLE memories ADD COLUMN language_type TEXT",
5420
+ "ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
4799
5421
  ]) {
4800
5422
  try {
4801
5423
  await client.execute(col);
@@ -4907,7 +5529,7 @@ var init_shard_manager = __esm({
4907
5529
  "src/lib/shard-manager.ts"() {
4908
5530
  "use strict";
4909
5531
  init_config();
4910
- SHARDS_DIR = path16.join(EXE_AI_DIR, "shards");
5532
+ SHARDS_DIR = path17.join(EXE_AI_DIR, "shards");
4911
5533
  _shards = /* @__PURE__ */ new Map();
4912
5534
  _encryptionKey = null;
4913
5535
  _shardingEnabled = false;
@@ -5112,15 +5734,15 @@ init_database();
5112
5734
  // src/lib/keychain.ts
5113
5735
  import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
5114
5736
  import { existsSync as existsSync12 } from "fs";
5115
- import path15 from "path";
5116
- import os8 from "os";
5737
+ import path16 from "path";
5738
+ import os9 from "os";
5117
5739
  var SERVICE = "exe-mem";
5118
5740
  var ACCOUNT = "master-key";
5119
5741
  function getKeyDir() {
5120
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path15.join(os8.homedir(), ".exe-os");
5742
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path16.join(os9.homedir(), ".exe-os");
5121
5743
  }
5122
5744
  function getKeyPath() {
5123
- return path15.join(getKeyDir(), "master.key");
5745
+ return path16.join(getKeyDir(), "master.key");
5124
5746
  }
5125
5747
  async function tryKeytar() {
5126
5748
  try {
@@ -5143,7 +5765,7 @@ async function getMasterKey() {
5143
5765
  const keyPath = getKeyPath();
5144
5766
  if (!existsSync12(keyPath)) {
5145
5767
  process.stderr.write(
5146
- `[keychain] Key not found at ${keyPath} (HOME=${os8.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
5768
+ `[keychain] Key not found at ${keyPath} (HOME=${os9.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
5147
5769
  `
5148
5770
  );
5149
5771
  return null;