@askexenow/exe-os 0.9.8 → 0.9.9

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 (101) hide show
  1. package/dist/bin/backfill-conversations.js +222 -49
  2. package/dist/bin/backfill-responses.js +221 -48
  3. package/dist/bin/backfill-vectors.js +225 -52
  4. package/dist/bin/cleanup-stale-review-tasks.js +150 -28
  5. package/dist/bin/cli.js +1295 -856
  6. package/dist/bin/exe-agent-config.js +36 -8
  7. package/dist/bin/exe-agent.js +14 -4
  8. package/dist/bin/exe-assign.js +221 -48
  9. package/dist/bin/exe-boot.js +778 -427
  10. package/dist/bin/exe-call.js +41 -13
  11. package/dist/bin/exe-cloud.js +163 -58
  12. package/dist/bin/exe-dispatch.js +276 -139
  13. package/dist/bin/exe-doctor.js +145 -27
  14. package/dist/bin/exe-export-behaviors.js +141 -23
  15. package/dist/bin/exe-forget.js +137 -19
  16. package/dist/bin/exe-gateway.js +677 -388
  17. package/dist/bin/exe-heartbeat.js +227 -108
  18. package/dist/bin/exe-kill.js +138 -20
  19. package/dist/bin/exe-launch-agent.js +172 -39
  20. package/dist/bin/exe-link.js +291 -100
  21. package/dist/bin/exe-new-employee.js +214 -106
  22. package/dist/bin/exe-pending-messages.js +395 -33
  23. package/dist/bin/exe-pending-notifications.js +684 -99
  24. package/dist/bin/exe-pending-reviews.js +420 -74
  25. package/dist/bin/exe-rename.js +147 -49
  26. package/dist/bin/exe-review.js +138 -20
  27. package/dist/bin/exe-search.js +240 -69
  28. package/dist/bin/exe-session-cleanup.js +440 -250
  29. package/dist/bin/exe-settings.js +61 -17
  30. package/dist/bin/exe-start-codex.js +158 -39
  31. package/dist/bin/exe-start-opencode.js +157 -38
  32. package/dist/bin/exe-status.js +151 -29
  33. package/dist/bin/exe-team.js +138 -20
  34. package/dist/bin/git-sweep.js +404 -212
  35. package/dist/bin/graph-backfill.js +137 -19
  36. package/dist/bin/graph-export.js +140 -22
  37. package/dist/bin/install.js +90 -61
  38. package/dist/bin/scan-tasks.js +412 -220
  39. package/dist/bin/setup.js +564 -293
  40. package/dist/bin/shard-migrate.js +139 -21
  41. package/dist/bin/update.js +138 -49
  42. package/dist/bin/wiki-sync.js +137 -19
  43. package/dist/gateway/index.js +533 -320
  44. package/dist/hooks/bug-report-worker.js +344 -193
  45. package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
  46. package/dist/hooks/commit-complete.js +402 -210
  47. package/dist/hooks/error-recall.js +245 -74
  48. package/dist/hooks/exe-heartbeat-hook.js +16 -6
  49. package/dist/hooks/ingest-worker.js +3423 -3157
  50. package/dist/hooks/ingest.js +832 -97
  51. package/dist/hooks/instructions-loaded.js +227 -54
  52. package/dist/hooks/notification.js +216 -43
  53. package/dist/hooks/post-compact.js +239 -62
  54. package/dist/hooks/pre-compact.js +408 -216
  55. package/dist/hooks/pre-tool-use.js +268 -90
  56. package/dist/hooks/prompt-ingest-worker.js +352 -102
  57. package/dist/hooks/prompt-submit.js +541 -328
  58. package/dist/hooks/response-ingest-worker.js +372 -122
  59. package/dist/hooks/session-end.js +443 -240
  60. package/dist/hooks/session-start.js +313 -127
  61. package/dist/hooks/stop.js +293 -98
  62. package/dist/hooks/subagent-stop.js +239 -62
  63. package/dist/hooks/summary-worker.js +568 -236
  64. package/dist/index.js +538 -324
  65. package/dist/lib/agent-config.js +28 -6
  66. package/dist/lib/cloud-sync.js +284 -105
  67. package/dist/lib/config.js +30 -10
  68. package/dist/lib/consolidation.js +16 -6
  69. package/dist/lib/database.js +123 -25
  70. package/dist/lib/db-daemon-client.js +73 -19
  71. package/dist/lib/db.js +123 -25
  72. package/dist/lib/device-registry.js +133 -35
  73. package/dist/lib/embedder.js +107 -32
  74. package/dist/lib/employee-templates.js +14 -4
  75. package/dist/lib/employees.js +41 -13
  76. package/dist/lib/exe-daemon-client.js +88 -22
  77. package/dist/lib/exe-daemon.js +935 -587
  78. package/dist/lib/hybrid-search.js +240 -69
  79. package/dist/lib/identity.js +18 -8
  80. package/dist/lib/license.js +133 -48
  81. package/dist/lib/messaging.js +116 -56
  82. package/dist/lib/reminders.js +14 -4
  83. package/dist/lib/schedules.js +137 -19
  84. package/dist/lib/skill-learning.js +33 -6
  85. package/dist/lib/store.js +137 -19
  86. package/dist/lib/task-router.js +14 -4
  87. package/dist/lib/tasks.js +280 -234
  88. package/dist/lib/tmux-routing.js +172 -125
  89. package/dist/lib/token-spend.js +26 -8
  90. package/dist/mcp/server.js +1326 -609
  91. package/dist/mcp/tools/complete-reminder.js +14 -4
  92. package/dist/mcp/tools/create-reminder.js +14 -4
  93. package/dist/mcp/tools/create-task.js +306 -248
  94. package/dist/mcp/tools/deactivate-behavior.js +16 -6
  95. package/dist/mcp/tools/list-reminders.js +14 -4
  96. package/dist/mcp/tools/list-tasks.js +123 -107
  97. package/dist/mcp/tools/send-message.js +75 -29
  98. package/dist/mcp/tools/update-task.js +1848 -199
  99. package/dist/runtime/index.js +441 -248
  100. package/dist/tui/App.js +761 -424
  101. package/package.json +1 -1
@@ -26,6 +26,44 @@ var __copyProps = (to, from, except, desc) => {
26
26
  };
27
27
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
28
 
29
+ // src/lib/secure-files.ts
30
+ import { chmodSync, existsSync, mkdirSync } from "fs";
31
+ import { chmod, mkdir } from "fs/promises";
32
+ async function ensurePrivateDir(dirPath) {
33
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
34
+ try {
35
+ await chmod(dirPath, PRIVATE_DIR_MODE);
36
+ } catch {
37
+ }
38
+ }
39
+ function ensurePrivateDirSync(dirPath) {
40
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
41
+ try {
42
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
43
+ } catch {
44
+ }
45
+ }
46
+ async function enforcePrivateFile(filePath) {
47
+ try {
48
+ await chmod(filePath, PRIVATE_FILE_MODE);
49
+ } catch {
50
+ }
51
+ }
52
+ function enforcePrivateFileSync(filePath) {
53
+ try {
54
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
55
+ } catch {
56
+ }
57
+ }
58
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
59
+ var init_secure_files = __esm({
60
+ "src/lib/secure-files.ts"() {
61
+ "use strict";
62
+ PRIVATE_DIR_MODE = 448;
63
+ PRIVATE_FILE_MODE = 384;
64
+ }
65
+ });
66
+
29
67
  // src/lib/config.ts
30
68
  var config_exports = {};
31
69
  __export(config_exports, {
@@ -42,8 +80,8 @@ __export(config_exports, {
42
80
  migrateConfig: () => migrateConfig,
43
81
  saveConfig: () => saveConfig
44
82
  });
45
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
46
- import { readFileSync, existsSync, renameSync } from "fs";
83
+ import { readFile, writeFile } from "fs/promises";
84
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
47
85
  import path from "path";
48
86
  import os from "os";
49
87
  function resolveDataDir() {
@@ -51,7 +89,7 @@ function resolveDataDir() {
51
89
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
52
90
  const newDir = path.join(os.homedir(), ".exe-os");
53
91
  const legacyDir = path.join(os.homedir(), ".exe-mem");
54
- if (!existsSync(newDir) && existsSync(legacyDir)) {
92
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
55
93
  try {
56
94
  renameSync(legacyDir, newDir);
57
95
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -114,9 +152,9 @@ function normalizeAutoUpdate(raw) {
114
152
  }
115
153
  async function loadConfig() {
116
154
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
117
- await mkdir(dir, { recursive: true });
155
+ await ensurePrivateDir(dir);
118
156
  const configPath = path.join(dir, "config.json");
119
- if (!existsSync(configPath)) {
157
+ if (!existsSync2(configPath)) {
120
158
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
121
159
  }
122
160
  const raw = await readFile(configPath, "utf-8");
@@ -129,6 +167,7 @@ async function loadConfig() {
129
167
  `);
130
168
  try {
131
169
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
170
+ await enforcePrivateFile(configPath);
132
171
  } catch {
133
172
  }
134
173
  }
@@ -147,7 +186,7 @@ async function loadConfig() {
147
186
  function loadConfigSync() {
148
187
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
149
188
  const configPath = path.join(dir, "config.json");
150
- if (!existsSync(configPath)) {
189
+ if (!existsSync2(configPath)) {
151
190
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
152
191
  }
153
192
  try {
@@ -165,12 +204,10 @@ function loadConfigSync() {
165
204
  }
166
205
  async function saveConfig(config) {
167
206
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
168
- await mkdir(dir, { recursive: true });
207
+ await ensurePrivateDir(dir);
169
208
  const configPath = path.join(dir, "config.json");
170
209
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
171
- if (config.cloud?.apiKey) {
172
- await chmod(configPath, 384);
173
- }
210
+ await enforcePrivateFile(configPath);
174
211
  }
175
212
  async function loadConfigFrom(configPath) {
176
213
  const raw = await readFile(configPath, "utf-8");
@@ -190,6 +227,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
190
227
  var init_config = __esm({
191
228
  "src/lib/config.ts"() {
192
229
  "use strict";
230
+ init_secure_files();
193
231
  EXE_AI_DIR = resolveDataDir();
194
232
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
195
233
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -268,7 +306,7 @@ var init_config = __esm({
268
306
 
269
307
  // src/lib/employees.ts
270
308
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
271
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
309
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
272
310
  import { execSync } from "child_process";
273
311
  import path2 from "path";
274
312
  import os2 from "os";
@@ -289,7 +327,7 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
289
327
  return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
290
328
  }
291
329
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
292
- if (!existsSync2(employeesPath)) {
330
+ if (!existsSync3(employeesPath)) {
293
331
  return [];
294
332
  }
295
333
  const raw = await readFile2(employeesPath, "utf-8");
@@ -304,7 +342,7 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
304
342
  await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
305
343
  }
306
344
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
307
- if (!existsSync2(employeesPath)) return [];
345
+ if (!existsSync3(employeesPath)) return [];
308
346
  try {
309
347
  return JSON.parse(readFileSync2(employeesPath, "utf-8"));
310
348
  } catch {
@@ -355,7 +393,7 @@ function registerBinSymlinks(name) {
355
393
  for (const suffix of ["", "-opencode"]) {
356
394
  const linkName = `${name}${suffix}`;
357
395
  const linkPath = path2.join(binDir, linkName);
358
- if (existsSync2(linkPath)) {
396
+ if (existsSync3(linkPath)) {
359
397
  skipped.push(linkName);
360
398
  continue;
361
399
  }
@@ -1020,13 +1058,50 @@ var init_database_adapter = __esm({
1020
1058
  }
1021
1059
  });
1022
1060
 
1061
+ // src/lib/daemon-auth.ts
1062
+ import crypto from "crypto";
1063
+ import path4 from "path";
1064
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
1065
+ function normalizeToken(token) {
1066
+ if (!token) return null;
1067
+ const trimmed = token.trim();
1068
+ return trimmed.length > 0 ? trimmed : null;
1069
+ }
1070
+ function readDaemonToken() {
1071
+ try {
1072
+ if (!existsSync4(DAEMON_TOKEN_PATH)) return null;
1073
+ return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
1074
+ } catch {
1075
+ return null;
1076
+ }
1077
+ }
1078
+ function ensureDaemonToken(seed) {
1079
+ const existing = readDaemonToken();
1080
+ if (existing) return existing;
1081
+ const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
1082
+ ensurePrivateDirSync(EXE_AI_DIR);
1083
+ writeFileSync2(DAEMON_TOKEN_PATH, `${token}
1084
+ `, "utf8");
1085
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
1086
+ return token;
1087
+ }
1088
+ var DAEMON_TOKEN_PATH;
1089
+ var init_daemon_auth = __esm({
1090
+ "src/lib/daemon-auth.ts"() {
1091
+ "use strict";
1092
+ init_config();
1093
+ init_secure_files();
1094
+ DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
1095
+ }
1096
+ });
1097
+
1023
1098
  // src/lib/exe-daemon-client.ts
1024
1099
  import net from "net";
1025
1100
  import os4 from "os";
1026
1101
  import { spawn } from "child_process";
1027
1102
  import { randomUUID } from "crypto";
1028
- import { existsSync as existsSync3, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
1029
- import path4 from "path";
1103
+ import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
1104
+ import path5 from "path";
1030
1105
  import { fileURLToPath } from "url";
1031
1106
  function handleData(chunk) {
1032
1107
  _buffer += chunk.toString();
@@ -1054,9 +1129,9 @@ function handleData(chunk) {
1054
1129
  }
1055
1130
  }
1056
1131
  function cleanupStaleFiles() {
1057
- if (existsSync3(PID_PATH)) {
1132
+ if (existsSync5(PID_PATH)) {
1058
1133
  try {
1059
- const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
1134
+ const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1060
1135
  if (pid > 0) {
1061
1136
  try {
1062
1137
  process.kill(pid, 0);
@@ -1077,11 +1152,11 @@ function cleanupStaleFiles() {
1077
1152
  }
1078
1153
  }
1079
1154
  function findPackageRoot() {
1080
- let dir = path4.dirname(fileURLToPath(import.meta.url));
1081
- const { root } = path4.parse(dir);
1155
+ let dir = path5.dirname(fileURLToPath(import.meta.url));
1156
+ const { root } = path5.parse(dir);
1082
1157
  while (dir !== root) {
1083
- if (existsSync3(path4.join(dir, "package.json"))) return dir;
1084
- dir = path4.dirname(dir);
1158
+ if (existsSync5(path5.join(dir, "package.json"))) return dir;
1159
+ dir = path5.dirname(dir);
1085
1160
  }
1086
1161
  return null;
1087
1162
  }
@@ -1107,16 +1182,17 @@ function spawnDaemon() {
1107
1182
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
1108
1183
  return;
1109
1184
  }
1110
- const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1111
- if (!existsSync3(daemonPath)) {
1185
+ const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1186
+ if (!existsSync5(daemonPath)) {
1112
1187
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1113
1188
  `);
1114
1189
  return;
1115
1190
  }
1116
1191
  const resolvedPath = daemonPath;
1192
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
1117
1193
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1118
1194
  `);
1119
- const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
1195
+ const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
1120
1196
  let stderrFd = "ignore";
1121
1197
  try {
1122
1198
  stderrFd = openSync(logPath, "a");
@@ -1134,7 +1210,8 @@ function spawnDaemon() {
1134
1210
  TMUX_PANE: void 0,
1135
1211
  // Prevents resolveExeSession() from scoping to one session
1136
1212
  EXE_DAEMON_SOCK: SOCKET_PATH,
1137
- EXE_DAEMON_PID: PID_PATH
1213
+ EXE_DAEMON_PID: PID_PATH,
1214
+ [DAEMON_TOKEN_ENV]: daemonToken
1138
1215
  }
1139
1216
  });
1140
1217
  child.unref();
@@ -1241,13 +1318,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
1241
1318
  return;
1242
1319
  }
1243
1320
  const id = randomUUID();
1321
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
1244
1322
  const timer = setTimeout(() => {
1245
1323
  _pending.delete(id);
1246
1324
  resolve({ error: "Request timeout" });
1247
1325
  }, timeoutMs);
1248
1326
  _pending.set(id, { resolve, timer });
1249
1327
  try {
1250
- _socket.write(JSON.stringify({ id, ...payload }) + "\n");
1328
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
1251
1329
  } catch {
1252
1330
  clearTimeout(timer);
1253
1331
  _pending.delete(id);
@@ -1258,17 +1336,19 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
1258
1336
  function isClientConnected() {
1259
1337
  return _connected;
1260
1338
  }
1261
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
1339
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _pending, MAX_BUFFER;
1262
1340
  var init_exe_daemon_client = __esm({
1263
1341
  "src/lib/exe-daemon-client.ts"() {
1264
1342
  "use strict";
1265
1343
  init_config();
1266
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
1267
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
1268
- SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
1344
+ init_daemon_auth();
1345
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
1346
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
1347
+ SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
1269
1348
  SPAWN_LOCK_STALE_MS = 3e4;
1270
1349
  CONNECT_TIMEOUT_MS = 15e3;
1271
1350
  REQUEST_TIMEOUT_MS = 3e4;
1351
+ DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
1272
1352
  _socket = null;
1273
1353
  _connected = false;
1274
1354
  _buffer = "";
@@ -1847,6 +1927,7 @@ async function ensureSchema() {
1847
1927
  project TEXT NOT NULL,
1848
1928
  summary TEXT NOT NULL,
1849
1929
  task_file TEXT,
1930
+ session_scope TEXT,
1850
1931
  read INTEGER NOT NULL DEFAULT 0,
1851
1932
  created_at TEXT NOT NULL
1852
1933
  );
@@ -1855,7 +1936,7 @@ async function ensureSchema() {
1855
1936
  ON notifications(read);
1856
1937
 
1857
1938
  CREATE INDEX IF NOT EXISTS idx_notifications_agent
1858
- ON notifications(agent_id);
1939
+ ON notifications(agent_id, session_scope);
1859
1940
 
1860
1941
  CREATE INDEX IF NOT EXISTS idx_notifications_task_file
1861
1942
  ON notifications(task_file);
@@ -1893,6 +1974,7 @@ async function ensureSchema() {
1893
1974
  target_agent TEXT NOT NULL,
1894
1975
  target_project TEXT,
1895
1976
  target_device TEXT NOT NULL DEFAULT 'local',
1977
+ session_scope TEXT,
1896
1978
  content TEXT NOT NULL,
1897
1979
  priority TEXT DEFAULT 'normal',
1898
1980
  status TEXT DEFAULT 'pending',
@@ -1906,10 +1988,31 @@ async function ensureSchema() {
1906
1988
  );
1907
1989
 
1908
1990
  CREATE INDEX IF NOT EXISTS idx_messages_target
1909
- ON messages(target_agent, status);
1991
+ ON messages(target_agent, session_scope, status);
1910
1992
 
1911
1993
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
1912
- ON messages(target_agent, from_agent, server_seq);
1994
+ ON messages(target_agent, session_scope, from_agent, server_seq);
1995
+ `);
1996
+ try {
1997
+ await client.execute({
1998
+ sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
1999
+ args: []
2000
+ });
2001
+ } catch {
2002
+ }
2003
+ try {
2004
+ await client.execute({
2005
+ sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
2006
+ args: []
2007
+ });
2008
+ } catch {
2009
+ }
2010
+ await client.executeMultiple(`
2011
+ CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
2012
+ ON notifications(agent_id, session_scope, read, created_at);
2013
+
2014
+ CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
2015
+ ON messages(target_agent, session_scope, status, created_at);
1913
2016
  `);
1914
2017
  try {
1915
2018
  await client.execute({
@@ -2493,6 +2596,13 @@ async function ensureSchema() {
2493
2596
  } catch {
2494
2597
  }
2495
2598
  }
2599
+ try {
2600
+ await client.execute({
2601
+ sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
2602
+ args: []
2603
+ });
2604
+ } catch {
2605
+ }
2496
2606
  }
2497
2607
  async function disposeDatabase() {
2498
2608
  if (_walCheckpointTimer) {
@@ -2734,14 +2844,14 @@ __export(keychain_exports, {
2734
2844
  setMasterKey: () => setMasterKey
2735
2845
  });
2736
2846
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
2737
- import { existsSync as existsSync4 } from "fs";
2738
- import path5 from "path";
2847
+ import { existsSync as existsSync6 } from "fs";
2848
+ import path6 from "path";
2739
2849
  import os5 from "os";
2740
2850
  function getKeyDir() {
2741
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path5.join(os5.homedir(), ".exe-os");
2851
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
2742
2852
  }
2743
2853
  function getKeyPath() {
2744
- return path5.join(getKeyDir(), "master.key");
2854
+ return path6.join(getKeyDir(), "master.key");
2745
2855
  }
2746
2856
  async function tryKeytar() {
2747
2857
  try {
@@ -2762,7 +2872,7 @@ async function getMasterKey() {
2762
2872
  }
2763
2873
  }
2764
2874
  const keyPath = getKeyPath();
2765
- if (!existsSync4(keyPath)) {
2875
+ if (!existsSync6(keyPath)) {
2766
2876
  process.stderr.write(
2767
2877
  `[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2768
2878
  `
@@ -2805,7 +2915,7 @@ async function deleteMasterKey() {
2805
2915
  }
2806
2916
  }
2807
2917
  const keyPath = getKeyPath();
2808
- if (existsSync4(keyPath)) {
2918
+ if (existsSync6(keyPath)) {
2809
2919
  await unlink(keyPath);
2810
2920
  }
2811
2921
  }
@@ -2907,6 +3017,7 @@ var shard_manager_exports = {};
2907
3017
  __export(shard_manager_exports, {
2908
3018
  disposeShards: () => disposeShards,
2909
3019
  ensureShardSchema: () => ensureShardSchema,
3020
+ getOpenShardCount: () => getOpenShardCount,
2910
3021
  getReadyShardClient: () => getReadyShardClient,
2911
3022
  getShardClient: () => getShardClient,
2912
3023
  getShardsDir: () => getShardsDir,
@@ -2915,15 +3026,18 @@ __export(shard_manager_exports, {
2915
3026
  listShards: () => listShards,
2916
3027
  shardExists: () => shardExists
2917
3028
  });
2918
- import path6 from "path";
2919
- import { existsSync as existsSync5, mkdirSync, readdirSync } from "fs";
3029
+ import path7 from "path";
3030
+ import { existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync } from "fs";
2920
3031
  import { createClient as createClient2 } from "@libsql/client";
2921
3032
  function initShardManager(encryptionKey) {
2922
3033
  _encryptionKey = encryptionKey;
2923
- if (!existsSync5(SHARDS_DIR)) {
2924
- mkdirSync(SHARDS_DIR, { recursive: true });
3034
+ if (!existsSync7(SHARDS_DIR)) {
3035
+ mkdirSync2(SHARDS_DIR, { recursive: true });
2925
3036
  }
2926
3037
  _shardingEnabled = true;
3038
+ if (_evictionTimer) clearInterval(_evictionTimer);
3039
+ _evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
3040
+ _evictionTimer.unref();
2927
3041
  }
2928
3042
  function isShardingEnabled() {
2929
3043
  return _shardingEnabled;
@@ -2940,21 +3054,28 @@ function getShardClient(projectName) {
2940
3054
  throw new Error(`Invalid project name for shard: "${projectName}"`);
2941
3055
  }
2942
3056
  const cached = _shards.get(safeName);
2943
- if (cached) return cached;
2944
- const dbPath = path6.join(SHARDS_DIR, `${safeName}.db`);
3057
+ if (cached) {
3058
+ _shardLastAccess.set(safeName, Date.now());
3059
+ return cached;
3060
+ }
3061
+ while (_shards.size >= MAX_OPEN_SHARDS) {
3062
+ evictLRU();
3063
+ }
3064
+ const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
2945
3065
  const client = createClient2({
2946
3066
  url: `file:${dbPath}`,
2947
3067
  encryptionKey: _encryptionKey
2948
3068
  });
2949
3069
  _shards.set(safeName, client);
3070
+ _shardLastAccess.set(safeName, Date.now());
2950
3071
  return client;
2951
3072
  }
2952
3073
  function shardExists(projectName) {
2953
3074
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
2954
- return existsSync5(path6.join(SHARDS_DIR, `${safeName}.db`));
3075
+ return existsSync7(path7.join(SHARDS_DIR, `${safeName}.db`));
2955
3076
  }
2956
3077
  function listShards() {
2957
- if (!existsSync5(SHARDS_DIR)) return [];
3078
+ if (!existsSync7(SHARDS_DIR)) return [];
2958
3079
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
2959
3080
  }
2960
3081
  async function ensureShardSchema(client) {
@@ -3006,6 +3127,8 @@ async function ensureShardSchema(client) {
3006
3127
  for (const col of [
3007
3128
  "ALTER TABLE memories ADD COLUMN task_id TEXT",
3008
3129
  "ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
3130
+ "ALTER TABLE memories ADD COLUMN author_device_id TEXT",
3131
+ "ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
3009
3132
  "ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
3010
3133
  "ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
3011
3134
  "ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
@@ -3143,21 +3266,69 @@ async function getReadyShardClient(projectName) {
3143
3266
  await ensureShardSchema(client);
3144
3267
  return client;
3145
3268
  }
3269
+ function evictLRU() {
3270
+ let oldest = null;
3271
+ let oldestTime = Infinity;
3272
+ for (const [name, time] of _shardLastAccess) {
3273
+ if (time < oldestTime) {
3274
+ oldestTime = time;
3275
+ oldest = name;
3276
+ }
3277
+ }
3278
+ if (oldest) {
3279
+ const client = _shards.get(oldest);
3280
+ if (client) {
3281
+ client.close();
3282
+ }
3283
+ _shards.delete(oldest);
3284
+ _shardLastAccess.delete(oldest);
3285
+ }
3286
+ }
3287
+ function evictIdleShards() {
3288
+ const now = Date.now();
3289
+ const toEvict = [];
3290
+ for (const [name, lastAccess] of _shardLastAccess) {
3291
+ if (now - lastAccess > SHARD_IDLE_MS) {
3292
+ toEvict.push(name);
3293
+ }
3294
+ }
3295
+ for (const name of toEvict) {
3296
+ const client = _shards.get(name);
3297
+ if (client) {
3298
+ client.close();
3299
+ }
3300
+ _shards.delete(name);
3301
+ _shardLastAccess.delete(name);
3302
+ }
3303
+ }
3304
+ function getOpenShardCount() {
3305
+ return _shards.size;
3306
+ }
3146
3307
  function disposeShards() {
3308
+ if (_evictionTimer) {
3309
+ clearInterval(_evictionTimer);
3310
+ _evictionTimer = null;
3311
+ }
3147
3312
  for (const [, client] of _shards) {
3148
3313
  client.close();
3149
3314
  }
3150
3315
  _shards.clear();
3316
+ _shardLastAccess.clear();
3151
3317
  _shardingEnabled = false;
3152
3318
  _encryptionKey = null;
3153
3319
  }
3154
- var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
3320
+ var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
3155
3321
  var init_shard_manager = __esm({
3156
3322
  "src/lib/shard-manager.ts"() {
3157
3323
  "use strict";
3158
3324
  init_config();
3159
- SHARDS_DIR = path6.join(EXE_AI_DIR, "shards");
3325
+ SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
3326
+ SHARD_IDLE_MS = 5 * 60 * 1e3;
3327
+ MAX_OPEN_SHARDS = 10;
3328
+ EVICTION_INTERVAL_MS = 60 * 1e3;
3160
3329
  _shards = /* @__PURE__ */ new Map();
3330
+ _shardLastAccess = /* @__PURE__ */ new Map();
3331
+ _evictionTimer = null;
3161
3332
  _encryptionKey = null;
3162
3333
  _shardingEnabled = false;
3163
3334
  }
@@ -3262,14 +3433,14 @@ __export(session_registry_exports, {
3262
3433
  pruneStaleSessions: () => pruneStaleSessions,
3263
3434
  registerSession: () => registerSession
3264
3435
  });
3265
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync6 } from "fs";
3436
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync8 } from "fs";
3266
3437
  import { execSync as execSync2 } from "child_process";
3267
- import path7 from "path";
3438
+ import path8 from "path";
3268
3439
  import os6 from "os";
3269
3440
  function registerSession(entry) {
3270
- const dir = path7.dirname(REGISTRY_PATH);
3271
- if (!existsSync6(dir)) {
3272
- mkdirSync2(dir, { recursive: true });
3441
+ const dir = path8.dirname(REGISTRY_PATH);
3442
+ if (!existsSync8(dir)) {
3443
+ mkdirSync3(dir, { recursive: true });
3273
3444
  }
3274
3445
  const sessions = listSessions();
3275
3446
  const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
@@ -3278,11 +3449,11 @@ function registerSession(entry) {
3278
3449
  } else {
3279
3450
  sessions.push(entry);
3280
3451
  }
3281
- writeFileSync2(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
3452
+ writeFileSync3(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
3282
3453
  }
3283
3454
  function listSessions() {
3284
3455
  try {
3285
- const raw = readFileSync4(REGISTRY_PATH, "utf8");
3456
+ const raw = readFileSync5(REGISTRY_PATH, "utf8");
3286
3457
  return JSON.parse(raw);
3287
3458
  } catch {
3288
3459
  return [];
@@ -3303,7 +3474,7 @@ function pruneStaleSessions() {
3303
3474
  const alive = sessions.filter((s) => liveSet.has(s.windowName));
3304
3475
  const pruned = sessions.length - alive.length;
3305
3476
  if (pruned > 0) {
3306
- writeFileSync2(REGISTRY_PATH, JSON.stringify(alive, null, 2));
3477
+ writeFileSync3(REGISTRY_PATH, JSON.stringify(alive, null, 2));
3307
3478
  }
3308
3479
  return pruned;
3309
3480
  }
@@ -3311,7 +3482,7 @@ var REGISTRY_PATH;
3311
3482
  var init_session_registry = __esm({
3312
3483
  "src/lib/session-registry.ts"() {
3313
3484
  "use strict";
3314
- REGISTRY_PATH = path7.join(os6.homedir(), ".exe-os", "session-registry.json");
3485
+ REGISTRY_PATH = path8.join(os6.homedir(), ".exe-os", "session-registry.json");
3315
3486
  }
3316
3487
  });
3317
3488
 
@@ -3591,12 +3762,12 @@ var init_runtime_table = __esm({
3591
3762
  });
3592
3763
 
3593
3764
  // src/lib/agent-config.ts
3594
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
3595
- import path8 from "path";
3765
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync9 } from "fs";
3766
+ import path9 from "path";
3596
3767
  function loadAgentConfig() {
3597
- if (!existsSync7(AGENT_CONFIG_PATH)) return {};
3768
+ if (!existsSync9(AGENT_CONFIG_PATH)) return {};
3598
3769
  try {
3599
- return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
3770
+ return JSON.parse(readFileSync6(AGENT_CONFIG_PATH, "utf-8"));
3600
3771
  } catch {
3601
3772
  return {};
3602
3773
  }
@@ -3615,7 +3786,8 @@ var init_agent_config = __esm({
3615
3786
  "use strict";
3616
3787
  init_config();
3617
3788
  init_runtime_table();
3618
- AGENT_CONFIG_PATH = path8.join(EXE_AI_DIR, "agent-config.json");
3789
+ init_secure_files();
3790
+ AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
3619
3791
  DEFAULT_MODELS = {
3620
3792
  claude: "claude-opus-4",
3621
3793
  codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
@@ -3633,17 +3805,17 @@ __export(intercom_queue_exports, {
3633
3805
  queueIntercom: () => queueIntercom,
3634
3806
  readQueue: () => readQueue
3635
3807
  });
3636
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
3637
- import path9 from "path";
3808
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
3809
+ import path10 from "path";
3638
3810
  import os7 from "os";
3639
3811
  function ensureDir() {
3640
- const dir = path9.dirname(QUEUE_PATH);
3641
- if (!existsSync8(dir)) mkdirSync4(dir, { recursive: true });
3812
+ const dir = path10.dirname(QUEUE_PATH);
3813
+ if (!existsSync10(dir)) mkdirSync4(dir, { recursive: true });
3642
3814
  }
3643
3815
  function readQueue() {
3644
3816
  try {
3645
- if (!existsSync8(QUEUE_PATH)) return [];
3646
- return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
3817
+ if (!existsSync10(QUEUE_PATH)) return [];
3818
+ return JSON.parse(readFileSync7(QUEUE_PATH, "utf8"));
3647
3819
  } catch {
3648
3820
  return [];
3649
3821
  }
@@ -3651,7 +3823,7 @@ function readQueue() {
3651
3823
  function writeQueue(queue) {
3652
3824
  ensureDir();
3653
3825
  const tmp = `${QUEUE_PATH}.tmp`;
3654
- writeFileSync4(tmp, JSON.stringify(queue, null, 2));
3826
+ writeFileSync5(tmp, JSON.stringify(queue, null, 2));
3655
3827
  renameSync3(tmp, QUEUE_PATH);
3656
3828
  }
3657
3829
  function queueIntercom(targetSession, reason) {
@@ -3743,10 +3915,10 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
3743
3915
  var init_intercom_queue = __esm({
3744
3916
  "src/lib/intercom-queue.ts"() {
3745
3917
  "use strict";
3746
- QUEUE_PATH = path9.join(os7.homedir(), ".exe-os", "intercom-queue.json");
3918
+ QUEUE_PATH = path10.join(os7.homedir(), ".exe-os", "intercom-queue.json");
3747
3919
  MAX_RETRIES2 = 5;
3748
3920
  TTL_MS = 60 * 60 * 1e3;
3749
- INTERCOM_LOG = path9.join(os7.homedir(), ".exe-os", "intercom.log");
3921
+ INTERCOM_LOG = path10.join(os7.homedir(), ".exe-os", "intercom.log");
3750
3922
  }
3751
3923
  });
3752
3924
 
@@ -3767,9 +3939,12 @@ __export(license_exports, {
3767
3939
  stopLicenseRevalidation: () => stopLicenseRevalidation,
3768
3940
  validateLicense: () => validateLicense
3769
3941
  });
3770
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
3942
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync5 } from "fs";
3771
3943
  import { randomUUID as randomUUID3 } from "crypto";
3772
- import path10 from "path";
3944
+ import { createRequire as createRequire2 } from "module";
3945
+ import { pathToFileURL as pathToFileURL2 } from "url";
3946
+ import os8 from "os";
3947
+ import path11 from "path";
3773
3948
  import { jwtVerify, importSPKI } from "jose";
3774
3949
  async function fetchRetry(url, init) {
3775
3950
  try {
@@ -3780,37 +3955,37 @@ async function fetchRetry(url, init) {
3780
3955
  }
3781
3956
  }
3782
3957
  function loadDeviceId() {
3783
- const deviceJsonPath = path10.join(EXE_AI_DIR, "device.json");
3958
+ const deviceJsonPath = path11.join(EXE_AI_DIR, "device.json");
3784
3959
  try {
3785
- if (existsSync9(deviceJsonPath)) {
3786
- const data = JSON.parse(readFileSync7(deviceJsonPath, "utf8"));
3960
+ if (existsSync11(deviceJsonPath)) {
3961
+ const data = JSON.parse(readFileSync8(deviceJsonPath, "utf8"));
3787
3962
  if (data.deviceId) return data.deviceId;
3788
3963
  }
3789
3964
  } catch {
3790
3965
  }
3791
3966
  try {
3792
- if (existsSync9(DEVICE_ID_PATH)) {
3793
- const id2 = readFileSync7(DEVICE_ID_PATH, "utf8").trim();
3967
+ if (existsSync11(DEVICE_ID_PATH)) {
3968
+ const id2 = readFileSync8(DEVICE_ID_PATH, "utf8").trim();
3794
3969
  if (id2) return id2;
3795
3970
  }
3796
3971
  } catch {
3797
3972
  }
3798
3973
  const id = randomUUID3();
3799
3974
  mkdirSync5(EXE_AI_DIR, { recursive: true });
3800
- writeFileSync5(DEVICE_ID_PATH, id, "utf8");
3975
+ writeFileSync6(DEVICE_ID_PATH, id, "utf8");
3801
3976
  return id;
3802
3977
  }
3803
3978
  function loadLicense() {
3804
3979
  try {
3805
- if (!existsSync9(LICENSE_PATH)) return null;
3806
- return readFileSync7(LICENSE_PATH, "utf8").trim();
3980
+ if (!existsSync11(LICENSE_PATH)) return null;
3981
+ return readFileSync8(LICENSE_PATH, "utf8").trim();
3807
3982
  } catch {
3808
3983
  return null;
3809
3984
  }
3810
3985
  }
3811
3986
  function saveLicense(apiKey) {
3812
3987
  mkdirSync5(EXE_AI_DIR, { recursive: true });
3813
- writeFileSync5(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
3988
+ writeFileSync6(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
3814
3989
  }
3815
3990
  async function verifyLicenseJwt(token) {
3816
3991
  try {
@@ -3836,8 +4011,8 @@ async function verifyLicenseJwt(token) {
3836
4011
  }
3837
4012
  async function getCachedLicense() {
3838
4013
  try {
3839
- if (!existsSync9(CACHE_PATH)) return null;
3840
- const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
4014
+ if (!existsSync11(CACHE_PATH)) return null;
4015
+ const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
3841
4016
  if (!raw.token || typeof raw.token !== "string") return null;
3842
4017
  return await verifyLicenseJwt(raw.token);
3843
4018
  } catch {
@@ -3846,8 +4021,8 @@ async function getCachedLicense() {
3846
4021
  }
3847
4022
  function readCachedToken() {
3848
4023
  try {
3849
- if (!existsSync9(CACHE_PATH)) return null;
3850
- const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
4024
+ if (!existsSync11(CACHE_PATH)) return null;
4025
+ const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
3851
4026
  return typeof raw.token === "string" ? raw.token : null;
3852
4027
  } catch {
3853
4028
  return null;
@@ -3881,57 +4056,131 @@ function getRawCachedPlan() {
3881
4056
  }
3882
4057
  function cacheResponse(token) {
3883
4058
  try {
3884
- writeFileSync5(CACHE_PATH, JSON.stringify({ token }), "utf8");
4059
+ writeFileSync6(CACHE_PATH, JSON.stringify({ token }), "utf8");
3885
4060
  } catch {
3886
4061
  }
3887
4062
  }
3888
- async function validateLicense(apiKey, deviceId) {
3889
- const did = deviceId ?? loadDeviceId();
4063
+ function loadPrismaForLicense() {
4064
+ if (_prismaFailed) return null;
4065
+ const dbUrl = process.env.DATABASE_URL;
4066
+ if (!dbUrl) {
4067
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path11.join(os8.homedir(), "exe-db");
4068
+ if (!existsSync11(path11.join(exeDbRoot, "package.json"))) {
4069
+ _prismaFailed = true;
4070
+ return null;
4071
+ }
4072
+ }
4073
+ if (!_prismaPromise) {
4074
+ _prismaPromise = (async () => {
4075
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
4076
+ if (explicitPath) {
4077
+ const mod2 = await import(pathToFileURL2(explicitPath).href);
4078
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
4079
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
4080
+ return new Ctor2();
4081
+ }
4082
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path11.join(os8.homedir(), "exe-db");
4083
+ const req = createRequire2(path11.join(exeDbRoot, "package.json"));
4084
+ const entry = req.resolve("@prisma/client");
4085
+ const mod = await import(pathToFileURL2(entry).href);
4086
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
4087
+ if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
4088
+ return new Ctor();
4089
+ })().catch((err) => {
4090
+ _prismaFailed = true;
4091
+ _prismaPromise = null;
4092
+ throw err;
4093
+ });
4094
+ }
4095
+ return _prismaPromise;
4096
+ }
4097
+ async function validateViaPostgres(apiKey) {
4098
+ const loader = loadPrismaForLicense();
4099
+ if (!loader) return null;
4100
+ try {
4101
+ const prisma = await loader;
4102
+ const rows = await prisma.$queryRawUnsafe(
4103
+ `SELECT plan, email, status, device_limit, employee_limit, memory_limit, expires_at
4104
+ FROM billing.licenses WHERE key = $1 LIMIT 1`,
4105
+ apiKey
4106
+ );
4107
+ if (!rows || rows.length === 0) return null;
4108
+ const row = rows[0];
4109
+ if (row.status !== "active") return null;
4110
+ if (row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date()) return null;
4111
+ const plan = row.plan;
4112
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
4113
+ return {
4114
+ valid: true,
4115
+ plan,
4116
+ email: row.email,
4117
+ expiresAt: row.expires_at ? new Date(row.expires_at).toISOString() : null,
4118
+ deviceLimit: row.device_limit ?? limits.devices,
4119
+ employeeLimit: row.employee_limit ?? limits.employees,
4120
+ memoryLimit: row.memory_limit ?? limits.memories
4121
+ };
4122
+ } catch {
4123
+ return null;
4124
+ }
4125
+ }
4126
+ async function validateViaCFWorker(apiKey, deviceId) {
3890
4127
  try {
3891
4128
  const res = await fetchRetry(`${API_BASE}/auth/activate`, {
3892
4129
  method: "POST",
3893
4130
  headers: { "Content-Type": "application/json" },
3894
- body: JSON.stringify({ apiKey, deviceId: did }),
4131
+ body: JSON.stringify({ apiKey, deviceId }),
3895
4132
  signal: AbortSignal.timeout(1e4)
3896
4133
  });
3897
- if (res.ok) {
3898
- const data = await res.json();
3899
- if (data.error === "device_limit_exceeded") {
3900
- const cached2 = await getCachedLicense();
3901
- if (cached2) return cached2;
3902
- const raw2 = getRawCachedPlan();
3903
- if (raw2) return { ...raw2, valid: false };
3904
- return { ...FREE_LICENSE, valid: false, plan: "free" };
3905
- }
3906
- if (data.token) {
3907
- cacheResponse(data.token);
3908
- const verified = await verifyLicenseJwt(data.token);
3909
- if (verified) return verified;
3910
- }
3911
- const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
3912
- return {
3913
- valid: data.valid,
3914
- plan: data.plan,
3915
- email: data.email,
3916
- expiresAt: data.expiresAt,
3917
- deviceLimit: limits.devices,
3918
- employeeLimit: limits.employees,
3919
- memoryLimit: limits.memories
3920
- };
4134
+ if (!res.ok) return null;
4135
+ const data = await res.json();
4136
+ if (data.error === "device_limit_exceeded") return null;
4137
+ if (!data.valid) return null;
4138
+ if (data.token) {
4139
+ cacheResponse(data.token);
4140
+ const verified = await verifyLicenseJwt(data.token);
4141
+ if (verified) return verified;
3921
4142
  }
3922
- const cached = await getCachedLicense();
3923
- if (cached) return cached;
3924
- const raw = getRawCachedPlan();
3925
- if (raw) return raw;
3926
- return { ...FREE_LICENSE, valid: false, plan: "free" };
4143
+ const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
4144
+ return {
4145
+ valid: data.valid,
4146
+ plan: data.plan,
4147
+ email: data.email,
4148
+ expiresAt: data.expiresAt,
4149
+ deviceLimit: limits.devices,
4150
+ employeeLimit: limits.employees,
4151
+ memoryLimit: limits.memories
4152
+ };
3927
4153
  } catch {
3928
- const cached = await getCachedLicense();
3929
- if (cached) return cached;
3930
- const rawFallback = getRawCachedPlan();
3931
- if (rawFallback) return rawFallback;
3932
- return { ...FREE_LICENSE, valid: false, error: "offline" };
4154
+ return null;
3933
4155
  }
3934
4156
  }
4157
+ async function validateLicense(apiKey, deviceId) {
4158
+ const did = deviceId ?? loadDeviceId();
4159
+ const pgResult = await validateViaPostgres(apiKey);
4160
+ if (pgResult) {
4161
+ try {
4162
+ writeFileSync6(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
4163
+ } catch {
4164
+ }
4165
+ return pgResult;
4166
+ }
4167
+ const cfResult = await validateViaCFWorker(apiKey, did);
4168
+ if (cfResult) return cfResult;
4169
+ const cached = await getCachedLicense();
4170
+ if (cached) return cached;
4171
+ try {
4172
+ if (existsSync11(CACHE_PATH)) {
4173
+ const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
4174
+ if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
4175
+ return raw.pgLicense;
4176
+ }
4177
+ }
4178
+ } catch {
4179
+ }
4180
+ const rawFallback = getRawCachedPlan();
4181
+ if (rawFallback) return rawFallback;
4182
+ return { ...FREE_LICENSE, valid: false };
4183
+ }
3935
4184
  function getCacheAgeMs() {
3936
4185
  try {
3937
4186
  const { statSync: statSync2 } = __require("fs");
@@ -3945,9 +4194,9 @@ async function checkLicense() {
3945
4194
  let key = loadLicense();
3946
4195
  if (!key) {
3947
4196
  try {
3948
- const configPath = path10.join(EXE_AI_DIR, "config.json");
3949
- if (existsSync9(configPath)) {
3950
- const raw = JSON.parse(readFileSync7(configPath, "utf8"));
4197
+ const configPath = path11.join(EXE_AI_DIR, "config.json");
4198
+ if (existsSync11(configPath)) {
4199
+ const raw = JSON.parse(readFileSync8(configPath, "utf8"));
3951
4200
  const cloud = raw.cloud;
3952
4201
  if (cloud?.apiKey) {
3953
4202
  key = cloud.apiKey;
@@ -4101,14 +4350,14 @@ function stopLicenseRevalidation() {
4101
4350
  _revalTimer = null;
4102
4351
  }
4103
4352
  }
4104
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS, _revalTimer;
4353
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, _prismaPromise, _prismaFailed, CACHE_MAX_AGE_MS, _revalTimer;
4105
4354
  var init_license = __esm({
4106
4355
  "src/lib/license.ts"() {
4107
4356
  "use strict";
4108
4357
  init_config();
4109
- LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
4110
- CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
4111
- DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
4358
+ LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
4359
+ CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
4360
+ DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
4112
4361
  API_BASE = "https://askexe.com/cloud";
4113
4362
  RETRY_DELAY_MS = 500;
4114
4363
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
@@ -4132,18 +4381,20 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
4132
4381
  employeeLimit: 1,
4133
4382
  memoryLimit: 5e3
4134
4383
  };
4384
+ _prismaPromise = null;
4385
+ _prismaFailed = false;
4135
4386
  CACHE_MAX_AGE_MS = 36e5;
4136
4387
  _revalTimer = null;
4137
4388
  }
4138
4389
  });
4139
4390
 
4140
4391
  // src/lib/plan-limits.ts
4141
- import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
4142
- import path11 from "path";
4392
+ import { readFileSync as readFileSync9, existsSync as existsSync12 } from "fs";
4393
+ import path12 from "path";
4143
4394
  function getLicenseSync() {
4144
4395
  try {
4145
- if (!existsSync10(CACHE_PATH2)) return freeLicense();
4146
- const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
4396
+ if (!existsSync12(CACHE_PATH2)) return freeLicense();
4397
+ const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
4147
4398
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
4148
4399
  const parts = raw.token.split(".");
4149
4400
  if (parts.length !== 3) return freeLicense();
@@ -4181,8 +4432,8 @@ function assertEmployeeLimitSync(rosterPath) {
4181
4432
  const filePath = rosterPath ?? EMPLOYEES_PATH;
4182
4433
  let count = 0;
4183
4434
  try {
4184
- if (existsSync10(filePath)) {
4185
- const raw = readFileSync8(filePath, "utf8");
4435
+ if (existsSync12(filePath)) {
4436
+ const raw = readFileSync9(filePath, "utf8");
4186
4437
  const employees = JSON.parse(raw);
4187
4438
  count = Array.isArray(employees) ? employees.length : 0;
4188
4439
  }
@@ -4211,29 +4462,30 @@ var init_plan_limits = __esm({
4211
4462
  this.name = "PlanLimitError";
4212
4463
  }
4213
4464
  };
4214
- CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
4465
+ CACHE_PATH2 = path12.join(EXE_AI_DIR, "license-cache.json");
4215
4466
  }
4216
4467
  });
4217
4468
 
4218
4469
  // src/lib/notifications.ts
4219
- import crypto from "crypto";
4220
- import path12 from "path";
4221
- import os8 from "os";
4470
+ import crypto2 from "crypto";
4471
+ import path13 from "path";
4472
+ import os9 from "os";
4222
4473
  import {
4223
- readFileSync as readFileSync9,
4474
+ readFileSync as readFileSync10,
4224
4475
  readdirSync as readdirSync2,
4225
4476
  unlinkSync as unlinkSync3,
4226
- existsSync as existsSync11,
4477
+ existsSync as existsSync13,
4227
4478
  rmdirSync
4228
4479
  } from "fs";
4229
4480
  async function writeNotification(notification) {
4230
4481
  try {
4231
4482
  const client = getClient();
4232
- const id = crypto.randomUUID();
4483
+ const id = crypto2.randomUUID();
4233
4484
  const now = (/* @__PURE__ */ new Date()).toISOString();
4485
+ const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
4234
4486
  await client.execute({
4235
- sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
4236
- VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
4487
+ sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
4488
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`,
4237
4489
  args: [
4238
4490
  id,
4239
4491
  notification.agentId,
@@ -4242,6 +4494,7 @@ async function writeNotification(notification) {
4242
4494
  notification.project,
4243
4495
  notification.summary,
4244
4496
  notification.taskFile ?? null,
4497
+ sessionScope,
4245
4498
  now
4246
4499
  ]
4247
4500
  });
@@ -4250,21 +4503,22 @@ async function writeNotification(notification) {
4250
4503
  `);
4251
4504
  }
4252
4505
  }
4253
- async function readUnreadNotifications(agentFilter) {
4506
+ async function readUnreadNotifications(agentFilter, sessionScope) {
4254
4507
  try {
4255
4508
  const client = getClient();
4256
4509
  const conditions = ["read = 0"];
4257
4510
  const args = [];
4511
+ const scope = strictSessionScopeFilter(sessionScope);
4258
4512
  if (agentFilter) {
4259
4513
  conditions.push("agent_id = ?");
4260
4514
  args.push(agentFilter);
4261
4515
  }
4262
4516
  const result = await client.execute({
4263
- sql: `SELECT id, agent_id, agent_role, event, project, summary, task_file, created_at
4517
+ sql: `SELECT id, agent_id, agent_role, event, project, summary, task_file, session_scope, created_at
4264
4518
  FROM notifications
4265
- WHERE ${conditions.join(" AND ")}
4519
+ WHERE ${conditions.join(" AND ")}${scope.sql}
4266
4520
  ORDER BY created_at ASC`,
4267
- args
4521
+ args: [...args, ...scope.args]
4268
4522
  });
4269
4523
  return result.rows.map((r) => ({
4270
4524
  id: String(r.id),
@@ -4274,6 +4528,7 @@ async function readUnreadNotifications(agentFilter) {
4274
4528
  project: String(r.project),
4275
4529
  summary: String(r.summary),
4276
4530
  taskFile: r.task_file ? String(r.task_file) : void 0,
4531
+ sessionScope: r.session_scope == null ? null : String(r.session_scope),
4277
4532
  timestamp: String(r.created_at),
4278
4533
  read: false
4279
4534
  }));
@@ -4281,54 +4536,60 @@ async function readUnreadNotifications(agentFilter) {
4281
4536
  return [];
4282
4537
  }
4283
4538
  }
4284
- async function markAsRead(ids) {
4539
+ async function markAsRead(ids, sessionScope) {
4285
4540
  if (ids.length === 0) return;
4286
4541
  try {
4287
4542
  const client = getClient();
4288
4543
  const placeholders = ids.map(() => "?").join(", ");
4544
+ const scope = strictSessionScopeFilter(sessionScope);
4289
4545
  await client.execute({
4290
- sql: `UPDATE notifications SET read = 1 WHERE id IN (${placeholders})`,
4291
- args: ids
4546
+ sql: `UPDATE notifications SET read = 1 WHERE id IN (${placeholders})${scope.sql}`,
4547
+ args: [...ids, ...scope.args]
4292
4548
  });
4293
4549
  } catch {
4294
4550
  }
4295
4551
  }
4296
- async function markAsReadByTaskFile(taskFile) {
4552
+ async function markAsReadByTaskFile(taskFile, sessionScope) {
4297
4553
  try {
4298
4554
  const client = getClient();
4555
+ const scope = strictSessionScopeFilter(sessionScope);
4299
4556
  await client.execute({
4300
- sql: "UPDATE notifications SET read = 1 WHERE task_file = ? AND read = 0",
4301
- args: [taskFile]
4557
+ sql: `UPDATE notifications SET read = 1
4558
+ WHERE task_file = ? AND read = 0${scope.sql}`,
4559
+ args: [taskFile, ...scope.args]
4302
4560
  });
4303
4561
  } catch {
4304
4562
  }
4305
4563
  }
4306
- async function cleanupOldNotifications(daysOld = CLEANUP_DAYS) {
4564
+ async function cleanupOldNotifications(daysOld = CLEANUP_DAYS, sessionScope) {
4307
4565
  try {
4308
4566
  const client = getClient();
4309
4567
  const cutoff = new Date(
4310
4568
  Date.now() - daysOld * 24 * 60 * 60 * 1e3
4311
4569
  ).toISOString();
4570
+ const scope = strictSessionScopeFilter(sessionScope);
4312
4571
  const result = await client.execute({
4313
- sql: "DELETE FROM notifications WHERE created_at < ?",
4314
- args: [cutoff]
4572
+ sql: `DELETE FROM notifications WHERE created_at < ?${scope.sql}`,
4573
+ args: [cutoff, ...scope.args]
4315
4574
  });
4316
4575
  return result.rowsAffected;
4317
4576
  } catch {
4318
4577
  return 0;
4319
4578
  }
4320
4579
  }
4321
- async function markDoneTaskNotificationsAsRead() {
4580
+ async function markDoneTaskNotificationsAsRead(sessionScope) {
4322
4581
  try {
4323
4582
  const client = getClient();
4583
+ const scope = strictSessionScopeFilter(sessionScope);
4324
4584
  const result = await client.execute({
4325
4585
  sql: `UPDATE notifications SET read = 1
4326
4586
  WHERE read = 0
4327
4587
  AND task_file IS NOT NULL
4588
+ ${scope.sql}
4328
4589
  AND task_file IN (
4329
- SELECT task_file FROM tasks WHERE status = 'done'
4590
+ SELECT task_file FROM tasks WHERE status = 'done'${scope.sql}
4330
4591
  )`,
4331
- args: []
4592
+ args: [...scope.args, ...scope.args]
4332
4593
  });
4333
4594
  return result.rowsAffected;
4334
4595
  } catch {
@@ -4336,9 +4597,9 @@ async function markDoneTaskNotificationsAsRead() {
4336
4597
  }
4337
4598
  }
4338
4599
  async function migrateJsonNotifications() {
4339
- const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path12.join(os8.homedir(), ".exe-os");
4340
- const notifDir = path12.join(base, "notifications");
4341
- if (!existsSync11(notifDir)) return 0;
4600
+ const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path13.join(os9.homedir(), ".exe-os");
4601
+ const notifDir = path13.join(base, "notifications");
4602
+ if (!existsSync13(notifDir)) return 0;
4342
4603
  let migrated = 0;
4343
4604
  try {
4344
4605
  const files = readdirSync2(notifDir).filter((f) => f.endsWith(".json"));
@@ -4346,19 +4607,20 @@ async function migrateJsonNotifications() {
4346
4607
  const client = getClient();
4347
4608
  for (const file of files) {
4348
4609
  try {
4349
- const filePath = path12.join(notifDir, file);
4350
- const data = JSON.parse(readFileSync9(filePath, "utf8"));
4610
+ const filePath = path13.join(notifDir, file);
4611
+ const data = JSON.parse(readFileSync10(filePath, "utf8"));
4351
4612
  await client.execute({
4352
- sql: `INSERT OR IGNORE INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
4353
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
4613
+ sql: `INSERT OR IGNORE INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
4614
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
4354
4615
  args: [
4355
- crypto.randomUUID(),
4616
+ crypto2.randomUUID(),
4356
4617
  data.agentId ?? "unknown",
4357
4618
  data.agentRole ?? "unknown",
4358
4619
  data.event ?? "session_summary",
4359
4620
  data.project ?? "unknown",
4360
4621
  data.summary ?? "",
4361
4622
  data.taskFile ?? null,
4623
+ null,
4362
4624
  data.read ? 1 : 0,
4363
4625
  data.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
4364
4626
  ]
@@ -4384,6 +4646,7 @@ var init_notifications = __esm({
4384
4646
  "src/lib/notifications.ts"() {
4385
4647
  "use strict";
4386
4648
  init_database();
4649
+ init_task_scope();
4387
4650
  CLEANUP_DAYS = 7;
4388
4651
  }
4389
4652
  });
@@ -4401,7 +4664,7 @@ __export(session_kill_telemetry_exports, {
4401
4664
  recordSessionKill: () => recordSessionKill,
4402
4665
  sumTokensSavedSince: () => sumTokensSavedSince
4403
4666
  });
4404
- import crypto2 from "crypto";
4667
+ import crypto3 from "crypto";
4405
4668
  async function recordSessionKill(input) {
4406
4669
  try {
4407
4670
  const client = getClient();
@@ -4411,7 +4674,7 @@ async function recordSessionKill(input) {
4411
4674
  ticks_idle, estimated_tokens_saved)
4412
4675
  VALUES (?, ?, ?, ?, ?, ?, ?)`,
4413
4676
  args: [
4414
- crypto2.randomUUID(),
4677
+ crypto3.randomUUID(),
4415
4678
  input.sessionName,
4416
4679
  input.agentId,
4417
4680
  (/* @__PURE__ */ new Date()).toISOString(),
@@ -4493,12 +4756,12 @@ var init_session_kill_telemetry = __esm({
4493
4756
  });
4494
4757
 
4495
4758
  // src/lib/tasks-crud.ts
4496
- import crypto3 from "crypto";
4497
- import path13 from "path";
4498
- import os9 from "os";
4759
+ import crypto4 from "crypto";
4760
+ import path14 from "path";
4761
+ import os10 from "os";
4499
4762
  import { execSync as execSync5 } from "child_process";
4500
4763
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
4501
- import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
4764
+ import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
4502
4765
  async function writeCheckpoint(input) {
4503
4766
  const client = getClient();
4504
4767
  const row = await resolveTask(client, input.taskId);
@@ -4614,7 +4877,7 @@ async function resolveTask(client, identifier, scopeSession) {
4614
4877
  }
4615
4878
  async function createTaskCore(input) {
4616
4879
  const client = getClient();
4617
- const id = crypto3.randomUUID();
4880
+ const id = crypto4.randomUUID();
4618
4881
  const now = (/* @__PURE__ */ new Date()).toISOString();
4619
4882
  const slug = slugify(input.title);
4620
4883
  let earlySessionScope = null;
@@ -4673,8 +4936,8 @@ ${laneWarning}` : laneWarning;
4673
4936
  }
4674
4937
  if (input.baseDir) {
4675
4938
  try {
4676
- await mkdir4(path13.join(input.baseDir, "exe", "output"), { recursive: true });
4677
- await mkdir4(path13.join(input.baseDir, "exe", "research"), { recursive: true });
4939
+ await mkdir4(path14.join(input.baseDir, "exe", "output"), { recursive: true });
4940
+ await mkdir4(path14.join(input.baseDir, "exe", "research"), { recursive: true });
4678
4941
  await ensureArchitectureDoc(input.baseDir, input.projectName);
4679
4942
  await ensureGitignoreExe(input.baseDir);
4680
4943
  } catch {
@@ -4710,13 +4973,19 @@ ${laneWarning}` : laneWarning;
4710
4973
  });
4711
4974
  if (input.baseDir) {
4712
4975
  try {
4713
- const EXE_OS_DIR = path13.join(os9.homedir(), ".exe-os");
4714
- const mdPath = path13.join(EXE_OS_DIR, taskFile);
4715
- const mdDir = path13.dirname(mdPath);
4716
- if (!existsSync12(mdDir)) await mkdir4(mdDir, { recursive: true });
4976
+ const EXE_OS_DIR = path14.join(os10.homedir(), ".exe-os");
4977
+ const mdPath = path14.join(EXE_OS_DIR, taskFile);
4978
+ const mdDir = path14.dirname(mdPath);
4979
+ if (!existsSync14(mdDir)) await mkdir4(mdDir, { recursive: true });
4717
4980
  const reviewer = input.reviewer ?? input.assignedBy;
4718
4981
  const mdContent = `# ${input.title}
4719
4982
 
4983
+ ## MANDATORY: When done
4984
+
4985
+ You MUST call update_task with status "done" and a result summary when finished.
4986
+ If you skip this, your reviewer will not know you're done and your work won't be reviewed.
4987
+ Do NOT let a failed commit or any error prevent you from calling update_task(done).
4988
+
4720
4989
  **ID:** ${id}
4721
4990
  **Status:** ${initialStatus}
4722
4991
  **Priority:** ${input.priority}
@@ -4730,12 +4999,6 @@ ${laneWarning}` : laneWarning;
4730
4999
  ## Context
4731
5000
 
4732
5001
  ${input.context}
4733
-
4734
- ## MANDATORY: When done
4735
-
4736
- You MUST call update_task with status "done" and a result summary when finished.
4737
- If you skip this, your reviewer will not know you're done and your work won't be reviewed.
4738
- Do NOT let a failed commit or any error prevent you from calling update_task(done).
4739
5002
  `;
4740
5003
  await writeFile4(mdPath, mdContent, "utf-8");
4741
5004
  } catch (err) {
@@ -4984,7 +5247,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
4984
5247
  await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
4985
5248
  } catch {
4986
5249
  }
4987
- if (input.status === "done" || input.status === "cancelled") {
5250
+ if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
4988
5251
  try {
4989
5252
  const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
4990
5253
  clearQueueForAgent2(String(row.assigned_to));
@@ -5013,9 +5276,9 @@ async function deleteTaskCore(taskId, _baseDir) {
5013
5276
  return { taskFile, assignedTo, assignedBy, taskSlug };
5014
5277
  }
5015
5278
  async function ensureArchitectureDoc(baseDir, projectName) {
5016
- const archPath = path13.join(baseDir, "exe", "ARCHITECTURE.md");
5279
+ const archPath = path14.join(baseDir, "exe", "ARCHITECTURE.md");
5017
5280
  try {
5018
- if (existsSync12(archPath)) return;
5281
+ if (existsSync14(archPath)) return;
5019
5282
  const template = [
5020
5283
  `# ${projectName} \u2014 System Architecture`,
5021
5284
  "",
@@ -5048,10 +5311,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
5048
5311
  }
5049
5312
  }
5050
5313
  async function ensureGitignoreExe(baseDir) {
5051
- const gitignorePath = path13.join(baseDir, ".gitignore");
5314
+ const gitignorePath = path14.join(baseDir, ".gitignore");
5052
5315
  try {
5053
- if (existsSync12(gitignorePath)) {
5054
- const content = readFileSync10(gitignorePath, "utf-8");
5316
+ if (existsSync14(gitignorePath)) {
5317
+ const content = readFileSync11(gitignorePath, "utf-8");
5055
5318
  if (/^\/?exe\/?$/m.test(content)) return;
5056
5319
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
5057
5320
  } else {
@@ -5082,58 +5345,42 @@ var init_tasks_crud = __esm({
5082
5345
  });
5083
5346
 
5084
5347
  // src/lib/tasks-review.ts
5085
- import path14 from "path";
5086
- import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
5348
+ import path15 from "path";
5349
+ import { existsSync as existsSync15, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
5087
5350
  async function countPendingReviews(sessionScope) {
5088
5351
  const client = getClient();
5089
- if (sessionScope) {
5090
- const result2 = await client.execute({
5091
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
5092
- args: [sessionScope]
5093
- });
5094
- return Number(result2.rows[0]?.cnt) || 0;
5095
- }
5352
+ const scope = strictSessionScopeFilter(
5353
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
5354
+ );
5096
5355
  const result = await client.execute({
5097
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review'",
5098
- args: []
5356
+ sql: `SELECT COUNT(*) as cnt FROM tasks
5357
+ WHERE status = 'needs_review'${scope.sql}`,
5358
+ args: [...scope.args]
5099
5359
  });
5100
5360
  return Number(result.rows[0]?.cnt) || 0;
5101
5361
  }
5102
5362
  async function countNewPendingReviewsSince(sinceIso, sessionScope) {
5103
5363
  const client = getClient();
5104
- if (sessionScope) {
5105
- const result2 = await client.execute({
5106
- sql: `SELECT COUNT(*) as cnt FROM tasks
5107
- WHERE status = 'needs_review' AND updated_at > ?
5108
- AND session_scope = ?`,
5109
- args: [sinceIso, sessionScope]
5110
- });
5111
- return Number(result2.rows[0]?.cnt) || 0;
5112
- }
5364
+ const scope = strictSessionScopeFilter(
5365
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
5366
+ );
5113
5367
  const result = await client.execute({
5114
5368
  sql: `SELECT COUNT(*) as cnt FROM tasks
5115
- WHERE status = 'needs_review' AND updated_at > ?`,
5116
- args: [sinceIso]
5369
+ WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
5370
+ args: [sinceIso, ...scope.args]
5117
5371
  });
5118
5372
  return Number(result.rows[0]?.cnt) || 0;
5119
5373
  }
5120
5374
  async function listPendingReviews(limit, sessionScope) {
5121
5375
  const client = getClient();
5122
- if (sessionScope) {
5123
- const result2 = await client.execute({
5124
- sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
5125
- WHERE status = 'needs_review'
5126
- AND session_scope = ?
5127
- ORDER BY updated_at ASC LIMIT ?`,
5128
- args: [sessionScope, limit]
5129
- });
5130
- return result2.rows;
5131
- }
5376
+ const scope = strictSessionScopeFilter(
5377
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
5378
+ );
5132
5379
  const result = await client.execute({
5133
5380
  sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
5134
- WHERE status = 'needs_review'
5381
+ WHERE status = 'needs_review'${scope.sql}
5135
5382
  ORDER BY updated_at ASC LIMIT ?`,
5136
- args: [limit]
5383
+ args: [...scope.args, limit]
5137
5384
  });
5138
5385
  return result.rows;
5139
5386
  }
@@ -5145,7 +5392,7 @@ async function cleanupOrphanedReviews() {
5145
5392
  WHERE status IN ('open', 'needs_review', 'in_progress')
5146
5393
  AND assigned_by = 'system'
5147
5394
  AND title LIKE 'Review:%'
5148
- AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
5395
+ AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
5149
5396
  args: [now]
5150
5397
  });
5151
5398
  const r1b = await client.execute({
@@ -5264,11 +5511,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
5264
5511
  );
5265
5512
  }
5266
5513
  try {
5267
- const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
5268
- if (existsSync13(cacheDir)) {
5514
+ const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
5515
+ if (existsSync15(cacheDir)) {
5269
5516
  for (const f of readdirSync3(cacheDir)) {
5270
5517
  if (f.startsWith("review-notified-")) {
5271
- unlinkSync4(path14.join(cacheDir, f));
5518
+ unlinkSync4(path15.join(cacheDir, f));
5272
5519
  }
5273
5520
  }
5274
5521
  }
@@ -5285,11 +5532,12 @@ var init_tasks_review = __esm({
5285
5532
  init_tmux_routing();
5286
5533
  init_session_key();
5287
5534
  init_state_bus();
5535
+ init_task_scope();
5288
5536
  }
5289
5537
  });
5290
5538
 
5291
5539
  // src/lib/tasks-chain.ts
5292
- import path15 from "path";
5540
+ import path16 from "path";
5293
5541
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
5294
5542
  async function cascadeUnblock(taskId, baseDir, now) {
5295
5543
  const client = getClient();
@@ -5306,7 +5554,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
5306
5554
  });
5307
5555
  for (const ur of unblockedRows.rows) {
5308
5556
  try {
5309
- const ubFile = path15.join(baseDir, String(ur.task_file));
5557
+ const ubFile = path16.join(baseDir, String(ur.task_file));
5310
5558
  let ubContent = await readFile4(ubFile, "utf-8");
5311
5559
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
5312
5560
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -5341,7 +5589,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
5341
5589
  const scScope = sessionScopeFilter();
5342
5590
  const remaining = await client.execute({
5343
5591
  sql: `SELECT COUNT(*) as cnt FROM tasks
5344
- WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
5592
+ WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
5345
5593
  args: [parentTaskId, ...scScope.args]
5346
5594
  });
5347
5595
  const cnt = Number(remaining.rows[0]?.cnt ?? 1);
@@ -5380,7 +5628,7 @@ __export(project_name_exports, {
5380
5628
  getProjectName: () => getProjectName
5381
5629
  });
5382
5630
  import { execSync as execSync6 } from "child_process";
5383
- import path16 from "path";
5631
+ import path17 from "path";
5384
5632
  function getProjectName(cwd) {
5385
5633
  const dir = cwd ?? process.cwd();
5386
5634
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -5393,7 +5641,7 @@ function getProjectName(cwd) {
5393
5641
  timeout: 2e3,
5394
5642
  stdio: ["pipe", "pipe", "pipe"]
5395
5643
  }).trim();
5396
- repoRoot = path16.dirname(gitCommonDir);
5644
+ repoRoot = path17.dirname(gitCommonDir);
5397
5645
  } catch {
5398
5646
  repoRoot = execSync6("git rev-parse --show-toplevel", {
5399
5647
  cwd: dir,
@@ -5402,11 +5650,11 @@ function getProjectName(cwd) {
5402
5650
  stdio: ["pipe", "pipe", "pipe"]
5403
5651
  }).trim();
5404
5652
  }
5405
- _cached2 = path16.basename(repoRoot);
5653
+ _cached2 = path17.basename(repoRoot);
5406
5654
  _cachedCwd = dir;
5407
5655
  return _cached2;
5408
5656
  } catch {
5409
- _cached2 = path16.basename(dir);
5657
+ _cached2 = path17.basename(dir);
5410
5658
  _cachedCwd = dir;
5411
5659
  return _cached2;
5412
5660
  }
@@ -5553,10 +5801,10 @@ var init_tasks_notify = __esm({
5553
5801
  });
5554
5802
 
5555
5803
  // src/lib/behaviors.ts
5556
- import crypto4 from "crypto";
5804
+ import crypto5 from "crypto";
5557
5805
  async function storeBehavior(opts) {
5558
5806
  const client = getClient();
5559
- const id = crypto4.randomUUID();
5807
+ const id = crypto5.randomUUID();
5560
5808
  const now = (/* @__PURE__ */ new Date()).toISOString();
5561
5809
  await client.execute({
5562
5810
  sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
@@ -5585,7 +5833,7 @@ __export(skill_learning_exports, {
5585
5833
  storeTrajectory: () => storeTrajectory,
5586
5834
  sweepTrajectories: () => sweepTrajectories
5587
5835
  });
5588
- import crypto5 from "crypto";
5836
+ import crypto6 from "crypto";
5589
5837
  async function extractTrajectory(taskId, agentId) {
5590
5838
  const client = getClient();
5591
5839
  const result = await client.execute({
@@ -5614,11 +5862,11 @@ async function extractTrajectory(taskId, agentId) {
5614
5862
  return signature;
5615
5863
  }
5616
5864
  function hashSignature(signature) {
5617
- return crypto5.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
5865
+ return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
5618
5866
  }
5619
5867
  async function storeTrajectory(opts) {
5620
5868
  const client = getClient();
5621
- const id = crypto5.randomUUID();
5869
+ const id = crypto6.randomUUID();
5622
5870
  const now = (/* @__PURE__ */ new Date()).toISOString();
5623
5871
  const signatureHash = hashSignature(opts.signature);
5624
5872
  await client.execute({
@@ -5883,8 +6131,8 @@ __export(tasks_exports, {
5883
6131
  updateTaskStatus: () => updateTaskStatus,
5884
6132
  writeCheckpoint: () => writeCheckpoint
5885
6133
  });
5886
- import path17 from "path";
5887
- import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5 } from "fs";
6134
+ import path18 from "path";
6135
+ import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5 } from "fs";
5888
6136
  async function createTask(input) {
5889
6137
  const result = await createTaskCore(input);
5890
6138
  if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -5903,12 +6151,12 @@ async function updateTask(input) {
5903
6151
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
5904
6152
  try {
5905
6153
  const agent = String(row.assigned_to);
5906
- const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
5907
- const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
6154
+ const cacheDir = path18.join(EXE_AI_DIR, "session-cache");
6155
+ const cachePath = path18.join(cacheDir, `current-task-${agent}.json`);
5908
6156
  if (input.status === "in_progress") {
5909
6157
  mkdirSync6(cacheDir, { recursive: true });
5910
- writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
5911
- } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
6158
+ writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
6159
+ } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
5912
6160
  try {
5913
6161
  unlinkSync5(cachePath);
5914
6162
  } catch {
@@ -5916,10 +6164,10 @@ async function updateTask(input) {
5916
6164
  }
5917
6165
  } catch {
5918
6166
  }
5919
- if (input.status === "done") {
6167
+ if (input.status === "done" || input.status === "closed") {
5920
6168
  await cleanupReviewFile(row, taskFile, input.baseDir);
5921
6169
  }
5922
- if (input.status === "done" || input.status === "cancelled") {
6170
+ if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
5923
6171
  try {
5924
6172
  const client = getClient();
5925
6173
  const taskTitle = String(row.title);
@@ -5935,7 +6183,7 @@ async function updateTask(input) {
5935
6183
  if (!isCoordinatorName(assignedAgent)) {
5936
6184
  try {
5937
6185
  const draftClient = getClient();
5938
- if (input.status === "done") {
6186
+ if (input.status === "done" || input.status === "closed") {
5939
6187
  await draftClient.execute({
5940
6188
  sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
5941
6189
  args: [assignedAgent]
@@ -5952,7 +6200,7 @@ async function updateTask(input) {
5952
6200
  try {
5953
6201
  const client = getClient();
5954
6202
  const cascaded = await client.execute({
5955
- sql: `UPDATE tasks SET status = 'done', updated_at = ?
6203
+ sql: `UPDATE tasks SET status = 'closed', updated_at = ?
5956
6204
  WHERE parent_task_id = ? AND status = 'needs_review'`,
5957
6205
  args: [now, taskId]
5958
6206
  });
@@ -5965,14 +6213,14 @@ async function updateTask(input) {
5965
6213
  } catch {
5966
6214
  }
5967
6215
  }
5968
- const isTerminal = input.status === "done" || input.status === "needs_review";
6216
+ const isTerminal = input.status === "done" || input.status === "needs_review" || input.status === "closed";
5969
6217
  if (isTerminal) {
5970
6218
  const isCoordinator = isCoordinatorName(String(row.assigned_to));
5971
6219
  if (!isCoordinator) {
5972
6220
  notifyTaskDone();
5973
6221
  }
5974
6222
  await markTaskNotificationsRead(taskFile);
5975
- if (input.status === "done") {
6223
+ if (input.status === "done" || input.status === "closed") {
5976
6224
  try {
5977
6225
  await cascadeUnblock(taskId, input.baseDir, now);
5978
6226
  } catch {
@@ -5992,7 +6240,7 @@ async function updateTask(input) {
5992
6240
  }
5993
6241
  }
5994
6242
  }
5995
- if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
6243
+ if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
5996
6244
  Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
5997
6245
  ({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
5998
6246
  taskId,
@@ -6364,6 +6612,7 @@ __export(tmux_routing_exports, {
6364
6612
  isEmployeeAlive: () => isEmployeeAlive,
6365
6613
  isExeSession: () => isExeSession,
6366
6614
  isSessionBusy: () => isSessionBusy,
6615
+ notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
6367
6616
  notifyParentExe: () => notifyParentExe,
6368
6617
  parseParentExe: () => parseParentExe,
6369
6618
  registerParentExe: () => registerParentExe,
@@ -6374,13 +6623,13 @@ __export(tmux_routing_exports, {
6374
6623
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
6375
6624
  });
6376
6625
  import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
6377
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync4 } from "fs";
6378
- import path18 from "path";
6379
- import os10 from "os";
6626
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, existsSync as existsSync16, appendFileSync, readdirSync as readdirSync4 } from "fs";
6627
+ import path19 from "path";
6628
+ import os11 from "os";
6380
6629
  import { fileURLToPath as fileURLToPath2 } from "url";
6381
6630
  import { unlinkSync as unlinkSync6 } from "fs";
6382
6631
  function spawnLockPath(sessionName) {
6383
- return path18.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
6632
+ return path19.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
6384
6633
  }
6385
6634
  function isProcessAlive(pid) {
6386
6635
  try {
@@ -6391,13 +6640,13 @@ function isProcessAlive(pid) {
6391
6640
  }
6392
6641
  }
6393
6642
  function acquireSpawnLock2(sessionName) {
6394
- if (!existsSync14(SPAWN_LOCK_DIR)) {
6643
+ if (!existsSync16(SPAWN_LOCK_DIR)) {
6395
6644
  mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
6396
6645
  }
6397
6646
  const lockFile = spawnLockPath(sessionName);
6398
- if (existsSync14(lockFile)) {
6647
+ if (existsSync16(lockFile)) {
6399
6648
  try {
6400
- const lock = JSON.parse(readFileSync11(lockFile, "utf8"));
6649
+ const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
6401
6650
  const age = Date.now() - lock.timestamp;
6402
6651
  if (isProcessAlive(lock.pid) && age < 6e4) {
6403
6652
  return false;
@@ -6405,7 +6654,7 @@ function acquireSpawnLock2(sessionName) {
6405
6654
  } catch {
6406
6655
  }
6407
6656
  }
6408
- writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
6657
+ writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
6409
6658
  return true;
6410
6659
  }
6411
6660
  function releaseSpawnLock2(sessionName) {
@@ -6417,13 +6666,13 @@ function releaseSpawnLock2(sessionName) {
6417
6666
  function resolveBehaviorsExporterScript() {
6418
6667
  try {
6419
6668
  const thisFile = fileURLToPath2(import.meta.url);
6420
- const scriptPath = path18.join(
6421
- path18.dirname(thisFile),
6669
+ const scriptPath = path19.join(
6670
+ path19.dirname(thisFile),
6422
6671
  "..",
6423
6672
  "bin",
6424
6673
  "exe-export-behaviors.js"
6425
6674
  );
6426
- return existsSync14(scriptPath) ? scriptPath : null;
6675
+ return existsSync16(scriptPath) ? scriptPath : null;
6427
6676
  } catch {
6428
6677
  return null;
6429
6678
  }
@@ -6489,12 +6738,12 @@ function extractRootExe(name) {
6489
6738
  return parts.length > 0 ? parts[parts.length - 1] : null;
6490
6739
  }
6491
6740
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
6492
- if (!existsSync14(SESSION_CACHE)) {
6741
+ if (!existsSync16(SESSION_CACHE)) {
6493
6742
  mkdirSync7(SESSION_CACHE, { recursive: true });
6494
6743
  }
6495
6744
  const rootExe = extractRootExe(parentExe) ?? parentExe;
6496
- const filePath = path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
6497
- writeFileSync7(filePath, JSON.stringify({
6745
+ const filePath = path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
6746
+ writeFileSync8(filePath, JSON.stringify({
6498
6747
  parentExe: rootExe,
6499
6748
  dispatchedBy: dispatchedBy || rootExe,
6500
6749
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -6502,7 +6751,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
6502
6751
  }
6503
6752
  function getParentExe(sessionKey) {
6504
6753
  try {
6505
- const data = JSON.parse(readFileSync11(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
6754
+ const data = JSON.parse(readFileSync12(path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
6506
6755
  return data.parentExe || null;
6507
6756
  } catch {
6508
6757
  return null;
@@ -6510,8 +6759,8 @@ function getParentExe(sessionKey) {
6510
6759
  }
6511
6760
  function getDispatchedBy(sessionKey) {
6512
6761
  try {
6513
- const data = JSON.parse(readFileSync11(
6514
- path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
6762
+ const data = JSON.parse(readFileSync12(
6763
+ path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
6515
6764
  "utf8"
6516
6765
  ));
6517
6766
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -6581,8 +6830,8 @@ async function verifyPaneAtCapacity(sessionName) {
6581
6830
  }
6582
6831
  function readDebounceState() {
6583
6832
  try {
6584
- if (!existsSync14(DEBOUNCE_FILE)) return {};
6585
- const raw = JSON.parse(readFileSync11(DEBOUNCE_FILE, "utf8"));
6833
+ if (!existsSync16(DEBOUNCE_FILE)) return {};
6834
+ const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
6586
6835
  const state = {};
6587
6836
  for (const [key, val] of Object.entries(raw)) {
6588
6837
  if (typeof val === "number") {
@@ -6598,8 +6847,8 @@ function readDebounceState() {
6598
6847
  }
6599
6848
  function writeDebounceState(state) {
6600
6849
  try {
6601
- if (!existsSync14(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
6602
- writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
6850
+ if (!existsSync16(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
6851
+ writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
6603
6852
  } catch {
6604
6853
  }
6605
6854
  }
@@ -6697,8 +6946,8 @@ function sendIntercom(targetSession) {
6697
6946
  try {
6698
6947
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
6699
6948
  const agent = baseAgentName(rawAgent);
6700
- const markerPath = path18.join(SESSION_CACHE, `current-task-${agent}.json`);
6701
- if (existsSync14(markerPath)) {
6949
+ const markerPath = path19.join(SESSION_CACHE, `current-task-${agent}.json`);
6950
+ if (existsSync16(markerPath)) {
6702
6951
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
6703
6952
  return "debounced";
6704
6953
  }
@@ -6707,8 +6956,8 @@ function sendIntercom(targetSession) {
6707
6956
  try {
6708
6957
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
6709
6958
  const agent = baseAgentName(rawAgent);
6710
- const taskDir = path18.join(process.cwd(), "exe", agent);
6711
- if (existsSync14(taskDir)) {
6959
+ const taskDir = path19.join(process.cwd(), "exe", agent);
6960
+ if (existsSync16(taskDir)) {
6712
6961
  const files = readdirSync4(taskDir).filter(
6713
6962
  (f) => f.endsWith(".md") && f !== "DONE.txt"
6714
6963
  );
@@ -6768,6 +7017,21 @@ function notifyParentExe(sessionKey) {
6768
7017
  }
6769
7018
  return true;
6770
7019
  }
7020
+ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitle) {
7021
+ const transport = getTransport();
7022
+ try {
7023
+ const sessions = transport.listSessions();
7024
+ if (!sessions.includes(coordinatorSession)) return false;
7025
+ execSync7(
7026
+ `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
7027
+ { timeout: 3e3 }
7028
+ );
7029
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
7030
+ return true;
7031
+ } catch {
7032
+ return false;
7033
+ }
7034
+ }
6771
7035
  function ensureEmployee(employeeName, exeSession, projectDir, opts) {
6772
7036
  if (isCoordinatorName(employeeName)) {
6773
7037
  return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
@@ -6841,26 +7105,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6841
7105
  const transport = getTransport();
6842
7106
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
6843
7107
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
6844
- const logDir = path18.join(os10.homedir(), ".exe-os", "session-logs");
6845
- const logFile = path18.join(logDir, `${instanceLabel}-${Date.now()}.log`);
6846
- if (!existsSync14(logDir)) {
7108
+ const logDir = path19.join(os11.homedir(), ".exe-os", "session-logs");
7109
+ const logFile = path19.join(logDir, `${instanceLabel}-${Date.now()}.log`);
7110
+ if (!existsSync16(logDir)) {
6847
7111
  mkdirSync7(logDir, { recursive: true });
6848
7112
  }
6849
7113
  transport.kill(sessionName);
6850
7114
  let cleanupSuffix = "";
6851
7115
  try {
6852
7116
  const thisFile = fileURLToPath2(import.meta.url);
6853
- const cleanupScript = path18.join(path18.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
6854
- if (existsSync14(cleanupScript)) {
7117
+ const cleanupScript = path19.join(path19.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
7118
+ if (existsSync16(cleanupScript)) {
6855
7119
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
6856
7120
  }
6857
7121
  } catch {
6858
7122
  }
6859
7123
  try {
6860
- const claudeJsonPath = path18.join(os10.homedir(), ".claude.json");
7124
+ const claudeJsonPath = path19.join(os11.homedir(), ".claude.json");
6861
7125
  let claudeJson = {};
6862
7126
  try {
6863
- claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
7127
+ claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
6864
7128
  } catch {
6865
7129
  }
6866
7130
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -6868,17 +7132,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6868
7132
  const trustDir = opts?.cwd ?? projectDir;
6869
7133
  if (!projects[trustDir]) projects[trustDir] = {};
6870
7134
  projects[trustDir].hasTrustDialogAccepted = true;
6871
- writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
7135
+ writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
6872
7136
  } catch {
6873
7137
  }
6874
7138
  try {
6875
- const settingsDir = path18.join(os10.homedir(), ".claude", "projects");
7139
+ const settingsDir = path19.join(os11.homedir(), ".claude", "projects");
6876
7140
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
6877
- const projSettingsDir = path18.join(settingsDir, normalizedKey);
6878
- const settingsPath = path18.join(projSettingsDir, "settings.json");
7141
+ const projSettingsDir = path19.join(settingsDir, normalizedKey);
7142
+ const settingsPath = path19.join(projSettingsDir, "settings.json");
6879
7143
  let settings = {};
6880
7144
  try {
6881
- settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
7145
+ settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
6882
7146
  } catch {
6883
7147
  }
6884
7148
  const perms = settings.permissions ?? {};
@@ -6907,7 +7171,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6907
7171
  perms.allow = allow;
6908
7172
  settings.permissions = perms;
6909
7173
  mkdirSync7(projSettingsDir, { recursive: true });
6910
- writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
7174
+ writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
6911
7175
  }
6912
7176
  } catch {
6913
7177
  }
@@ -6922,8 +7186,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6922
7186
  let behaviorsFlag = "";
6923
7187
  let legacyFallbackWarned = false;
6924
7188
  if (!useExeAgent && !useBinSymlink) {
6925
- const identityPath = path18.join(
6926
- os10.homedir(),
7189
+ const identityPath = path19.join(
7190
+ os11.homedir(),
6927
7191
  ".exe-os",
6928
7192
  "identity",
6929
7193
  `${employeeName}.md`
@@ -6932,13 +7196,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6932
7196
  const hasAgentFlag = claudeSupportsAgentFlag();
6933
7197
  if (hasAgentFlag) {
6934
7198
  identityFlag = ` --agent ${employeeName}`;
6935
- } else if (existsSync14(identityPath)) {
7199
+ } else if (existsSync16(identityPath)) {
6936
7200
  identityFlag = ` --append-system-prompt-file ${identityPath}`;
6937
7201
  legacyFallbackWarned = true;
6938
7202
  }
6939
7203
  const behaviorsFile = exportBehaviorsSync(
6940
7204
  employeeName,
6941
- path18.basename(spawnCwd),
7205
+ path19.basename(spawnCwd),
6942
7206
  sessionName
6943
7207
  );
6944
7208
  if (behaviorsFile) {
@@ -6953,16 +7217,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6953
7217
  }
6954
7218
  let sessionContextFlag = "";
6955
7219
  try {
6956
- const ctxDir = path18.join(os10.homedir(), ".exe-os", "session-cache");
7220
+ const ctxDir = path19.join(os11.homedir(), ".exe-os", "session-cache");
6957
7221
  mkdirSync7(ctxDir, { recursive: true });
6958
- const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
7222
+ const ctxFile = path19.join(ctxDir, `session-context-${sessionName}.md`);
6959
7223
  const ctxContent = [
6960
7224
  `## Session Context`,
6961
7225
  `You are running in tmux session: ${sessionName}.`,
6962
7226
  `Your parent coordinator session is ${exeSession}.`,
6963
7227
  `Your employees (if any) use the -${exeSession} suffix.`
6964
7228
  ].join("\n");
6965
- writeFileSync7(ctxFile, ctxContent);
7229
+ writeFileSync8(ctxFile, ctxContent);
6966
7230
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
6967
7231
  } catch {
6968
7232
  }
@@ -7039,8 +7303,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7039
7303
  transport.pipeLog(sessionName, logFile);
7040
7304
  try {
7041
7305
  const mySession = getMySession();
7042
- const dispatchInfo = path18.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
7043
- writeFileSync7(dispatchInfo, JSON.stringify({
7306
+ const dispatchInfo = path19.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
7307
+ writeFileSync8(dispatchInfo, JSON.stringify({
7044
7308
  dispatchedBy: mySession,
7045
7309
  rootExe: exeSession,
7046
7310
  provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
@@ -7114,15 +7378,15 @@ var init_tmux_routing = __esm({
7114
7378
  init_intercom_queue();
7115
7379
  init_plan_limits();
7116
7380
  init_employees();
7117
- SPAWN_LOCK_DIR = path18.join(os10.homedir(), ".exe-os", "spawn-locks");
7118
- SESSION_CACHE = path18.join(os10.homedir(), ".exe-os", "session-cache");
7381
+ SPAWN_LOCK_DIR = path19.join(os11.homedir(), ".exe-os", "spawn-locks");
7382
+ SESSION_CACHE = path19.join(os11.homedir(), ".exe-os", "session-cache");
7119
7383
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
7120
7384
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
7121
7385
  VERIFY_PANE_LINES = 200;
7122
7386
  INTERCOM_DEBOUNCE_MS = 3e4;
7123
7387
  CODEX_DEBOUNCE_MS = 12e4;
7124
- INTERCOM_LOG2 = path18.join(os10.homedir(), ".exe-os", "intercom.log");
7125
- DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
7388
+ INTERCOM_LOG2 = path19.join(os11.homedir(), ".exe-os", "intercom.log");
7389
+ DEBOUNCE_FILE = path19.join(SESSION_CACHE, "intercom-debounce.json");
7126
7390
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
7127
7391
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
7128
7392
  }
@@ -7145,6 +7409,15 @@ function sessionScopeFilter(sessionScope, tableAlias) {
7145
7409
  args: [scope]
7146
7410
  };
7147
7411
  }
7412
+ function strictSessionScopeFilter(sessionScope, tableAlias) {
7413
+ const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
7414
+ if (!scope) return { sql: "", args: [] };
7415
+ const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
7416
+ return {
7417
+ sql: ` AND ${col} = ?`,
7418
+ args: [scope]
7419
+ };
7420
+ }
7148
7421
  var init_task_scope = __esm({
7149
7422
  "src/lib/task-scope.ts"() {
7150
7423
  "use strict";
@@ -7179,14 +7452,14 @@ __export(worker_gate_exports, {
7179
7452
  tryAcquireBackfillLock: () => tryAcquireBackfillLock,
7180
7453
  tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
7181
7454
  });
7182
- import { readdirSync as readdirSync6, writeFileSync as writeFileSync9, unlinkSync as unlinkSync8, mkdirSync as mkdirSync9, existsSync as existsSync15 } from "fs";
7183
- import path20 from "path";
7455
+ import { readdirSync as readdirSync6, writeFileSync as writeFileSync10, unlinkSync as unlinkSync8, mkdirSync as mkdirSync9, existsSync as existsSync17 } from "fs";
7456
+ import path21 from "path";
7184
7457
  function tryAcquireWorkerSlot() {
7185
7458
  try {
7186
7459
  mkdirSync9(WORKER_PID_DIR, { recursive: true });
7187
7460
  const reservationId = `res-${process.pid}-${Date.now()}`;
7188
- const reservationPath = path20.join(WORKER_PID_DIR, `${reservationId}.pid`);
7189
- writeFileSync9(reservationPath, String(process.pid));
7461
+ const reservationPath = path21.join(WORKER_PID_DIR, `${reservationId}.pid`);
7462
+ writeFileSync10(reservationPath, String(process.pid));
7190
7463
  const files = readdirSync6(WORKER_PID_DIR);
7191
7464
  let alive = 0;
7192
7465
  for (const f of files) {
@@ -7203,7 +7476,7 @@ function tryAcquireWorkerSlot() {
7203
7476
  alive++;
7204
7477
  } catch {
7205
7478
  try {
7206
- unlinkSync8(path20.join(WORKER_PID_DIR, f));
7479
+ unlinkSync8(path21.join(WORKER_PID_DIR, f));
7207
7480
  } catch {
7208
7481
  }
7209
7482
  }
@@ -7227,20 +7500,20 @@ function tryAcquireWorkerSlot() {
7227
7500
  function registerWorkerPid(pid) {
7228
7501
  try {
7229
7502
  mkdirSync9(WORKER_PID_DIR, { recursive: true });
7230
- writeFileSync9(path20.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
7503
+ writeFileSync10(path21.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
7231
7504
  } catch {
7232
7505
  }
7233
7506
  }
7234
7507
  function cleanupWorkerPid() {
7235
7508
  try {
7236
- unlinkSync8(path20.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
7509
+ unlinkSync8(path21.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
7237
7510
  } catch {
7238
7511
  }
7239
7512
  }
7240
7513
  function tryAcquireBackfillLock() {
7241
7514
  try {
7242
7515
  mkdirSync9(WORKER_PID_DIR, { recursive: true });
7243
- if (existsSync15(BACKFILL_LOCK)) {
7516
+ if (existsSync17(BACKFILL_LOCK)) {
7244
7517
  try {
7245
7518
  const pid = parseInt(
7246
7519
  __require("fs").readFileSync(BACKFILL_LOCK, "utf8").trim(),
@@ -7256,7 +7529,7 @@ function tryAcquireBackfillLock() {
7256
7529
  } catch {
7257
7530
  }
7258
7531
  }
7259
- writeFileSync9(BACKFILL_LOCK, String(process.pid));
7532
+ writeFileSync10(BACKFILL_LOCK, String(process.pid));
7260
7533
  return true;
7261
7534
  } catch {
7262
7535
  return true;
@@ -7273,9 +7546,9 @@ var init_worker_gate = __esm({
7273
7546
  "src/lib/worker-gate.ts"() {
7274
7547
  "use strict";
7275
7548
  init_config();
7276
- WORKER_PID_DIR = path20.join(EXE_AI_DIR, "worker-pids");
7549
+ WORKER_PID_DIR = path21.join(EXE_AI_DIR, "worker-pids");
7277
7550
  MAX_CONCURRENT_WORKERS = 3;
7278
- BACKFILL_LOCK = path20.join(WORKER_PID_DIR, "backfill.lock");
7551
+ BACKFILL_LOCK = path21.join(WORKER_PID_DIR, "backfill.lock");
7279
7552
  }
7280
7553
  });
7281
7554
 
@@ -7287,13 +7560,13 @@ __export(crypto_exports, {
7287
7560
  initSyncCrypto: () => initSyncCrypto,
7288
7561
  isSyncCryptoInitialized: () => isSyncCryptoInitialized
7289
7562
  });
7290
- import crypto6 from "crypto";
7563
+ import crypto7 from "crypto";
7291
7564
  function initSyncCrypto(masterKey) {
7292
7565
  if (masterKey.length !== 32) {
7293
7566
  throw new Error(`Master key must be 32 bytes, got ${masterKey.length}`);
7294
7567
  }
7295
7568
  _syncKey = Buffer.from(
7296
- crypto6.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
7569
+ crypto7.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
7297
7570
  );
7298
7571
  }
7299
7572
  function isSyncCryptoInitialized() {
@@ -7307,8 +7580,8 @@ function requireSyncKey() {
7307
7580
  }
7308
7581
  function encryptSyncBlob(data) {
7309
7582
  const key = requireSyncKey();
7310
- const iv = crypto6.randomBytes(IV_LENGTH);
7311
- const cipher = crypto6.createCipheriv(ALGORITHM, key, iv);
7583
+ const iv = crypto7.randomBytes(IV_LENGTH);
7584
+ const cipher = crypto7.createCipheriv(ALGORITHM, key, iv);
7312
7585
  const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
7313
7586
  const tag = cipher.getAuthTag();
7314
7587
  return Buffer.concat([iv, encrypted, tag]).toString("base64");
@@ -7322,7 +7595,7 @@ function decryptSyncBlob(ciphertext) {
7322
7595
  const iv = combined.subarray(0, IV_LENGTH);
7323
7596
  const tag = combined.subarray(combined.length - TAG_LENGTH);
7324
7597
  const encrypted = combined.subarray(IV_LENGTH, combined.length - TAG_LENGTH);
7325
- const decipher = crypto6.createDecipheriv(ALGORITHM, key, iv);
7598
+ const decipher = crypto7.createDecipheriv(ALGORITHM, key, iv);
7326
7599
  decipher.setAuthTag(tag);
7327
7600
  return Buffer.concat([decipher.update(encrypted), decipher.final()]);
7328
7601
  }
@@ -7377,8 +7650,8 @@ __export(crdt_sync_exports, {
7377
7650
  rebuildFromDb: () => rebuildFromDb
7378
7651
  });
7379
7652
  import * as Y from "yjs";
7380
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, existsSync as existsSync16, mkdirSync as mkdirSync10, unlinkSync as unlinkSync9 } from "fs";
7381
- import path21 from "path";
7653
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync11, existsSync as existsSync18, mkdirSync as mkdirSync10, unlinkSync as unlinkSync9 } from "fs";
7654
+ import path22 from "path";
7382
7655
  import { homedir } from "os";
7383
7656
  function getStatePath() {
7384
7657
  return _statePathOverride ?? DEFAULT_STATE_PATH;
@@ -7390,9 +7663,9 @@ function initCrdtDoc() {
7390
7663
  if (doc) return doc;
7391
7664
  doc = new Y.Doc();
7392
7665
  const sp = getStatePath();
7393
- if (existsSync16(sp)) {
7666
+ if (existsSync18(sp)) {
7394
7667
  try {
7395
- const state = readFileSync13(sp);
7668
+ const state = readFileSync14(sp);
7396
7669
  Y.applyUpdate(doc, new Uint8Array(state));
7397
7670
  } catch {
7398
7671
  console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
@@ -7534,10 +7807,10 @@ function persistState() {
7534
7807
  if (!doc) return;
7535
7808
  try {
7536
7809
  const sp = getStatePath();
7537
- const dir = path21.dirname(sp);
7538
- if (!existsSync16(dir)) mkdirSync10(dir, { recursive: true });
7810
+ const dir = path22.dirname(sp);
7811
+ if (!existsSync18(dir)) mkdirSync10(dir, { recursive: true });
7539
7812
  const state = Y.encodeStateAsUpdate(doc);
7540
- writeFileSync10(sp, Buffer.from(state));
7813
+ writeFileSync11(sp, Buffer.from(state));
7541
7814
  } catch {
7542
7815
  }
7543
7816
  }
@@ -7578,7 +7851,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
7578
7851
  var init_crdt_sync = __esm({
7579
7852
  "src/lib/crdt-sync.ts"() {
7580
7853
  "use strict";
7581
- DEFAULT_STATE_PATH = path21.join(homedir(), ".exe-os", "crdt-state.bin");
7854
+ DEFAULT_STATE_PATH = path22.join(homedir(), ".exe-os", "crdt-state.bin");
7582
7855
  _statePathOverride = null;
7583
7856
  doc = null;
7584
7857
  }
@@ -7610,39 +7883,107 @@ __export(cloud_sync_exports, {
7610
7883
  cloudSync: () => cloudSync,
7611
7884
  mergeConfig: () => mergeConfig,
7612
7885
  mergeRosterFromRemote: () => mergeRosterFromRemote,
7886
+ pushToPostgres: () => pushToPostgres,
7613
7887
  recordRosterDeletion: () => recordRosterDeletion
7614
7888
  });
7615
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync11, existsSync as existsSync17, readdirSync as readdirSync7, mkdirSync as mkdirSync11, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
7616
- import crypto7 from "crypto";
7617
- import path22 from "path";
7889
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync12, existsSync as existsSync19, readdirSync as readdirSync7, mkdirSync as mkdirSync11, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
7890
+ import crypto8 from "crypto";
7891
+ import path23 from "path";
7618
7892
  import { homedir as homedir2 } from "os";
7619
7893
  function sqlSafe(v) {
7620
7894
  return v === void 0 ? null : v;
7621
7895
  }
7622
7896
  function logError(msg) {
7623
7897
  try {
7624
- const logPath = path22.join(homedir2(), ".exe-os", "workers.log");
7898
+ const logPath = path23.join(homedir2(), ".exe-os", "workers.log");
7625
7899
  appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
7626
7900
  `);
7627
7901
  } catch {
7628
7902
  }
7629
7903
  }
7904
+ function loadPgClient() {
7905
+ if (_pgFailed) return null;
7906
+ const postgresUrl = process.env.DATABASE_URL;
7907
+ const configPath = path23.join(EXE_AI_DIR, "config.json");
7908
+ let cloudPostgresUrl;
7909
+ try {
7910
+ if (existsSync19(configPath)) {
7911
+ const cfg = JSON.parse(readFileSync15(configPath, "utf8"));
7912
+ cloudPostgresUrl = cfg.cloud?.postgresUrl;
7913
+ if (cfg.cloud?.syncToPostgres === false) {
7914
+ _pgFailed = true;
7915
+ return null;
7916
+ }
7917
+ }
7918
+ } catch {
7919
+ }
7920
+ const url = postgresUrl || cloudPostgresUrl;
7921
+ if (!url) {
7922
+ _pgFailed = true;
7923
+ return null;
7924
+ }
7925
+ if (!_pgPromise) {
7926
+ _pgPromise = (async () => {
7927
+ const { createRequire: createRequire3 } = await import("module");
7928
+ const { pathToFileURL: pathToFileURL3 } = await import("url");
7929
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path23.join(homedir2(), "exe-db");
7930
+ const req = createRequire3(path23.join(exeDbRoot, "package.json"));
7931
+ const entry = req.resolve("@prisma/client");
7932
+ const mod = await import(pathToFileURL3(entry).href);
7933
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
7934
+ if (!Ctor) throw new Error("No PrismaClient");
7935
+ return new Ctor();
7936
+ })().catch(() => {
7937
+ _pgFailed = true;
7938
+ _pgPromise = null;
7939
+ throw new Error("pg_unavailable");
7940
+ });
7941
+ }
7942
+ return _pgPromise;
7943
+ }
7944
+ async function pushToPostgres(records) {
7945
+ const loader = loadPgClient();
7946
+ if (!loader) return 0;
7947
+ let prisma;
7948
+ try {
7949
+ prisma = await loader;
7950
+ } catch {
7951
+ return 0;
7952
+ }
7953
+ let inserted = 0;
7954
+ for (const rec of records) {
7955
+ try {
7956
+ await prisma.$executeRawUnsafe(
7957
+ `INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
7958
+ VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
7959
+ ON CONFLICT (source, source_id, event_type) DO NOTHING`,
7960
+ String(rec.id ?? ""),
7961
+ JSON.stringify(rec),
7962
+ JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
7963
+ rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
7964
+ );
7965
+ inserted++;
7966
+ } catch {
7967
+ }
7968
+ }
7969
+ return inserted;
7970
+ }
7630
7971
  async function withRosterLock(fn) {
7631
7972
  try {
7632
7973
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
7633
7974
  closeSync2(fd);
7634
- writeFileSync11(ROSTER_LOCK_PATH, String(Date.now()));
7975
+ writeFileSync12(ROSTER_LOCK_PATH, String(Date.now()));
7635
7976
  } catch (err) {
7636
7977
  if (err.code === "EEXIST") {
7637
7978
  try {
7638
- const ts = parseInt(readFileSync14(ROSTER_LOCK_PATH, "utf-8"), 10);
7979
+ const ts = parseInt(readFileSync15(ROSTER_LOCK_PATH, "utf-8"), 10);
7639
7980
  if (Date.now() - ts < LOCK_STALE_MS) {
7640
7981
  throw new Error("Roster merge already in progress \u2014 another sync is running");
7641
7982
  }
7642
7983
  unlinkSync10(ROSTER_LOCK_PATH);
7643
7984
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
7644
7985
  closeSync2(fd);
7645
- writeFileSync11(ROSTER_LOCK_PATH, String(Date.now()));
7986
+ writeFileSync12(ROSTER_LOCK_PATH, String(Date.now()));
7646
7987
  } catch (retryErr) {
7647
7988
  if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
7648
7989
  throw new Error("Roster merge already in progress \u2014 another sync is running");
@@ -7912,6 +8253,10 @@ async function cloudSync(config) {
7912
8253
  const maxVersion = Number(records[records.length - 1].version);
7913
8254
  const pushOk = await cloudPush(records, maxVersion, config);
7914
8255
  if (!pushOk) break;
8256
+ try {
8257
+ await pushToPostgres(records);
8258
+ } catch {
8259
+ }
7915
8260
  await client.execute({
7916
8261
  sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
7917
8262
  args: [String(maxVersion)]
@@ -8016,8 +8361,8 @@ async function cloudSync(config) {
8016
8361
  try {
8017
8362
  const employees = await loadEmployees();
8018
8363
  rosterResult.employees = employees.length;
8019
- const idDir = path22.join(EXE_AI_DIR, "identity");
8020
- if (existsSync17(idDir)) {
8364
+ const idDir = path23.join(EXE_AI_DIR, "identity");
8365
+ if (existsSync19(idDir)) {
8021
8366
  rosterResult.identities = readdirSync7(idDir).filter((f) => f.endsWith(".md")).length;
8022
8367
  }
8023
8368
  } catch {
@@ -8038,62 +8383,62 @@ async function cloudSync(config) {
8038
8383
  function recordRosterDeletion(name) {
8039
8384
  let deletions = [];
8040
8385
  try {
8041
- if (existsSync17(ROSTER_DELETIONS_PATH)) {
8042
- deletions = JSON.parse(readFileSync14(ROSTER_DELETIONS_PATH, "utf-8"));
8386
+ if (existsSync19(ROSTER_DELETIONS_PATH)) {
8387
+ deletions = JSON.parse(readFileSync15(ROSTER_DELETIONS_PATH, "utf-8"));
8043
8388
  }
8044
8389
  } catch {
8045
8390
  }
8046
8391
  if (!deletions.includes(name)) deletions.push(name);
8047
- writeFileSync11(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
8392
+ writeFileSync12(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
8048
8393
  }
8049
8394
  function consumeRosterDeletions() {
8050
8395
  try {
8051
- if (!existsSync17(ROSTER_DELETIONS_PATH)) return [];
8052
- const deletions = JSON.parse(readFileSync14(ROSTER_DELETIONS_PATH, "utf-8"));
8053
- writeFileSync11(ROSTER_DELETIONS_PATH, "[]");
8396
+ if (!existsSync19(ROSTER_DELETIONS_PATH)) return [];
8397
+ const deletions = JSON.parse(readFileSync15(ROSTER_DELETIONS_PATH, "utf-8"));
8398
+ writeFileSync12(ROSTER_DELETIONS_PATH, "[]");
8054
8399
  return deletions;
8055
8400
  } catch {
8056
8401
  return [];
8057
8402
  }
8058
8403
  }
8059
8404
  function buildRosterBlob(paths) {
8060
- const rosterPath = paths?.rosterPath ?? path22.join(EXE_AI_DIR, "exe-employees.json");
8061
- const identityDir = paths?.identityDir ?? path22.join(EXE_AI_DIR, "identity");
8062
- const configPath = paths?.configPath ?? path22.join(EXE_AI_DIR, "config.json");
8405
+ const rosterPath = paths?.rosterPath ?? path23.join(EXE_AI_DIR, "exe-employees.json");
8406
+ const identityDir = paths?.identityDir ?? path23.join(EXE_AI_DIR, "identity");
8407
+ const configPath = paths?.configPath ?? path23.join(EXE_AI_DIR, "config.json");
8063
8408
  let roster = [];
8064
- if (existsSync17(rosterPath)) {
8409
+ if (existsSync19(rosterPath)) {
8065
8410
  try {
8066
- roster = JSON.parse(readFileSync14(rosterPath, "utf-8"));
8411
+ roster = JSON.parse(readFileSync15(rosterPath, "utf-8"));
8067
8412
  } catch {
8068
8413
  }
8069
8414
  }
8070
8415
  const identities = {};
8071
- if (existsSync17(identityDir)) {
8416
+ if (existsSync19(identityDir)) {
8072
8417
  for (const file of readdirSync7(identityDir).filter((f) => f.endsWith(".md"))) {
8073
8418
  try {
8074
- identities[file] = readFileSync14(path22.join(identityDir, file), "utf-8");
8419
+ identities[file] = readFileSync15(path23.join(identityDir, file), "utf-8");
8075
8420
  } catch {
8076
8421
  }
8077
8422
  }
8078
8423
  }
8079
8424
  let config;
8080
- if (existsSync17(configPath)) {
8425
+ if (existsSync19(configPath)) {
8081
8426
  try {
8082
- config = JSON.parse(readFileSync14(configPath, "utf-8"));
8427
+ config = JSON.parse(readFileSync15(configPath, "utf-8"));
8083
8428
  } catch {
8084
8429
  }
8085
8430
  }
8086
8431
  let agentConfig;
8087
- const agentConfigPath = path22.join(EXE_AI_DIR, "agent-config.json");
8088
- if (existsSync17(agentConfigPath)) {
8432
+ const agentConfigPath = path23.join(EXE_AI_DIR, "agent-config.json");
8433
+ if (existsSync19(agentConfigPath)) {
8089
8434
  try {
8090
- agentConfig = JSON.parse(readFileSync14(agentConfigPath, "utf-8"));
8435
+ agentConfig = JSON.parse(readFileSync15(agentConfigPath, "utf-8"));
8091
8436
  } catch {
8092
8437
  }
8093
8438
  }
8094
8439
  const deletedNames = consumeRosterDeletions();
8095
8440
  const content = JSON.stringify({ roster, identities, config, agentConfig, deletedNames });
8096
- const hash = crypto7.createHash("sha256").update(content).digest("hex").slice(0, 16);
8441
+ const hash = crypto8.createHash("sha256").update(content).digest("hex").slice(0, 16);
8097
8442
  return { roster, identities, config, agentConfig, deletedNames, version: hash };
8098
8443
  }
8099
8444
  async function cloudPushRoster(config) {
@@ -8163,23 +8508,24 @@ async function cloudPullRoster(config) {
8163
8508
  }
8164
8509
  }
8165
8510
  function mergeConfig(remoteConfig, configPath) {
8166
- const cfgPath = configPath ?? path22.join(EXE_AI_DIR, "config.json");
8511
+ const cfgPath = configPath ?? path23.join(EXE_AI_DIR, "config.json");
8167
8512
  let local = {};
8168
- if (existsSync17(cfgPath)) {
8513
+ if (existsSync19(cfgPath)) {
8169
8514
  try {
8170
- local = JSON.parse(readFileSync14(cfgPath, "utf-8"));
8515
+ local = JSON.parse(readFileSync15(cfgPath, "utf-8"));
8171
8516
  } catch {
8172
8517
  }
8173
8518
  }
8174
8519
  const merged = { ...remoteConfig, ...local };
8175
- const dir = path22.dirname(cfgPath);
8176
- if (!existsSync17(dir)) mkdirSync11(dir, { recursive: true });
8177
- writeFileSync11(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
8520
+ const dir = path23.dirname(cfgPath);
8521
+ ensurePrivateDirSync(dir);
8522
+ writeFileSync12(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
8523
+ enforcePrivateFileSync(cfgPath);
8178
8524
  }
8179
8525
  async function mergeRosterFromRemote(remote, paths) {
8180
8526
  return withRosterLock(async () => {
8181
8527
  const rosterPath = paths?.rosterPath ?? void 0;
8182
- const identityDir = paths?.identityDir ?? path22.join(EXE_AI_DIR, "identity");
8528
+ const identityDir = paths?.identityDir ?? path23.join(EXE_AI_DIR, "identity");
8183
8529
  const localEmployees = await loadEmployees(rosterPath);
8184
8530
  const localNames = new Set(localEmployees.map((e) => e.name));
8185
8531
  let added = 0;
@@ -8200,15 +8546,15 @@ async function mergeRosterFromRemote(remote, paths) {
8200
8546
  ) ?? lookupKey;
8201
8547
  const remoteIdentity = remote.identities[matchedKey];
8202
8548
  if (remoteIdentity) {
8203
- if (!existsSync17(identityDir)) mkdirSync11(identityDir, { recursive: true });
8204
- const idPath = path22.join(identityDir, `${remoteEmp.name}.md`);
8549
+ if (!existsSync19(identityDir)) mkdirSync11(identityDir, { recursive: true });
8550
+ const idPath = path23.join(identityDir, `${remoteEmp.name}.md`);
8205
8551
  let localIdentity = null;
8206
8552
  try {
8207
- localIdentity = existsSync17(idPath) ? readFileSync14(idPath, "utf-8") : null;
8553
+ localIdentity = existsSync19(idPath) ? readFileSync15(idPath, "utf-8") : null;
8208
8554
  } catch {
8209
8555
  }
8210
8556
  if (localIdentity !== remoteIdentity) {
8211
- writeFileSync11(idPath, remoteIdentity, "utf-8");
8557
+ writeFileSync12(idPath, remoteIdentity, "utf-8");
8212
8558
  identitiesUpdated++;
8213
8559
  }
8214
8560
  }
@@ -8234,16 +8580,18 @@ async function mergeRosterFromRemote(remote, paths) {
8234
8580
  }
8235
8581
  if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
8236
8582
  try {
8237
- const agentConfigPath = path22.join(EXE_AI_DIR, "agent-config.json");
8583
+ const agentConfigPath = path23.join(EXE_AI_DIR, "agent-config.json");
8238
8584
  let local = {};
8239
- if (existsSync17(agentConfigPath)) {
8585
+ if (existsSync19(agentConfigPath)) {
8240
8586
  try {
8241
- local = JSON.parse(readFileSync14(agentConfigPath, "utf-8"));
8587
+ local = JSON.parse(readFileSync15(agentConfigPath, "utf-8"));
8242
8588
  } catch {
8243
8589
  }
8244
8590
  }
8245
8591
  const merged = { ...remote.agentConfig, ...local };
8246
- writeFileSync11(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
8592
+ ensurePrivateDirSync(path23.dirname(agentConfigPath));
8593
+ writeFileSync12(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
8594
+ enforcePrivateFileSync(agentConfigPath);
8247
8595
  } catch {
8248
8596
  }
8249
8597
  }
@@ -8667,7 +9015,7 @@ async function cloudPullDocuments(config) {
8667
9015
  }
8668
9016
  return { pulled };
8669
9017
  }
8670
- var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, ROSTER_DELETIONS_PATH;
9018
+ var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, ROSTER_DELETIONS_PATH;
8671
9019
  var init_cloud_sync = __esm({
8672
9020
  "src/lib/cloud-sync.ts"() {
8673
9021
  "use strict";
@@ -8678,12 +9026,15 @@ var init_cloud_sync = __esm({
8678
9026
  init_config();
8679
9027
  init_crdt_sync();
8680
9028
  init_employees();
9029
+ init_secure_files();
8681
9030
  LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
8682
9031
  FETCH_TIMEOUT_MS = 3e4;
8683
9032
  PUSH_BATCH_SIZE = 5e3;
8684
- ROSTER_LOCK_PATH = path22.join(EXE_AI_DIR, "roster-merge.lock");
9033
+ ROSTER_LOCK_PATH = path23.join(EXE_AI_DIR, "roster-merge.lock");
8685
9034
  LOCK_STALE_MS = 3e4;
8686
- ROSTER_DELETIONS_PATH = path22.join(EXE_AI_DIR, "roster-deletions.json");
9035
+ _pgPromise = null;
9036
+ _pgFailed = false;
9037
+ ROSTER_DELETIONS_PATH = path23.join(EXE_AI_DIR, "roster-deletions.json");
8687
9038
  }
8688
9039
  });
8689
9040
 
@@ -8695,7 +9046,7 @@ __export(schedules_exports, {
8695
9046
  listSchedules: () => listSchedules,
8696
9047
  parseHumanCron: () => parseHumanCron
8697
9048
  });
8698
- import crypto8 from "crypto";
9049
+ import crypto9 from "crypto";
8699
9050
  import { execSync as execSync9 } from "child_process";
8700
9051
  async function ensureDb() {
8701
9052
  if (!isInitialized()) {
@@ -8764,7 +9115,7 @@ function parseHumanCron(input) {
8764
9115
  async function createSchedule(input) {
8765
9116
  await ensureDb();
8766
9117
  const client = getClient();
8767
- const id = crypto8.randomUUID().slice(0, 8);
9118
+ const id = crypto9.randomUUID().slice(0, 8);
8768
9119
  const now = (/* @__PURE__ */ new Date()).toISOString();
8769
9120
  const prompt = input.prompt ?? input.description;
8770
9121
  await client.execute({
@@ -8865,10 +9216,10 @@ var init_schedules = __esm({
8865
9216
 
8866
9217
  // src/bin/exe-boot.ts
8867
9218
  init_employees();
8868
- import path23 from "path";
9219
+ import path24 from "path";
8869
9220
  import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
8870
- import { existsSync as existsSync18, readFileSync as readFileSync15, readdirSync as readdirSync8, unlinkSync as unlinkSync11 } from "fs";
8871
- import os11 from "os";
9221
+ import { existsSync as existsSync20, readFileSync as readFileSync16, readdirSync as readdirSync8, unlinkSync as unlinkSync11 } from "fs";
9222
+ import os12 from "os";
8872
9223
 
8873
9224
  // src/lib/employee-templates.ts
8874
9225
  init_global_procedures();
@@ -9370,18 +9721,18 @@ init_notifications();
9370
9721
  init_config();
9371
9722
  init_session_key();
9372
9723
  init_employees();
9373
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7, readdirSync as readdirSync5 } from "fs";
9724
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7, readdirSync as readdirSync5 } from "fs";
9374
9725
  import { execSync as execSync8 } from "child_process";
9375
- import path19 from "path";
9376
- var CACHE_DIR = path19.join(EXE_AI_DIR, "session-cache");
9726
+ import path20 from "path";
9727
+ var CACHE_DIR = path20.join(EXE_AI_DIR, "session-cache");
9377
9728
  var STALE_MS = 24 * 60 * 60 * 1e3;
9378
9729
  function getMarkerPath() {
9379
- return path19.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
9730
+ return path20.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
9380
9731
  }
9381
9732
  function writeActiveAgent(agentId, agentRole) {
9382
9733
  try {
9383
9734
  mkdirSync8(CACHE_DIR, { recursive: true });
9384
- writeFileSync8(
9735
+ writeFileSync9(
9385
9736
  getMarkerPath(),
9386
9737
  JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
9387
9738
  );
@@ -9391,11 +9742,11 @@ function writeActiveAgent(agentId, agentRole) {
9391
9742
  function cleanupSessionMarkers() {
9392
9743
  const key = getSessionKey();
9393
9744
  try {
9394
- unlinkSync7(path19.join(CACHE_DIR, `active-agent-${key}.json`));
9745
+ unlinkSync7(path20.join(CACHE_DIR, `active-agent-${key}.json`));
9395
9746
  } catch {
9396
9747
  }
9397
9748
  try {
9398
- unlinkSync7(path19.join(CACHE_DIR, "active-agent-undefined.json"));
9749
+ unlinkSync7(path20.join(CACHE_DIR, "active-agent-undefined.json"));
9399
9750
  } catch {
9400
9751
  }
9401
9752
  }
@@ -9485,7 +9836,7 @@ async function boot(options) {
9485
9836
  const employeeDirs = entries.filter((e) => e.isDirectory() && !["output", "research"].includes(e.name));
9486
9837
  for (const dir of employeeDirs) {
9487
9838
  const employee = dir.name;
9488
- const taskDir = path23.join(exeDir, employee);
9839
+ const taskDir = path24.join(exeDir, employee);
9489
9840
  let files;
9490
9841
  try {
9491
9842
  files = readdirSync9(taskDir).filter((f) => f.endsWith(".md"));
@@ -9496,7 +9847,7 @@ async function boot(options) {
9496
9847
  const taskFilePath = `exe/${employee}/${file}`;
9497
9848
  let content;
9498
9849
  try {
9499
- content = readFs(path23.join(taskDir, file), "utf8");
9850
+ content = readFs(path24.join(taskDir, file), "utf8");
9500
9851
  } catch {
9501
9852
  continue;
9502
9853
  }
@@ -9582,12 +9933,12 @@ async function boot(options) {
9582
9933
  }
9583
9934
  try {
9584
9935
  for (const reviewDirName of /* @__PURE__ */ new Set(["exe", coordinatorName])) {
9585
- const reviewDir = path23.join(process.cwd(), "exe", reviewDirName);
9586
- if (existsSync18(reviewDir)) {
9936
+ const reviewDir = path24.join(process.cwd(), "exe", reviewDirName);
9937
+ if (existsSync20(reviewDir)) {
9587
9938
  for (const f of readdirSync8(reviewDir)) {
9588
9939
  if (f.startsWith("review-") && f.endsWith(".md")) {
9589
9940
  try {
9590
- unlinkSync11(path23.join(reviewDir, f));
9941
+ unlinkSync11(path24.join(reviewDir, f));
9591
9942
  } catch {
9592
9943
  }
9593
9944
  }
@@ -9633,12 +9984,12 @@ async function boot(options) {
9633
9984
  });
9634
9985
  const taskFile = String(r.task_file);
9635
9986
  try {
9636
- const filePath = path23.join(process.cwd(), taskFile);
9637
- if (existsSync18(filePath)) {
9638
- let content = readFileSync15(filePath, "utf8");
9987
+ const filePath = path24.join(process.cwd(), taskFile);
9988
+ if (existsSync20(filePath)) {
9989
+ let content = readFileSync16(filePath, "utf8");
9639
9990
  content = content.replace(/\*\*Status:\*\* needs_review/, "**Status:** done");
9640
- const { writeFileSync: writeFileSync12 } = await import("fs");
9641
- writeFileSync12(filePath, content);
9991
+ const { writeFileSync: writeFileSync13 } = await import("fs");
9992
+ writeFileSync13(filePath, content);
9642
9993
  }
9643
9994
  } catch {
9644
9995
  }
@@ -10132,19 +10483,19 @@ async function boot(options) {
10132
10483
  })()
10133
10484
  ]);
10134
10485
  try {
10135
- const configPath = path23.join(
10136
- process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path23.join(os11.homedir(), ".exe-os"),
10486
+ const configPath = path24.join(
10487
+ process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path24.join(os12.homedir(), ".exe-os"),
10137
10488
  "config.json"
10138
10489
  );
10139
- if (existsSync18(configPath)) {
10140
- const raw = JSON.parse(readFileSync15(configPath, "utf8"));
10490
+ if (existsSync20(configPath)) {
10491
+ const raw = JSON.parse(readFileSync16(configPath, "utf8"));
10141
10492
  briefData.cloudConnected = !!(raw.cloud || raw.turso);
10142
10493
  }
10143
10494
  } catch {
10144
10495
  }
10145
10496
  try {
10146
- const backfillFlagPath = path23.join(EXE_AI_DIR, "session-cache", "needs-backfill");
10147
- const isBackfillNeeded = () => existsSync18(backfillFlagPath);
10497
+ const backfillFlagPath = path24.join(EXE_AI_DIR, "session-cache", "needs-backfill");
10498
+ const isBackfillNeeded = () => existsSync20(backfillFlagPath);
10148
10499
  const coverageResult = await client.execute({
10149
10500
  sql: `SELECT COUNT(*) as total,
10150
10501
  SUM(CASE WHEN vector IS NOT NULL THEN 1 ELSE 0 END) as with_vectors
@@ -10166,8 +10517,8 @@ async function boot(options) {
10166
10517
  let daemonRunning = false;
10167
10518
  let daemonUptime;
10168
10519
  let daemonRequestsServed;
10169
- const socketPath = path23.join(EXE_AI_DIR, "exed.sock");
10170
- if (existsSync18(socketPath)) {
10520
+ const socketPath = path24.join(EXE_AI_DIR, "exed.sock");
10521
+ if (existsSync20(socketPath)) {
10171
10522
  try {
10172
10523
  const net2 = await import("net");
10173
10524
  const health = await new Promise((resolve) => {
@@ -10209,10 +10560,10 @@ async function boot(options) {
10209
10560
  }
10210
10561
  }
10211
10562
  if (!daemonRunning) {
10212
- const pidPath = path23.join(EXE_AI_DIR, "exed.pid");
10213
- if (existsSync18(pidPath)) {
10563
+ const pidPath = path24.join(EXE_AI_DIR, "exed.pid");
10564
+ if (existsSync20(pidPath)) {
10214
10565
  try {
10215
- const pid = parseInt(readFileSync15(pidPath, "utf8").trim(), 10);
10566
+ const pid = parseInt(readFileSync16(pidPath, "utf8").trim(), 10);
10216
10567
  if (pid > 0) {
10217
10568
  process.kill(pid, 0);
10218
10569
  daemonRunning = true;
@@ -10223,8 +10574,8 @@ async function boot(options) {
10223
10574
  }
10224
10575
  if (nullCount === 0) {
10225
10576
  try {
10226
- const flagPath = path23.join(EXE_AI_DIR, "session-cache", "needs-backfill");
10227
- if (existsSync18(flagPath)) {
10577
+ const flagPath = path24.join(EXE_AI_DIR, "session-cache", "needs-backfill");
10578
+ if (existsSync20(flagPath)) {
10228
10579
  const { unlinkSync: unlinkSync12 } = await import("fs");
10229
10580
  unlinkSync12(flagPath);
10230
10581
  }
@@ -10250,10 +10601,10 @@ async function boot(options) {
10250
10601
  const { spawn: spawn2 } = await import("child_process");
10251
10602
  const { fileURLToPath: fileURLToPath4 } = await import("url");
10252
10603
  const thisFile = fileURLToPath4(import.meta.url);
10253
- const backfillPath = path23.resolve(path23.dirname(thisFile), "backfill-vectors.js");
10254
- if (existsSync18(backfillPath)) {
10604
+ const backfillPath = path24.resolve(path24.dirname(thisFile), "backfill-vectors.js");
10605
+ if (existsSync20(backfillPath)) {
10255
10606
  const { openSync: openSync3, closeSync: closeSync3 } = await import("fs");
10256
- const workerLogPath = path23.join(EXE_AI_DIR, "workers.log");
10607
+ const workerLogPath = path24.join(EXE_AI_DIR, "workers.log");
10257
10608
  let stderrFd = "ignore";
10258
10609
  try {
10259
10610
  stderrFd = openSync3(workerLogPath, "a");
@@ -10283,8 +10634,8 @@ async function boot(options) {
10283
10634
  const criticalBinaries = ["backfill-vectors.js", "scan-tasks.js"];
10284
10635
  const missing = [];
10285
10636
  for (const bin of criticalBinaries) {
10286
- const binPath = path23.resolve(path23.dirname(thisFile), bin);
10287
- if (!existsSync18(binPath)) {
10637
+ const binPath = path24.resolve(path24.dirname(thisFile), bin);
10638
+ if (!existsSync20(binPath)) {
10288
10639
  missing.push(`dist/bin/${bin}`);
10289
10640
  }
10290
10641
  }
@@ -10313,7 +10664,7 @@ async function boot(options) {
10313
10664
  console.log(brief);
10314
10665
  return;
10315
10666
  }
10316
- const sessionDir = path23.join(EXE_AI_DIR, "sessions", coordinatorName);
10667
+ const sessionDir = path24.join(EXE_AI_DIR, "sessions", coordinatorName);
10317
10668
  await mkdir5(sessionDir, { recursive: true });
10318
10669
  const claudeMdContent = `${getSessionPrompt(coordinatorEmployee.systemPrompt)}
10319
10670
 
@@ -10322,7 +10673,7 @@ async function boot(options) {
10322
10673
  # Status Brief
10323
10674
 
10324
10675
  ${brief}`;
10325
- await writeFile6(path23.join(sessionDir, "CLAUDE.md"), claudeMdContent, "utf-8");
10676
+ await writeFile6(path24.join(sessionDir, "CLAUDE.md"), claudeMdContent, "utf-8");
10326
10677
  const unread = await readUnreadNotifications();
10327
10678
  if (unread.length > 0) {
10328
10679
  console.log(`\u{1F4EC} ${unread.length} unread notification${unread.length === 1 ? "" : "s"}`);
@@ -10331,12 +10682,12 @@ ${brief}`;
10331
10682
  await cleanupOldNotifications();
10332
10683
  console.log(brief);
10333
10684
  try {
10334
- const configPath2 = path23.join(
10335
- process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path23.join(os11.homedir(), ".exe-os"),
10685
+ const configPath2 = path24.join(
10686
+ process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path24.join(os12.homedir(), ".exe-os"),
10336
10687
  "config.json"
10337
10688
  );
10338
- if (existsSync18(configPath2)) {
10339
- const rawCfg = JSON.parse(readFileSync15(configPath2, "utf8"));
10689
+ if (existsSync20(configPath2)) {
10690
+ const rawCfg = JSON.parse(readFileSync16(configPath2, "utf8"));
10340
10691
  const cloudCfg = rawCfg.cloud;
10341
10692
  if (cloudCfg?.apiKey) {
10342
10693
  const { initSyncCrypto: initSyncCrypto2, isSyncCryptoInitialized: isSyncCryptoInitialized2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));