@askexenow/exe-os 0.9.16 → 0.9.18

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 (97) hide show
  1. package/dist/bin/backfill-conversations.js +1242 -909
  2. package/dist/bin/backfill-responses.js +1245 -912
  3. package/dist/bin/backfill-vectors.js +1244 -906
  4. package/dist/bin/cleanup-stale-review-tasks.js +1870 -417
  5. package/dist/bin/cli.js +217 -107
  6. package/dist/bin/exe-agent-config.js +2 -2
  7. package/dist/bin/exe-agent.js +62 -0
  8. package/dist/bin/exe-assign.js +346 -10
  9. package/dist/bin/exe-boot.js +387 -32
  10. package/dist/bin/exe-call.js +72 -2
  11. package/dist/bin/exe-cloud.js +8 -0
  12. package/dist/bin/exe-dispatch.js +1821 -225
  13. package/dist/bin/exe-doctor.js +720 -52
  14. package/dist/bin/exe-export-behaviors.js +1429 -148
  15. package/dist/bin/exe-forget.js +1408 -34
  16. package/dist/bin/exe-gateway.js +1629 -1295
  17. package/dist/bin/exe-heartbeat.js +1899 -448
  18. package/dist/bin/exe-kill.js +1624 -346
  19. package/dist/bin/exe-launch-agent.js +726 -90
  20. package/dist/bin/exe-link.js +27 -8
  21. package/dist/bin/exe-new-employee.js +75 -9
  22. package/dist/bin/exe-pending-messages.js +2769 -1316
  23. package/dist/bin/exe-pending-notifications.js +2829 -1376
  24. package/dist/bin/exe-pending-reviews.js +2847 -1392
  25. package/dist/bin/exe-rename.js +89 -8
  26. package/dist/bin/exe-review.js +1494 -312
  27. package/dist/bin/exe-search.js +1608 -1300
  28. package/dist/bin/exe-session-cleanup.js +194 -91
  29. package/dist/bin/exe-settings.js +10 -2
  30. package/dist/bin/exe-start-codex.js +769 -120
  31. package/dist/bin/exe-start-opencode.js +763 -108
  32. package/dist/bin/exe-status.js +1887 -434
  33. package/dist/bin/exe-team.js +1782 -324
  34. package/dist/bin/git-sweep.js +408 -33
  35. package/dist/bin/graph-backfill.js +681 -27
  36. package/dist/bin/graph-export.js +1419 -141
  37. package/dist/bin/install.js +4 -8
  38. package/dist/bin/intercom-check.js +8641 -0
  39. package/dist/bin/scan-tasks.js +553 -38
  40. package/dist/bin/setup.js +82 -10
  41. package/dist/bin/shard-migrate.js +682 -28
  42. package/dist/gateway/index.js +1629 -1295
  43. package/dist/hooks/bug-report-worker.js +1136 -183
  44. package/dist/hooks/codex-stop-task-finalizer.js +1060 -107
  45. package/dist/hooks/commit-complete.js +408 -33
  46. package/dist/hooks/error-recall.js +1608 -1300
  47. package/dist/hooks/ingest-worker.js +250 -7966
  48. package/dist/hooks/ingest.js +707 -119
  49. package/dist/hooks/instructions-loaded.js +383 -20
  50. package/dist/hooks/notification.js +383 -20
  51. package/dist/hooks/post-compact.js +384 -21
  52. package/dist/hooks/{response-ingest-worker.js → post-tool-combined.js} +4157 -1716
  53. package/dist/hooks/pre-compact.js +486 -45
  54. package/dist/hooks/pre-tool-use.js +385 -22
  55. package/dist/hooks/prompt-submit.js +291 -96
  56. package/dist/hooks/session-end.js +490 -48
  57. package/dist/hooks/session-start.js +236 -22
  58. package/dist/hooks/stop.js +192 -50
  59. package/dist/hooks/subagent-stop.js +95 -18
  60. package/dist/hooks/summary-worker.js +205 -361
  61. package/dist/index.js +221 -105
  62. package/dist/lib/agent-config.js +2 -2
  63. package/dist/lib/cloud-sync.js +27 -8
  64. package/dist/lib/consolidation.js +437 -41
  65. package/dist/lib/database.js +20 -8
  66. package/dist/lib/db-daemon-client.js +2 -2
  67. package/dist/lib/db.js +20 -8
  68. package/dist/lib/device-registry.js +27 -8
  69. package/dist/lib/employee-templates.js +62 -0
  70. package/dist/lib/employees.js +2 -2
  71. package/dist/lib/exe-daemon.js +6703 -6259
  72. package/dist/lib/hybrid-search.js +1608 -1300
  73. package/dist/lib/identity.js +1 -1
  74. package/dist/lib/messaging.js +11 -3
  75. package/dist/lib/reminders.js +1 -1
  76. package/dist/lib/schedules.js +718 -26
  77. package/dist/lib/session-registry.js +11 -0
  78. package/dist/lib/skill-learning.js +639 -9
  79. package/dist/lib/store.js +676 -27
  80. package/dist/lib/tasks.js +665 -27
  81. package/dist/lib/tmux-routing.js +732 -94
  82. package/dist/lib/token-spend.js +1 -1
  83. package/dist/mcp/server.js +856 -383
  84. package/dist/mcp/tools/complete-reminder.js +1 -1
  85. package/dist/mcp/tools/create-reminder.js +1 -1
  86. package/dist/mcp/tools/create-task.js +616 -46
  87. package/dist/mcp/tools/deactivate-behavior.js +9 -1
  88. package/dist/mcp/tools/list-reminders.js +1 -1
  89. package/dist/mcp/tools/list-tasks.js +10 -2
  90. package/dist/mcp/tools/send-message.js +11 -3
  91. package/dist/mcp/tools/update-task.js +677 -39
  92. package/dist/runtime/index.js +425 -37
  93. package/dist/tui/App.js +365 -34
  94. package/package.json +5 -2
  95. package/src/commands/exe/intercom.md +6 -17
  96. package/dist/bin/wiki-sync.js +0 -2991
  97. package/dist/hooks/prompt-ingest-worker.js +0 -3979
@@ -35,12 +35,25 @@ async function ensurePrivateDir(dirPath) {
35
35
  } catch {
36
36
  }
37
37
  }
38
+ function ensurePrivateDirSync(dirPath) {
39
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
40
+ try {
41
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
42
+ } catch {
43
+ }
44
+ }
38
45
  async function enforcePrivateFile(filePath) {
39
46
  try {
40
47
  await chmod(filePath, PRIVATE_FILE_MODE);
41
48
  } catch {
42
49
  }
43
50
  }
51
+ function enforcePrivateFileSync(filePath) {
52
+ try {
53
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
54
+ } catch {
55
+ }
56
+ }
44
57
  var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
45
58
  var init_secure_files = __esm({
46
59
  "src/lib/secure-files.ts"() {
@@ -51,6 +64,21 @@ var init_secure_files = __esm({
51
64
  });
52
65
 
53
66
  // src/lib/config.ts
67
+ var config_exports = {};
68
+ __export(config_exports, {
69
+ CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
70
+ CONFIG_PATH: () => CONFIG_PATH,
71
+ CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
72
+ DB_PATH: () => DB_PATH,
73
+ EXE_AI_DIR: () => EXE_AI_DIR,
74
+ LEGACY_LANCE_PATH: () => LEGACY_LANCE_PATH,
75
+ MODELS_DIR: () => MODELS_DIR,
76
+ loadConfig: () => loadConfig,
77
+ loadConfigFrom: () => loadConfigFrom,
78
+ loadConfigSync: () => loadConfigSync,
79
+ migrateConfig: () => migrateConfig,
80
+ saveConfig: () => saveConfig
81
+ });
54
82
  import { readFile, writeFile } from "fs/promises";
55
83
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
56
84
  import path from "path";
@@ -154,6 +182,46 @@ async function loadConfig() {
154
182
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
155
183
  }
156
184
  }
185
+ function loadConfigSync() {
186
+ const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
187
+ const configPath = path.join(dir, "config.json");
188
+ if (!existsSync2(configPath)) {
189
+ return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
190
+ }
191
+ try {
192
+ const raw = readFileSync(configPath, "utf-8");
193
+ let parsed = JSON.parse(raw);
194
+ parsed = migrateLegacyConfig(parsed);
195
+ const { config: migratedCfg } = migrateConfig(parsed);
196
+ normalizeScalingRoadmap(migratedCfg);
197
+ normalizeSessionLifecycle(migratedCfg);
198
+ normalizeAutoUpdate(migratedCfg);
199
+ return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
200
+ } catch {
201
+ return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
202
+ }
203
+ }
204
+ async function saveConfig(config) {
205
+ const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
206
+ await ensurePrivateDir(dir);
207
+ const configPath = path.join(dir, "config.json");
208
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
209
+ await enforcePrivateFile(configPath);
210
+ }
211
+ async function loadConfigFrom(configPath) {
212
+ const raw = await readFile(configPath, "utf-8");
213
+ try {
214
+ let parsed = JSON.parse(raw);
215
+ parsed = migrateLegacyConfig(parsed);
216
+ const { config: migratedCfg } = migrateConfig(parsed);
217
+ normalizeScalingRoadmap(migratedCfg);
218
+ normalizeSessionLifecycle(migratedCfg);
219
+ normalizeAutoUpdate(migratedCfg);
220
+ return { ...DEFAULT_CONFIG, ...migratedCfg };
221
+ } catch {
222
+ return { ...DEFAULT_CONFIG };
223
+ }
224
+ }
157
225
  var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
158
226
  var init_config = __esm({
159
227
  "src/lib/config.ts"() {
@@ -340,9 +408,9 @@ var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
340
408
  var init_db_retry = __esm({
341
409
  "src/lib/db-retry.ts"() {
342
410
  "use strict";
343
- MAX_RETRIES = 3;
344
- BASE_DELAY_MS = 200;
345
- MAX_JITTER_MS = 300;
411
+ MAX_RETRIES = 5;
412
+ BASE_DELAY_MS = 250;
413
+ MAX_JITTER_MS = 400;
346
414
  }
347
415
  });
348
416
 
@@ -930,7 +998,696 @@ var init_database_adapter = __esm({
930
998
  }
931
999
  });
932
1000
 
1001
+ // src/types/memory.ts
1002
+ var EMBEDDING_DIM;
1003
+ var init_memory = __esm({
1004
+ "src/types/memory.ts"() {
1005
+ "use strict";
1006
+ EMBEDDING_DIM = 1024;
1007
+ }
1008
+ });
1009
+
1010
+ // src/lib/daemon-auth.ts
1011
+ import crypto from "crypto";
1012
+ import path4 from "path";
1013
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
1014
+ function normalizeToken(token) {
1015
+ if (!token) return null;
1016
+ const trimmed = token.trim();
1017
+ return trimmed.length > 0 ? trimmed : null;
1018
+ }
1019
+ function readDaemonToken() {
1020
+ try {
1021
+ if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
1022
+ return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
1023
+ } catch {
1024
+ return null;
1025
+ }
1026
+ }
1027
+ function ensureDaemonToken(seed) {
1028
+ const existing = readDaemonToken();
1029
+ if (existing) return existing;
1030
+ const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
1031
+ ensurePrivateDirSync(EXE_AI_DIR);
1032
+ writeFileSync2(DAEMON_TOKEN_PATH, `${token}
1033
+ `, "utf8");
1034
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
1035
+ return token;
1036
+ }
1037
+ var DAEMON_TOKEN_PATH;
1038
+ var init_daemon_auth = __esm({
1039
+ "src/lib/daemon-auth.ts"() {
1040
+ "use strict";
1041
+ init_config();
1042
+ init_secure_files();
1043
+ DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
1044
+ }
1045
+ });
1046
+
1047
+ // src/lib/exe-daemon-client.ts
1048
+ import net from "net";
1049
+ import os4 from "os";
1050
+ import { spawn } from "child_process";
1051
+ import { randomUUID } from "crypto";
1052
+ import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
1053
+ import path5 from "path";
1054
+ import { fileURLToPath } from "url";
1055
+ function handleData(chunk) {
1056
+ _buffer += chunk.toString();
1057
+ if (_buffer.length > MAX_BUFFER) {
1058
+ _buffer = "";
1059
+ return;
1060
+ }
1061
+ let newlineIdx;
1062
+ while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
1063
+ const line = _buffer.slice(0, newlineIdx).trim();
1064
+ _buffer = _buffer.slice(newlineIdx + 1);
1065
+ if (!line) continue;
1066
+ try {
1067
+ const response = JSON.parse(line);
1068
+ const id = response.id;
1069
+ if (!id) continue;
1070
+ const entry = _pending.get(id);
1071
+ if (entry) {
1072
+ clearTimeout(entry.timer);
1073
+ _pending.delete(id);
1074
+ entry.resolve(response);
1075
+ }
1076
+ } catch {
1077
+ }
1078
+ }
1079
+ }
1080
+ function cleanupStaleFiles() {
1081
+ if (existsSync5(PID_PATH)) {
1082
+ try {
1083
+ const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1084
+ if (pid > 0) {
1085
+ try {
1086
+ process.kill(pid, 0);
1087
+ return;
1088
+ } catch {
1089
+ }
1090
+ }
1091
+ } catch {
1092
+ }
1093
+ try {
1094
+ unlinkSync2(PID_PATH);
1095
+ } catch {
1096
+ }
1097
+ try {
1098
+ unlinkSync2(SOCKET_PATH);
1099
+ } catch {
1100
+ }
1101
+ }
1102
+ }
1103
+ function findPackageRoot() {
1104
+ let dir = path5.dirname(fileURLToPath(import.meta.url));
1105
+ const { root } = path5.parse(dir);
1106
+ while (dir !== root) {
1107
+ if (existsSync5(path5.join(dir, "package.json"))) return dir;
1108
+ dir = path5.dirname(dir);
1109
+ }
1110
+ return null;
1111
+ }
1112
+ function getAvailableMemoryGB() {
1113
+ if (process.platform === "darwin") {
1114
+ try {
1115
+ const { execSync: execSync5 } = __require("child_process");
1116
+ const vmstat = execSync5("vm_stat", { encoding: "utf8" });
1117
+ const pageSize = 16384;
1118
+ const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1119
+ const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1120
+ const free = vmstat.match(/Pages free:\s+(\d+)/);
1121
+ const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1122
+ const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1123
+ const freePages = free ? parseInt(free[1], 10) : 0;
1124
+ const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1125
+ const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1126
+ return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1127
+ } catch {
1128
+ return os4.freemem() / (1024 * 1024 * 1024);
1129
+ }
1130
+ }
1131
+ return os4.freemem() / (1024 * 1024 * 1024);
1132
+ }
1133
+ function spawnDaemon() {
1134
+ const freeGB = getAvailableMemoryGB();
1135
+ const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1136
+ if (totalGB <= 8) {
1137
+ process.stderr.write(
1138
+ `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1139
+ `
1140
+ );
1141
+ return;
1142
+ }
1143
+ if (totalGB <= 16 && freeGB < 2) {
1144
+ process.stderr.write(
1145
+ `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1146
+ `
1147
+ );
1148
+ return;
1149
+ }
1150
+ const pkgRoot = findPackageRoot();
1151
+ if (!pkgRoot) {
1152
+ process.stderr.write("[exed-client] WARN: cannot find package root\n");
1153
+ return;
1154
+ }
1155
+ const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1156
+ if (!existsSync5(daemonPath)) {
1157
+ process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1158
+ `);
1159
+ return;
1160
+ }
1161
+ const resolvedPath = daemonPath;
1162
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
1163
+ process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1164
+ `);
1165
+ const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
1166
+ let stderrFd = "ignore";
1167
+ try {
1168
+ stderrFd = openSync(logPath, "a");
1169
+ } catch {
1170
+ }
1171
+ const heapCapMB = totalGB <= 8 ? 256 : 512;
1172
+ const nodeArgs = [`--max-old-space-size=${heapCapMB}`, resolvedPath];
1173
+ const child = spawn(process.execPath, nodeArgs, {
1174
+ detached: true,
1175
+ stdio: ["ignore", "ignore", stderrFd],
1176
+ env: {
1177
+ ...process.env,
1178
+ TMUX: void 0,
1179
+ // Daemon is global — must not inherit session scope
1180
+ TMUX_PANE: void 0,
1181
+ // Prevents resolveExeSession() from scoping to one session
1182
+ EXE_DAEMON_SOCK: SOCKET_PATH,
1183
+ EXE_DAEMON_PID: PID_PATH,
1184
+ [DAEMON_TOKEN_ENV]: daemonToken
1185
+ }
1186
+ });
1187
+ child.unref();
1188
+ if (typeof stderrFd === "number") {
1189
+ try {
1190
+ closeSync(stderrFd);
1191
+ } catch {
1192
+ }
1193
+ }
1194
+ }
1195
+ function acquireSpawnLock() {
1196
+ try {
1197
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
1198
+ closeSync(fd);
1199
+ return true;
1200
+ } catch {
1201
+ try {
1202
+ const stat = statSync(SPAWN_LOCK_PATH);
1203
+ if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
1204
+ try {
1205
+ unlinkSync2(SPAWN_LOCK_PATH);
1206
+ } catch {
1207
+ }
1208
+ try {
1209
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
1210
+ closeSync(fd);
1211
+ return true;
1212
+ } catch {
1213
+ }
1214
+ }
1215
+ } catch {
1216
+ }
1217
+ return false;
1218
+ }
1219
+ }
1220
+ function releaseSpawnLock() {
1221
+ try {
1222
+ unlinkSync2(SPAWN_LOCK_PATH);
1223
+ } catch {
1224
+ }
1225
+ }
1226
+ function connectToSocket() {
1227
+ return new Promise((resolve) => {
1228
+ if (_socket && _connected) {
1229
+ resolve(true);
1230
+ return;
1231
+ }
1232
+ const socket = net.createConnection({ path: SOCKET_PATH });
1233
+ const connectTimeout = setTimeout(() => {
1234
+ socket.destroy();
1235
+ resolve(false);
1236
+ }, 2e3);
1237
+ socket.on("connect", () => {
1238
+ clearTimeout(connectTimeout);
1239
+ _socket = socket;
1240
+ _connected = true;
1241
+ _buffer = "";
1242
+ socket.on("data", handleData);
1243
+ socket.on("close", () => {
1244
+ _connected = false;
1245
+ _socket = null;
1246
+ for (const [id, entry] of _pending) {
1247
+ clearTimeout(entry.timer);
1248
+ _pending.delete(id);
1249
+ entry.resolve({ error: "Connection closed" });
1250
+ }
1251
+ });
1252
+ socket.on("error", () => {
1253
+ _connected = false;
1254
+ _socket = null;
1255
+ });
1256
+ resolve(true);
1257
+ });
1258
+ socket.on("error", () => {
1259
+ clearTimeout(connectTimeout);
1260
+ resolve(false);
1261
+ });
1262
+ });
1263
+ }
1264
+ async function connectEmbedDaemon() {
1265
+ if (_socket && _connected) return true;
1266
+ if (await connectToSocket()) return true;
1267
+ if (acquireSpawnLock()) {
1268
+ try {
1269
+ cleanupStaleFiles();
1270
+ spawnDaemon();
1271
+ } finally {
1272
+ releaseSpawnLock();
1273
+ }
1274
+ }
1275
+ const start = Date.now();
1276
+ let delay2 = 100;
1277
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1278
+ await new Promise((r) => setTimeout(r, delay2));
1279
+ if (await connectToSocket()) return true;
1280
+ delay2 = Math.min(delay2 * 2, 3e3);
1281
+ }
1282
+ return false;
1283
+ }
1284
+ function sendRequest(texts, priority) {
1285
+ return sendDaemonRequest({ texts, priority });
1286
+ }
1287
+ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
1288
+ return new Promise((resolve) => {
1289
+ if (!_socket || !_connected) {
1290
+ resolve({ error: "Not connected" });
1291
+ return;
1292
+ }
1293
+ const id = randomUUID();
1294
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
1295
+ const timer = setTimeout(() => {
1296
+ _pending.delete(id);
1297
+ resolve({ error: "Request timeout" });
1298
+ }, timeoutMs);
1299
+ _pending.set(id, { resolve, timer });
1300
+ try {
1301
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
1302
+ } catch {
1303
+ clearTimeout(timer);
1304
+ _pending.delete(id);
1305
+ resolve({ error: "Write failed" });
1306
+ }
1307
+ });
1308
+ }
1309
+ async function pingDaemon() {
1310
+ if (!_socket || !_connected) return null;
1311
+ const response = await sendDaemonRequest({ type: "health" }, 5e3);
1312
+ if (response.health) {
1313
+ return response.health;
1314
+ }
1315
+ return null;
1316
+ }
1317
+ function killAndRespawnDaemon() {
1318
+ if (!acquireSpawnLock()) {
1319
+ process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
1320
+ if (_socket) {
1321
+ _socket.destroy();
1322
+ _socket = null;
1323
+ }
1324
+ _connected = false;
1325
+ _buffer = "";
1326
+ return;
1327
+ }
1328
+ try {
1329
+ process.stderr.write("[exed-client] Killing daemon for restart...\n");
1330
+ if (existsSync5(PID_PATH)) {
1331
+ try {
1332
+ const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1333
+ if (pid > 0) {
1334
+ try {
1335
+ process.kill(pid, "SIGKILL");
1336
+ } catch {
1337
+ }
1338
+ }
1339
+ } catch {
1340
+ }
1341
+ }
1342
+ if (_socket) {
1343
+ _socket.destroy();
1344
+ _socket = null;
1345
+ }
1346
+ _connected = false;
1347
+ _buffer = "";
1348
+ try {
1349
+ unlinkSync2(PID_PATH);
1350
+ } catch {
1351
+ }
1352
+ try {
1353
+ unlinkSync2(SOCKET_PATH);
1354
+ } catch {
1355
+ }
1356
+ spawnDaemon();
1357
+ } finally {
1358
+ releaseSpawnLock();
1359
+ }
1360
+ }
1361
+ function isDaemonTooYoung() {
1362
+ try {
1363
+ const stat = statSync(PID_PATH);
1364
+ return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
1365
+ } catch {
1366
+ return false;
1367
+ }
1368
+ }
1369
+ async function retryThenRestart(doRequest, label) {
1370
+ const result = await doRequest();
1371
+ if (!result.error) {
1372
+ _consecutiveFailures = 0;
1373
+ return result;
1374
+ }
1375
+ _consecutiveFailures++;
1376
+ for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
1377
+ const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
1378
+ process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
1379
+ `);
1380
+ await new Promise((r) => setTimeout(r, delayMs));
1381
+ if (!_connected) {
1382
+ if (!await connectToSocket()) continue;
1383
+ }
1384
+ const retry = await doRequest();
1385
+ if (!retry.error) {
1386
+ _consecutiveFailures = 0;
1387
+ return retry;
1388
+ }
1389
+ _consecutiveFailures++;
1390
+ }
1391
+ if (isDaemonTooYoung()) {
1392
+ process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
1393
+ `);
1394
+ return { error: result.error };
1395
+ }
1396
+ process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
1397
+ `);
1398
+ killAndRespawnDaemon();
1399
+ const start = Date.now();
1400
+ let delay2 = 200;
1401
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1402
+ await new Promise((r) => setTimeout(r, delay2));
1403
+ if (await connectToSocket()) break;
1404
+ delay2 = Math.min(delay2 * 2, 3e3);
1405
+ }
1406
+ if (!_connected) return { error: "Daemon restart failed" };
1407
+ const final = await doRequest();
1408
+ if (!final.error) _consecutiveFailures = 0;
1409
+ return final;
1410
+ }
1411
+ async function embedViaClient(text, priority = "high") {
1412
+ if (!_connected && !await connectEmbedDaemon()) return null;
1413
+ _requestCount++;
1414
+ if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
1415
+ const health = await pingDaemon();
1416
+ if (!health && !isDaemonTooYoung()) {
1417
+ process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
1418
+ `);
1419
+ killAndRespawnDaemon();
1420
+ const start = Date.now();
1421
+ let d = 200;
1422
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1423
+ await new Promise((r) => setTimeout(r, d));
1424
+ if (await connectToSocket()) break;
1425
+ d = Math.min(d * 2, 3e3);
1426
+ }
1427
+ if (!_connected) return null;
1428
+ }
1429
+ }
1430
+ const result = await retryThenRestart(
1431
+ () => sendRequest([text], priority),
1432
+ "Embed"
1433
+ );
1434
+ return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
1435
+ }
1436
+ function disconnectClient() {
1437
+ if (_socket) {
1438
+ _socket.destroy();
1439
+ _socket = null;
1440
+ }
1441
+ _connected = false;
1442
+ _buffer = "";
1443
+ for (const [id, entry] of _pending) {
1444
+ clearTimeout(entry.timer);
1445
+ _pending.delete(id);
1446
+ entry.resolve({ error: "Client disconnected" });
1447
+ }
1448
+ }
1449
+ function isClientConnected() {
1450
+ return _connected;
1451
+ }
1452
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
1453
+ var init_exe_daemon_client = __esm({
1454
+ "src/lib/exe-daemon-client.ts"() {
1455
+ "use strict";
1456
+ init_config();
1457
+ init_daemon_auth();
1458
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
1459
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
1460
+ SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
1461
+ SPAWN_LOCK_STALE_MS = 3e4;
1462
+ CONNECT_TIMEOUT_MS = 15e3;
1463
+ REQUEST_TIMEOUT_MS = 3e4;
1464
+ DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
1465
+ _socket = null;
1466
+ _connected = false;
1467
+ _buffer = "";
1468
+ _requestCount = 0;
1469
+ _consecutiveFailures = 0;
1470
+ HEALTH_CHECK_INTERVAL = 100;
1471
+ MAX_RETRIES_BEFORE_RESTART = 3;
1472
+ RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
1473
+ MIN_DAEMON_AGE_MS = 3e4;
1474
+ _pending = /* @__PURE__ */ new Map();
1475
+ MAX_BUFFER = 1e7;
1476
+ }
1477
+ });
1478
+
1479
+ // src/lib/daemon-protocol.ts
1480
+ function serializeValue(v) {
1481
+ if (v === null || v === void 0) return null;
1482
+ if (typeof v === "bigint") return Number(v);
1483
+ if (typeof v === "boolean") return v ? 1 : 0;
1484
+ if (v instanceof Uint8Array) {
1485
+ return { __blob: Buffer.from(v).toString("base64") };
1486
+ }
1487
+ if (ArrayBuffer.isView(v)) {
1488
+ return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
1489
+ }
1490
+ if (v instanceof ArrayBuffer) {
1491
+ return { __blob: Buffer.from(v).toString("base64") };
1492
+ }
1493
+ if (typeof v === "string" || typeof v === "number") return v;
1494
+ return String(v);
1495
+ }
1496
+ function deserializeValue(v) {
1497
+ if (v === null) return null;
1498
+ if (typeof v === "object" && v !== null && "__blob" in v) {
1499
+ const buf = Buffer.from(v.__blob, "base64");
1500
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
1501
+ }
1502
+ return v;
1503
+ }
1504
+ function deserializeResultSet(srs) {
1505
+ const rows = srs.rows.map((obj) => {
1506
+ const values = srs.columns.map(
1507
+ (col) => deserializeValue(obj[col] ?? null)
1508
+ );
1509
+ const row = values;
1510
+ for (let i = 0; i < srs.columns.length; i++) {
1511
+ const col = srs.columns[i];
1512
+ if (col !== void 0) {
1513
+ row[col] = values[i] ?? null;
1514
+ }
1515
+ }
1516
+ Object.defineProperty(row, "length", {
1517
+ value: values.length,
1518
+ enumerable: false
1519
+ });
1520
+ return row;
1521
+ });
1522
+ return {
1523
+ columns: srs.columns,
1524
+ columnTypes: srs.columnTypes ?? [],
1525
+ rows,
1526
+ rowsAffected: srs.rowsAffected,
1527
+ lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
1528
+ toJSON: () => ({
1529
+ columns: srs.columns,
1530
+ columnTypes: srs.columnTypes ?? [],
1531
+ rows: srs.rows,
1532
+ rowsAffected: srs.rowsAffected,
1533
+ lastInsertRowid: srs.lastInsertRowid
1534
+ })
1535
+ };
1536
+ }
1537
+ var init_daemon_protocol = __esm({
1538
+ "src/lib/daemon-protocol.ts"() {
1539
+ "use strict";
1540
+ }
1541
+ });
1542
+
1543
+ // src/lib/db-daemon-client.ts
1544
+ var db_daemon_client_exports = {};
1545
+ __export(db_daemon_client_exports, {
1546
+ createDaemonDbClient: () => createDaemonDbClient,
1547
+ initDaemonDbClient: () => initDaemonDbClient
1548
+ });
1549
+ function normalizeStatement2(stmt) {
1550
+ if (typeof stmt === "string") {
1551
+ return { sql: stmt, args: [] };
1552
+ }
1553
+ const sql = stmt.sql;
1554
+ let args = [];
1555
+ if (Array.isArray(stmt.args)) {
1556
+ args = stmt.args.map((v) => serializeValue(v));
1557
+ } else if (stmt.args && typeof stmt.args === "object") {
1558
+ const named = {};
1559
+ for (const [key, val] of Object.entries(stmt.args)) {
1560
+ named[key] = serializeValue(val);
1561
+ }
1562
+ return { sql, args: named };
1563
+ }
1564
+ return { sql, args };
1565
+ }
1566
+ function createDaemonDbClient(fallbackClient) {
1567
+ let _useDaemon = false;
1568
+ const client = {
1569
+ async execute(stmt) {
1570
+ if (!_useDaemon || !isClientConnected()) {
1571
+ return fallbackClient.execute(stmt);
1572
+ }
1573
+ const { sql, args } = normalizeStatement2(stmt);
1574
+ const response = await sendDaemonRequest({
1575
+ type: "db-execute",
1576
+ sql,
1577
+ args
1578
+ });
1579
+ if (response.error) {
1580
+ const errMsg = String(response.error);
1581
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed" || errMsg === "DB not initialized") {
1582
+ process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
1583
+ `);
1584
+ return fallbackClient.execute(stmt);
1585
+ }
1586
+ throw new Error(errMsg);
1587
+ }
1588
+ if (response.db) {
1589
+ return deserializeResultSet(response.db);
1590
+ }
1591
+ process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
1592
+ return fallbackClient.execute(stmt);
1593
+ },
1594
+ async batch(stmts, mode) {
1595
+ if (!_useDaemon || !isClientConnected()) {
1596
+ return fallbackClient.batch(stmts, mode);
1597
+ }
1598
+ const statements = stmts.map(normalizeStatement2);
1599
+ const response = await sendDaemonRequest({
1600
+ type: "db-batch",
1601
+ statements,
1602
+ mode: mode ?? "deferred"
1603
+ });
1604
+ if (response.error) {
1605
+ const errMsg = String(response.error);
1606
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed" || errMsg === "DB not initialized") {
1607
+ process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
1608
+ `);
1609
+ return fallbackClient.batch(stmts, mode);
1610
+ }
1611
+ throw new Error(errMsg);
1612
+ }
1613
+ const batchResults = response["db-batch"];
1614
+ if (batchResults) {
1615
+ return batchResults.map(deserializeResultSet);
1616
+ }
1617
+ process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
1618
+ return fallbackClient.batch(stmts, mode);
1619
+ },
1620
+ // Transaction support — delegate to fallback (transactions need direct connection)
1621
+ async transaction(mode) {
1622
+ return fallbackClient.transaction(mode);
1623
+ },
1624
+ // executeMultiple — delegate to fallback (used only for schema migrations)
1625
+ async executeMultiple(sql) {
1626
+ return fallbackClient.executeMultiple(sql);
1627
+ },
1628
+ // migrate — delegate to fallback
1629
+ async migrate(stmts) {
1630
+ return fallbackClient.migrate(stmts);
1631
+ },
1632
+ // Sync mode — delegate to fallback
1633
+ sync() {
1634
+ return fallbackClient.sync();
1635
+ },
1636
+ close() {
1637
+ _useDaemon = false;
1638
+ },
1639
+ get closed() {
1640
+ return fallbackClient.closed;
1641
+ },
1642
+ get protocol() {
1643
+ return fallbackClient.protocol;
1644
+ }
1645
+ };
1646
+ return {
1647
+ ...client,
1648
+ /** Enable daemon routing (call after confirming daemon is connected) */
1649
+ _enableDaemon() {
1650
+ _useDaemon = true;
1651
+ },
1652
+ /** Check if daemon routing is active */
1653
+ _isDaemonActive() {
1654
+ return _useDaemon && isClientConnected();
1655
+ }
1656
+ };
1657
+ }
1658
+ async function initDaemonDbClient(fallbackClient) {
1659
+ if (process.env.EXE_IS_DAEMON === "1") return null;
1660
+ const connected = await connectEmbedDaemon();
1661
+ if (!connected) {
1662
+ process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
1663
+ return null;
1664
+ }
1665
+ const client = createDaemonDbClient(fallbackClient);
1666
+ client._enableDaemon();
1667
+ process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
1668
+ return client;
1669
+ }
1670
+ var init_db_daemon_client = __esm({
1671
+ "src/lib/db-daemon-client.ts"() {
1672
+ "use strict";
1673
+ init_exe_daemon_client();
1674
+ init_daemon_protocol();
1675
+ }
1676
+ });
1677
+
933
1678
  // src/lib/database.ts
1679
+ var database_exports = {};
1680
+ __export(database_exports, {
1681
+ disposeDatabase: () => disposeDatabase,
1682
+ disposeTurso: () => disposeTurso,
1683
+ ensureSchema: () => ensureSchema,
1684
+ getClient: () => getClient,
1685
+ getRawClient: () => getRawClient,
1686
+ initDaemonClient: () => initDaemonClient,
1687
+ initDatabase: () => initDatabase,
1688
+ initTurso: () => initTurso,
1689
+ isInitialized: () => isInitialized
1690
+ });
934
1691
  import { createClient } from "@libsql/client";
935
1692
  async function initDatabase(config) {
936
1693
  if (_walCheckpointTimer) {
@@ -969,15 +1726,18 @@ async function initDatabase(config) {
969
1726
  });
970
1727
  }, 3e4);
971
1728
  _walCheckpointTimer.unref();
972
- if (process.env.DATABASE_URL) {
1729
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
973
1730
  _adapterClient = await createPrismaDbAdapter(_resilientClient);
974
1731
  }
975
1732
  }
1733
+ function isInitialized() {
1734
+ return _adapterClient !== null || _client !== null;
1735
+ }
976
1736
  function getClient() {
977
1737
  if (!_adapterClient) {
978
1738
  throw new Error("Database client not initialized. Call initDatabase() first.");
979
1739
  }
980
- if (process.env.DATABASE_URL) {
1740
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
981
1741
  return _adapterClient;
982
1742
  }
983
1743
  if (process.env.EXE_IS_DAEMON === "1") {
@@ -988,6 +1748,22 @@ function getClient() {
988
1748
  }
989
1749
  return _resilientClient;
990
1750
  }
1751
+ async function initDaemonClient() {
1752
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") return;
1753
+ if (process.env.EXE_IS_DAEMON === "1") return;
1754
+ if (process.env.VITEST) return;
1755
+ if (!_resilientClient) return;
1756
+ if (_daemonClient) return;
1757
+ try {
1758
+ const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
1759
+ _daemonClient = await initDaemonDbClient2(_resilientClient);
1760
+ } catch (err) {
1761
+ process.stderr.write(
1762
+ `[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
1763
+ `
1764
+ );
1765
+ }
1766
+ }
991
1767
  function getRawClient() {
992
1768
  if (!_client) {
993
1769
  throw new Error("Database client not initialized. Call initDatabase() first.");
@@ -1126,6 +1902,13 @@ async function ensureSchema() {
1126
1902
  });
1127
1903
  } catch {
1128
1904
  }
1905
+ try {
1906
+ await client.execute({
1907
+ sql: `ALTER TABLE behaviors ADD COLUMN vector F32_BLOB(${EMBEDDING_DIM})`,
1908
+ args: []
1909
+ });
1910
+ } catch {
1911
+ }
1129
1912
  try {
1130
1913
  await client.execute({
1131
1914
  sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
@@ -1970,30 +2753,51 @@ async function ensureSchema() {
1970
2753
  } catch {
1971
2754
  }
1972
2755
  }
1973
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso;
2756
+ async function disposeDatabase() {
2757
+ if (_walCheckpointTimer) {
2758
+ clearInterval(_walCheckpointTimer);
2759
+ _walCheckpointTimer = null;
2760
+ }
2761
+ if (_daemonClient) {
2762
+ _daemonClient.close();
2763
+ _daemonClient = null;
2764
+ }
2765
+ if (_adapterClient && _adapterClient !== _resilientClient) {
2766
+ _adapterClient.close();
2767
+ }
2768
+ _adapterClient = null;
2769
+ if (_client) {
2770
+ _client.close();
2771
+ _client = null;
2772
+ _resilientClient = null;
2773
+ }
2774
+ }
2775
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
1974
2776
  var init_database = __esm({
1975
2777
  "src/lib/database.ts"() {
1976
2778
  "use strict";
1977
2779
  init_db_retry();
1978
2780
  init_employees();
1979
2781
  init_database_adapter();
2782
+ init_memory();
1980
2783
  _client = null;
1981
2784
  _resilientClient = null;
1982
2785
  _walCheckpointTimer = null;
1983
2786
  _daemonClient = null;
1984
2787
  _adapterClient = null;
1985
2788
  initTurso = initDatabase;
2789
+ disposeTurso = disposeDatabase;
1986
2790
  }
1987
2791
  });
1988
2792
 
1989
2793
  // src/lib/session-registry.ts
1990
- import path4 from "path";
1991
- import os4 from "os";
2794
+ import path6 from "path";
2795
+ import os5 from "os";
1992
2796
  var REGISTRY_PATH;
1993
2797
  var init_session_registry = __esm({
1994
2798
  "src/lib/session-registry.ts"() {
1995
2799
  "use strict";
1996
- REGISTRY_PATH = path4.join(os4.homedir(), ".exe-os", "session-registry.json");
2800
+ REGISTRY_PATH = path6.join(os5.homedir(), ".exe-os", "session-registry.json");
1997
2801
  }
1998
2802
  });
1999
2803
 
@@ -2239,12 +3043,12 @@ var init_runtime_table = __esm({
2239
3043
  });
2240
3044
 
2241
3045
  // src/lib/agent-config.ts
2242
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync4 } from "fs";
2243
- import path5 from "path";
3046
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync6 } from "fs";
3047
+ import path7 from "path";
2244
3048
  function loadAgentConfig() {
2245
- if (!existsSync4(AGENT_CONFIG_PATH)) return {};
3049
+ if (!existsSync6(AGENT_CONFIG_PATH)) return {};
2246
3050
  try {
2247
- return JSON.parse(readFileSync3(AGENT_CONFIG_PATH, "utf-8"));
3051
+ return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
2248
3052
  } catch {
2249
3053
  return {};
2250
3054
  }
@@ -2264,9 +3068,9 @@ var init_agent_config = __esm({
2264
3068
  init_config();
2265
3069
  init_runtime_table();
2266
3070
  init_secure_files();
2267
- AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
3071
+ AGENT_CONFIG_PATH = path7.join(EXE_AI_DIR, "agent-config.json");
2268
3072
  DEFAULT_MODELS = {
2269
- claude: "claude-opus-4",
3073
+ claude: "claude-opus-4.6",
2270
3074
  codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
2271
3075
  opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
2272
3076
  };
@@ -2282,17 +3086,17 @@ __export(intercom_queue_exports, {
2282
3086
  queueIntercom: () => queueIntercom,
2283
3087
  readQueue: () => readQueue
2284
3088
  });
2285
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
2286
- import path6 from "path";
2287
- import os5 from "os";
3089
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
3090
+ import path8 from "path";
3091
+ import os6 from "os";
2288
3092
  function ensureDir() {
2289
- const dir = path6.dirname(QUEUE_PATH);
2290
- if (!existsSync5(dir)) mkdirSync2(dir, { recursive: true });
3093
+ const dir = path8.dirname(QUEUE_PATH);
3094
+ if (!existsSync7(dir)) mkdirSync2(dir, { recursive: true });
2291
3095
  }
2292
3096
  function readQueue() {
2293
3097
  try {
2294
- if (!existsSync5(QUEUE_PATH)) return [];
2295
- return JSON.parse(readFileSync4(QUEUE_PATH, "utf8"));
3098
+ if (!existsSync7(QUEUE_PATH)) return [];
3099
+ return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
2296
3100
  } catch {
2297
3101
  return [];
2298
3102
  }
@@ -2300,7 +3104,7 @@ function readQueue() {
2300
3104
  function writeQueue(queue) {
2301
3105
  ensureDir();
2302
3106
  const tmp = `${QUEUE_PATH}.tmp`;
2303
- writeFileSync3(tmp, JSON.stringify(queue, null, 2));
3107
+ writeFileSync4(tmp, JSON.stringify(queue, null, 2));
2304
3108
  renameSync3(tmp, QUEUE_PATH);
2305
3109
  }
2306
3110
  function queueIntercom(targetSession, reason) {
@@ -2392,35 +3196,35 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
2392
3196
  var init_intercom_queue = __esm({
2393
3197
  "src/lib/intercom-queue.ts"() {
2394
3198
  "use strict";
2395
- QUEUE_PATH = path6.join(os5.homedir(), ".exe-os", "intercom-queue.json");
3199
+ QUEUE_PATH = path8.join(os6.homedir(), ".exe-os", "intercom-queue.json");
2396
3200
  MAX_RETRIES2 = 5;
2397
3201
  TTL_MS = 60 * 60 * 1e3;
2398
- INTERCOM_LOG = path6.join(os5.homedir(), ".exe-os", "intercom.log");
3202
+ INTERCOM_LOG = path8.join(os6.homedir(), ".exe-os", "intercom.log");
2399
3203
  }
2400
3204
  });
2401
3205
 
2402
3206
  // src/lib/license.ts
2403
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
2404
- import { randomUUID } from "crypto";
3207
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
3208
+ import { randomUUID as randomUUID2 } from "crypto";
2405
3209
  import { createRequire as createRequire2 } from "module";
2406
3210
  import { pathToFileURL as pathToFileURL2 } from "url";
2407
- import os6 from "os";
2408
- import path7 from "path";
3211
+ import os7 from "os";
3212
+ import path9 from "path";
2409
3213
  import { jwtVerify, importSPKI } from "jose";
2410
3214
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
2411
3215
  var init_license = __esm({
2412
3216
  "src/lib/license.ts"() {
2413
3217
  "use strict";
2414
3218
  init_config();
2415
- LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
2416
- CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
2417
- DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
3219
+ LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
3220
+ CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
3221
+ DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
2418
3222
  }
2419
3223
  });
2420
3224
 
2421
3225
  // src/lib/plan-limits.ts
2422
- import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
2423
- import path8 from "path";
3226
+ import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
3227
+ import path10 from "path";
2424
3228
  var CACHE_PATH2;
2425
3229
  var init_plan_limits = __esm({
2426
3230
  "src/lib/plan-limits.ts"() {
@@ -2429,15 +3233,15 @@ var init_plan_limits = __esm({
2429
3233
  init_employees();
2430
3234
  init_license();
2431
3235
  init_config();
2432
- CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
3236
+ CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
2433
3237
  }
2434
3238
  });
2435
3239
 
2436
3240
  // src/lib/tmux-routing.ts
2437
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync8, appendFileSync, readdirSync } from "fs";
2438
- import path9 from "path";
2439
- import os7 from "os";
2440
- import { fileURLToPath } from "url";
3241
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync4, existsSync as existsSync10, appendFileSync, readdirSync } from "fs";
3242
+ import path11 from "path";
3243
+ import os8 from "os";
3244
+ import { fileURLToPath as fileURLToPath2 } from "url";
2441
3245
  function getMySession() {
2442
3246
  return getTransport().getMySession();
2443
3247
  }
@@ -2449,7 +3253,7 @@ function extractRootExe(name) {
2449
3253
  }
2450
3254
  function getParentExe(sessionKey) {
2451
3255
  try {
2452
- const data = JSON.parse(readFileSync7(path9.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
3256
+ const data = JSON.parse(readFileSync9(path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
2453
3257
  return data.parentExe || null;
2454
3258
  } catch {
2455
3259
  return null;
@@ -2457,8 +3261,8 @@ function getParentExe(sessionKey) {
2457
3261
  }
2458
3262
  function getDispatchedBy(sessionKey) {
2459
3263
  try {
2460
- const data = JSON.parse(readFileSync7(
2461
- path9.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
3264
+ const data = JSON.parse(readFileSync9(
3265
+ path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
2462
3266
  "utf8"
2463
3267
  ));
2464
3268
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -2490,8 +3294,8 @@ function resolveExeSession() {
2490
3294
  }
2491
3295
  function readDebounceState() {
2492
3296
  try {
2493
- if (!existsSync8(DEBOUNCE_FILE)) return {};
2494
- const raw = JSON.parse(readFileSync7(DEBOUNCE_FILE, "utf8"));
3297
+ if (!existsSync10(DEBOUNCE_FILE)) return {};
3298
+ const raw = JSON.parse(readFileSync9(DEBOUNCE_FILE, "utf8"));
2495
3299
  const state = {};
2496
3300
  for (const [key, val] of Object.entries(raw)) {
2497
3301
  if (typeof val === "number") {
@@ -2507,8 +3311,8 @@ function readDebounceState() {
2507
3311
  }
2508
3312
  function writeDebounceState(state) {
2509
3313
  try {
2510
- if (!existsSync8(SESSION_CACHE)) mkdirSync4(SESSION_CACHE, { recursive: true });
2511
- writeFileSync5(DEBOUNCE_FILE, JSON.stringify(state));
3314
+ if (!existsSync10(SESSION_CACHE)) mkdirSync4(SESSION_CACHE, { recursive: true });
3315
+ writeFileSync6(DEBOUNCE_FILE, JSON.stringify(state));
2512
3316
  } catch {
2513
3317
  }
2514
3318
  }
@@ -2568,7 +3372,7 @@ function getSessionState(sessionName) {
2568
3372
  function isExeSession(sessionName) {
2569
3373
  const matchesBaseWithInstance = (baseName) => sessionName === baseName || sessionName.startsWith(baseName) && /^\d+$/.test(sessionName.slice(baseName.length));
2570
3374
  const coordinatorName = getCoordinatorName();
2571
- return matchesBaseWithInstance(coordinatorName) || matchesBaseWithInstance("exe");
3375
+ return matchesBaseWithInstance(coordinatorName);
2572
3376
  }
2573
3377
  function sendIntercom(targetSession) {
2574
3378
  const transport = getTransport();
@@ -2603,8 +3407,8 @@ function sendIntercom(targetSession) {
2603
3407
  try {
2604
3408
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
2605
3409
  const agent = baseAgentName(rawAgent);
2606
- const markerPath = path9.join(SESSION_CACHE, `current-task-${agent}.json`);
2607
- if (existsSync8(markerPath)) {
3410
+ const markerPath = path11.join(SESSION_CACHE, `current-task-${agent}.json`);
3411
+ if (existsSync10(markerPath)) {
2608
3412
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker + not idle \u2014 will auto-chain)`);
2609
3413
  return "debounced";
2610
3414
  }
@@ -2614,8 +3418,8 @@ function sendIntercom(targetSession) {
2614
3418
  try {
2615
3419
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
2616
3420
  const agent = baseAgentName(rawAgent);
2617
- const taskDir = path9.join(process.cwd(), "exe", agent);
2618
- if (existsSync8(taskDir)) {
3421
+ const taskDir = path11.join(process.cwd(), "exe", agent);
3422
+ if (existsSync10(taskDir)) {
2619
3423
  const files = readdirSync(taskDir).filter(
2620
3424
  (f) => f.endsWith(".md") && f !== "DONE.txt"
2621
3425
  );
@@ -2691,12 +3495,12 @@ var init_tmux_routing = __esm({
2691
3495
  init_intercom_queue();
2692
3496
  init_plan_limits();
2693
3497
  init_employees();
2694
- SPAWN_LOCK_DIR = path9.join(os7.homedir(), ".exe-os", "spawn-locks");
2695
- SESSION_CACHE = path9.join(os7.homedir(), ".exe-os", "session-cache");
3498
+ SPAWN_LOCK_DIR = path11.join(os8.homedir(), ".exe-os", "spawn-locks");
3499
+ SESSION_CACHE = path11.join(os8.homedir(), ".exe-os", "session-cache");
2696
3500
  INTERCOM_DEBOUNCE_MS = 3e4;
2697
3501
  CODEX_DEBOUNCE_MS = 12e4;
2698
- INTERCOM_LOG2 = path9.join(os7.homedir(), ".exe-os", "intercom.log");
2699
- DEBOUNCE_FILE = path9.join(SESSION_CACHE, "intercom-debounce.json");
3502
+ INTERCOM_LOG2 = path11.join(os8.homedir(), ".exe-os", "intercom.log");
3503
+ DEBOUNCE_FILE = path11.join(SESSION_CACHE, "intercom-debounce.json");
2700
3504
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
2701
3505
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
2702
3506
  }
@@ -2736,20 +3540,20 @@ var init_task_scope = __esm({
2736
3540
  });
2737
3541
 
2738
3542
  // src/lib/notifications.ts
2739
- import crypto from "crypto";
2740
- import path10 from "path";
2741
- import os8 from "os";
3543
+ import crypto2 from "crypto";
3544
+ import path12 from "path";
3545
+ import os9 from "os";
2742
3546
  import {
2743
- readFileSync as readFileSync8,
3547
+ readFileSync as readFileSync10,
2744
3548
  readdirSync as readdirSync2,
2745
- unlinkSync as unlinkSync2,
2746
- existsSync as existsSync9,
3549
+ unlinkSync as unlinkSync3,
3550
+ existsSync as existsSync11,
2747
3551
  rmdirSync
2748
3552
  } from "fs";
2749
3553
  async function writeNotification(notification) {
2750
3554
  try {
2751
3555
  const client = getClient();
2752
- const id = crypto.randomUUID();
3556
+ const id = crypto2.randomUUID();
2753
3557
  const now = (/* @__PURE__ */ new Date()).toISOString();
2754
3558
  const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
2755
3559
  await client.execute({
@@ -2848,12 +3652,12 @@ var init_state_bus = __esm({
2848
3652
  });
2849
3653
 
2850
3654
  // src/lib/tasks-crud.ts
2851
- import crypto2 from "crypto";
2852
- import path11 from "path";
2853
- import os9 from "os";
3655
+ import crypto3 from "crypto";
3656
+ import path13 from "path";
3657
+ import os10 from "os";
2854
3658
  import { execSync as execSync4 } from "child_process";
2855
3659
  import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
2856
- import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
3660
+ import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
2857
3661
  async function writeCheckpoint(input) {
2858
3662
  const client = getClient();
2859
3663
  const row = await resolveTask(client, input.taskId);
@@ -3199,8 +4003,8 @@ var init_tasks_crud = __esm({
3199
4003
  });
3200
4004
 
3201
4005
  // src/lib/tasks-review.ts
3202
- import path12 from "path";
3203
- import { existsSync as existsSync11, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
4006
+ import path14 from "path";
4007
+ import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
3204
4008
  async function cleanupReviewFile(row, taskFile, _baseDir) {
3205
4009
  if (String(row.assigned_by) !== "system" || !taskFile.includes("review-")) return;
3206
4010
  try {
@@ -3245,11 +4049,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
3245
4049
  );
3246
4050
  }
3247
4051
  try {
3248
- const cacheDir = path12.join(EXE_AI_DIR, "session-cache");
3249
- if (existsSync11(cacheDir)) {
4052
+ const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
4053
+ if (existsSync13(cacheDir)) {
3250
4054
  for (const f of readdirSync3(cacheDir)) {
3251
4055
  if (f.startsWith("review-notified-")) {
3252
- unlinkSync3(path12.join(cacheDir, f));
4056
+ unlinkSync4(path14.join(cacheDir, f));
3253
4057
  }
3254
4058
  }
3255
4059
  }
@@ -3271,7 +4075,7 @@ var init_tasks_review = __esm({
3271
4075
  });
3272
4076
 
3273
4077
  // src/lib/tasks-chain.ts
3274
- import path13 from "path";
4078
+ import path15 from "path";
3275
4079
  import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
3276
4080
  async function cascadeUnblock(taskId, baseDir, now) {
3277
4081
  const client = getClient();
@@ -3288,7 +4092,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
3288
4092
  });
3289
4093
  for (const ur of unblockedRows.rows) {
3290
4094
  try {
3291
- const ubFile = path13.join(baseDir, String(ur.task_file));
4095
+ const ubFile = path15.join(baseDir, String(ur.task_file));
3292
4096
  let ubContent = await readFile3(ubFile, "utf-8");
3293
4097
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
3294
4098
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -3359,7 +4163,13 @@ var init_tasks_chain = __esm({
3359
4163
  function notifyTaskDone() {
3360
4164
  try {
3361
4165
  const key = getSessionKey();
3362
- if (key && !process.env.VITEST) notifyParentExe(key);
4166
+ if (key && !process.env.VITEST) {
4167
+ notifyParentExe(key);
4168
+ process.stderr.write(
4169
+ `[dispatch-audit] notifyTaskDone \u2192 parent coordinator (session=${key})
4170
+ `
4171
+ );
4172
+ }
3363
4173
  } catch {
3364
4174
  }
3365
4175
  }
@@ -3380,16 +4190,91 @@ var init_tasks_notify = __esm({
3380
4190
  }
3381
4191
  });
3382
4192
 
4193
+ // src/lib/embedder.ts
4194
+ var embedder_exports = {};
4195
+ __export(embedder_exports, {
4196
+ disposeEmbedder: () => disposeEmbedder,
4197
+ embed: () => embed,
4198
+ embedDirect: () => embedDirect,
4199
+ getEmbedder: () => getEmbedder
4200
+ });
4201
+ async function getEmbedder() {
4202
+ const ok = await connectEmbedDaemon();
4203
+ if (!ok) {
4204
+ throw new Error(
4205
+ "Could not connect to embedding daemon. Ensure the model is installed (run /exe-setup)."
4206
+ );
4207
+ }
4208
+ }
4209
+ async function embed(text) {
4210
+ const priority = process.env.EXE_EMBED_PRIORITY === "low" ? "low" : "high";
4211
+ const vector = await embedViaClient(text, priority);
4212
+ if (!vector) {
4213
+ throw new Error(
4214
+ "Embedding failed: daemon unavailable. Run /exe-setup to verify model installation."
4215
+ );
4216
+ }
4217
+ if (vector.length !== EMBEDDING_DIM) {
4218
+ throw new Error(
4219
+ `Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}. Ensure the correct Jina v5-small Q4_K_M GGUF model is installed.`
4220
+ );
4221
+ }
4222
+ return vector;
4223
+ }
4224
+ async function disposeEmbedder() {
4225
+ disconnectClient();
4226
+ }
4227
+ async function embedDirect(text) {
4228
+ const llamaCpp = await import("node-llama-cpp");
4229
+ const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
4230
+ const { existsSync: existsSync16 } = await import("fs");
4231
+ const path20 = await import("path");
4232
+ const modelPath = path20.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
4233
+ if (!existsSync16(modelPath)) {
4234
+ throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
4235
+ }
4236
+ const llama = await llamaCpp.getLlama();
4237
+ const model = await llama.loadModel({ modelPath });
4238
+ const context = await model.createEmbeddingContext();
4239
+ try {
4240
+ const embedding = await context.getEmbeddingFor(text);
4241
+ const vector = Array.from(embedding.vector);
4242
+ if (vector.length !== EMBEDDING_DIM) {
4243
+ throw new Error(
4244
+ `Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}.`
4245
+ );
4246
+ }
4247
+ return vector;
4248
+ } finally {
4249
+ await context.dispose();
4250
+ await model.dispose();
4251
+ }
4252
+ }
4253
+ var init_embedder = __esm({
4254
+ "src/lib/embedder.ts"() {
4255
+ "use strict";
4256
+ init_memory();
4257
+ init_exe_daemon_client();
4258
+ }
4259
+ });
4260
+
3383
4261
  // src/lib/behaviors.ts
3384
- import crypto3 from "crypto";
4262
+ import crypto4 from "crypto";
3385
4263
  async function storeBehavior(opts) {
3386
4264
  const client = getClient();
3387
- const id = crypto3.randomUUID();
4265
+ const id = crypto4.randomUUID();
3388
4266
  const now = (/* @__PURE__ */ new Date()).toISOString();
4267
+ let vector = null;
4268
+ try {
4269
+ const { embed: embed2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
4270
+ const vec = await embed2(opts.content);
4271
+ vector = new Float32Array(vec);
4272
+ } catch {
4273
+ }
3389
4274
  await client.execute({
3390
- sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
3391
- VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)`,
3392
- args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now]
4275
+ sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector)
4276
+ VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`,
4277
+ args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null]
3393
4278
  });
3394
4279
  return id;
3395
4280
  }
@@ -3413,7 +4298,7 @@ __export(skill_learning_exports, {
3413
4298
  storeTrajectory: () => storeTrajectory,
3414
4299
  sweepTrajectories: () => sweepTrajectories
3415
4300
  });
3416
- import crypto4 from "crypto";
4301
+ import crypto5 from "crypto";
3417
4302
  async function extractTrajectory(taskId, agentId) {
3418
4303
  const client = getClient();
3419
4304
  const result = await client.execute({
@@ -3442,11 +4327,11 @@ async function extractTrajectory(taskId, agentId) {
3442
4327
  return signature;
3443
4328
  }
3444
4329
  function hashSignature(signature) {
3445
- return crypto4.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
4330
+ return crypto5.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
3446
4331
  }
3447
4332
  async function storeTrajectory(opts) {
3448
4333
  const client = getClient();
3449
- const id = crypto4.randomUUID();
4334
+ const id = crypto5.randomUUID();
3450
4335
  const now = (/* @__PURE__ */ new Date()).toISOString();
3451
4336
  const signatureHash = hashSignature(opts.signature);
3452
4337
  await client.execute({
@@ -3691,20 +4576,20 @@ var init_skill_learning = __esm({
3691
4576
  });
3692
4577
 
3693
4578
  // src/lib/tasks.ts
3694
- import path14 from "path";
3695
- import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "fs";
4579
+ import path16 from "path";
4580
+ import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
3696
4581
  async function updateTask(input) {
3697
4582
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
3698
4583
  try {
3699
4584
  const agent = String(row.assigned_to);
3700
- const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
3701
- const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
4585
+ const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
4586
+ const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
3702
4587
  if (input.status === "in_progress") {
3703
4588
  mkdirSync5(cacheDir, { recursive: true });
3704
- writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
4589
+ writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
3705
4590
  } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
3706
4591
  try {
3707
- unlinkSync4(cachePath);
4592
+ unlinkSync5(cachePath);
3708
4593
  } catch {
3709
4594
  }
3710
4595
  }
@@ -3857,12 +4742,12 @@ __export(shard_manager_exports, {
3857
4742
  listShards: () => listShards,
3858
4743
  shardExists: () => shardExists
3859
4744
  });
3860
- import path16 from "path";
3861
- import { existsSync as existsSync13, mkdirSync as mkdirSync6, readdirSync as readdirSync4 } from "fs";
4745
+ import path18 from "path";
4746
+ import { existsSync as existsSync15, mkdirSync as mkdirSync6, readdirSync as readdirSync4 } from "fs";
3862
4747
  import { createClient as createClient2 } from "@libsql/client";
3863
4748
  function initShardManager(encryptionKey) {
3864
4749
  _encryptionKey = encryptionKey;
3865
- if (!existsSync13(SHARDS_DIR)) {
4750
+ if (!existsSync15(SHARDS_DIR)) {
3866
4751
  mkdirSync6(SHARDS_DIR, { recursive: true });
3867
4752
  }
3868
4753
  _shardingEnabled = true;
@@ -3892,7 +4777,7 @@ function getShardClient(projectName) {
3892
4777
  while (_shards.size >= MAX_OPEN_SHARDS) {
3893
4778
  evictLRU();
3894
4779
  }
3895
- const dbPath = path16.join(SHARDS_DIR, `${safeName}.db`);
4780
+ const dbPath = path18.join(SHARDS_DIR, `${safeName}.db`);
3896
4781
  const client = createClient2({
3897
4782
  url: `file:${dbPath}`,
3898
4783
  encryptionKey: _encryptionKey
@@ -3903,10 +4788,10 @@ function getShardClient(projectName) {
3903
4788
  }
3904
4789
  function shardExists(projectName) {
3905
4790
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3906
- return existsSync13(path16.join(SHARDS_DIR, `${safeName}.db`));
4791
+ return existsSync15(path18.join(SHARDS_DIR, `${safeName}.db`));
3907
4792
  }
3908
4793
  function listShards() {
3909
- if (!existsSync13(SHARDS_DIR)) return [];
4794
+ if (!existsSync15(SHARDS_DIR)) return [];
3910
4795
  return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
3911
4796
  }
3912
4797
  async function ensureShardSchema(client) {
@@ -4153,7 +5038,7 @@ var init_shard_manager = __esm({
4153
5038
  "src/lib/shard-manager.ts"() {
4154
5039
  "use strict";
4155
5040
  init_config();
4156
- SHARDS_DIR = path16.join(EXE_AI_DIR, "shards");
5041
+ SHARDS_DIR = path18.join(EXE_AI_DIR, "shards");
4157
5042
  SHARD_IDLE_MS = 5 * 60 * 1e3;
4158
5043
  MAX_OPEN_SHARDS = 10;
4159
5044
  EVICTION_INTERVAL_MS = 60 * 1e3;
@@ -4273,6 +5158,68 @@ var init_platform_procedures = __esm({
4273
5158
  domain: "architecture",
4274
5159
  priority: "p0",
4275
5160
  content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
5161
+ },
5162
+ // --- MCP is the ONLY data interface ---
5163
+ {
5164
+ title: "MCP disconnect \u2014 ask the user, never work around it",
5165
+ domain: "workflow",
5166
+ priority: "p0",
5167
+ content: "If MCP tools are unavailable, disconnected, or returning connection errors: STOP. Tell the user clearly: 'MCP server is disconnected. Please run /mcp to reconnect.' Do NOT attempt workarounds \u2014 no raw Node imports, no direct DB access, no CLI hacks, no daemon socket calls. MCP is the ONLY data interface. Working around it wastes time, hits bundling issues, and bypasses the contract boundary. Ask once, wait, proceed when reconnected."
5168
+ },
5169
+ // --- MCP Tool Catalog (Layer 0 — every agent knows what tools exist) ---
5170
+ {
5171
+ title: "MCP tools \u2014 memory and search",
5172
+ domain: "tool-use",
5173
+ priority: "p1",
5174
+ content: "recall_my_memory: search your own memories (semantic + FTS). ask_team_memory: search a colleague's memories by agent name. store_memory: persist a memory (decisions, summaries, context). commit_memory: high-importance memory that survives consolidation. search_everything: unified search across memories, tasks, entities, conversations. get_session_context: temporal window of memories around a timestamp. consolidate_memories: merge duplicate/related memories into insights. get_memory_cardinality: count memories per agent (health check)."
5175
+ },
5176
+ {
5177
+ title: "MCP tools \u2014 task orchestration",
5178
+ domain: "tool-use",
5179
+ priority: "p1",
5180
+ content: "create_task: dispatch work to an employee (auto-spawns session). The ONLY dispatch path. list_tasks: query tasks by status, assignee, project. get_task: fetch full task details by ID. update_task: change status (in_progress, done, blocked, cancelled) + add result summary. close_task: finalize a reviewed task (COO only). checkpoint_task: save progress state for crash recovery. resume_employee: re-spawn an employee session for an existing task."
5181
+ },
5182
+ {
5183
+ title: "MCP tools \u2014 knowledge graph (GraphRAG)",
5184
+ domain: "tool-use",
5185
+ priority: "p1",
5186
+ content: "query_relationships: find connections between entities in the knowledge graph. get_entity_neighbors: explore an entity's direct connections. get_hot_entities: find most-referenced entities (trending topics). get_graph_stats: graph health \u2014 entity/relationship counts, density. export_graph: export graph data for visualization. merge_entities: deduplicate entities (alias resolution). find_similar_trajectories: match tool-call patterns to past task solutions."
5187
+ },
5188
+ {
5189
+ title: "MCP tools \u2014 identity, behavior, and decisions",
5190
+ domain: "tool-use",
5191
+ priority: "p1",
5192
+ content: "get_identity: read an agent's exe.md (Layer 1 identity). update_identity: write an agent's exe.md. Identity > behavior \u2014 use for permanent rules. store_behavior: record a correction or pattern for an agent (Layer 2 expertise). list_behaviors: view an agent's active behaviors. deactivate_behavior: soft-delete a stale or conflicting behavior. store_decision: record an ADR (architectural decision record). get_decision: retrieve a past decision by query."
5193
+ },
5194
+ {
5195
+ title: "MCP tools \u2014 communication and messaging",
5196
+ domain: "tool-use",
5197
+ priority: "p1",
5198
+ content: "send_message: send supplementary context to another agent (NOT for actionable work \u2014 use create_task). acknowledge_messages: mark messages as read. send_whatsapp: send WhatsApp message via gateway (customer-facing alerts). query_conversations: search ingested conversations across all channels (WhatsApp, email, etc.)."
5199
+ },
5200
+ {
5201
+ title: "MCP tools \u2014 wiki, documents, and content",
5202
+ domain: "tool-use",
5203
+ priority: "p1",
5204
+ content: "create_wiki_page: create a wiki page in exe-wiki. list_wiki_pages: browse wiki pages. get_wiki_page: read a wiki page. update_wiki_page: edit a wiki page. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
5205
+ },
5206
+ {
5207
+ title: "MCP tools \u2014 system, operations, and admin",
5208
+ domain: "tool-use",
5209
+ priority: "p1",
5210
+ content: "get_agent_spend: token usage per agent/session (cost tracking). list_agent_sessions: view agent session history. get_session_kills: audit log of killed sessions. get_daemon_health: check exed daemon status. get_auto_wake_status: daemon auto-wake configuration. get_worker_gate: check worker deployment gates. run_memory_audit: health check \u2014 duplicates, null vectors, orphaned rows. run_consolidation: trigger sleep-time memory consolidation. cloud_sync: force a cloud sync cycle. backup_vps: trigger VPS backup."
5211
+ },
5212
+ {
5213
+ title: "MCP tools \u2014 config, licensing, and team",
5214
+ domain: "tool-use",
5215
+ priority: "p1",
5216
+ content: "set_agent_config: view/change per-agent runtime and model settings. list_employees: view the employee roster. add_person: add a person to the CRM contacts roster. list_people: browse CRM contacts. get_person: fetch contact details. get_license_status: check license validity. create_license: generate a new license key (admin). list_licenses: view all issued licenses. activate_license: activate a license on a device."
5217
+ },
5218
+ {
5219
+ title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
5220
+ domain: "tool-use",
5221
+ priority: "p1",
5222
+ content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. global_procedure: manage Layer 0 procedures (actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
4276
5223
  }
4277
5224
  ];
4278
5225
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -4289,7 +5236,7 @@ __export(global_procedures_exports, {
4289
5236
  loadGlobalProcedures: () => loadGlobalProcedures,
4290
5237
  storeGlobalProcedure: () => storeGlobalProcedure
4291
5238
  });
4292
- import { randomUUID as randomUUID2 } from "crypto";
5239
+ import { randomUUID as randomUUID3 } from "crypto";
4293
5240
  async function loadGlobalProcedures() {
4294
5241
  const client = getClient();
4295
5242
  const result = await client.execute({
@@ -4318,7 +5265,7 @@ ${sections.join("\n\n")}
4318
5265
  `;
4319
5266
  }
4320
5267
  async function storeGlobalProcedure(input) {
4321
- const id = randomUUID2();
5268
+ const id = randomUUID3();
4322
5269
  const now = (/* @__PURE__ */ new Date()).toISOString();
4323
5270
  const client = getClient();
4324
5271
  await client.execute({
@@ -4358,21 +5305,22 @@ init_tasks();
4358
5305
  import { spawnSync } from "child_process";
4359
5306
 
4360
5307
  // src/lib/store.ts
4361
- import { createHash } from "crypto";
5308
+ init_memory();
4362
5309
  init_database();
5310
+ import { createHash } from "crypto";
4363
5311
 
4364
5312
  // src/lib/keychain.ts
4365
5313
  import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
4366
- import { existsSync as existsSync12 } from "fs";
4367
- import path15 from "path";
4368
- import os10 from "os";
5314
+ import { existsSync as existsSync14 } from "fs";
5315
+ import path17 from "path";
5316
+ import os11 from "os";
4369
5317
  var SERVICE = "exe-mem";
4370
5318
  var ACCOUNT = "master-key";
4371
5319
  function getKeyDir() {
4372
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path15.join(os10.homedir(), ".exe-os");
5320
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path17.join(os11.homedir(), ".exe-os");
4373
5321
  }
4374
5322
  function getKeyPath() {
4375
- return path15.join(getKeyDir(), "master.key");
5323
+ return path17.join(getKeyDir(), "master.key");
4376
5324
  }
4377
5325
  async function tryKeytar() {
4378
5326
  try {
@@ -4393,9 +5341,9 @@ async function getMasterKey() {
4393
5341
  }
4394
5342
  }
4395
5343
  const keyPath = getKeyPath();
4396
- if (!existsSync12(keyPath)) {
5344
+ if (!existsSync14(keyPath)) {
4397
5345
  process.stderr.write(
4398
- `[keychain] Key not found at ${keyPath} (HOME=${os10.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
5346
+ `[keychain] Key not found at ${keyPath} (HOME=${os11.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
4399
5347
  `
4400
5348
  );
4401
5349
  return null;
@@ -4474,6 +5422,11 @@ async function initStore(options) {
4474
5422
  encryptionKey: hexKey
4475
5423
  });
4476
5424
  await retryOnBusy2(() => ensureSchema(), "ensureSchema");
5425
+ try {
5426
+ const { initDaemonClient: initDaemonClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
5427
+ await initDaemonClient2();
5428
+ } catch {
5429
+ }
4477
5430
  if (!options?.lightweight) {
4478
5431
  try {
4479
5432
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -4495,7 +5448,7 @@ async function initStore(options) {
4495
5448
  }
4496
5449
 
4497
5450
  // src/lib/codex-stop-finalizer.ts
4498
- import path17 from "path";
5451
+ import path19 from "path";
4499
5452
  var SECRET_PATH_PATTERNS = [
4500
5453
  /(^|\/)\.env($|\.)/i,
4501
5454
  /(^|\/)[^/]*credentials[^/]*(\/|$)/i,
@@ -4538,7 +5491,7 @@ function parseGitStatusPorcelainZ(output) {
4538
5491
  }
4539
5492
  function isSecretLikePath(filePath) {
4540
5493
  const normalized = normalizePath(filePath);
4541
- const basename = path17.basename(normalized);
5494
+ const basename = path19.basename(normalized);
4542
5495
  if (basename === ".env" || basename.startsWith(".env.")) {
4543
5496
  return true;
4544
5497
  }