@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
@@ -34,6 +34,44 @@ var init_memory = __esm({
34
34
  }
35
35
  });
36
36
 
37
+ // src/lib/secure-files.ts
38
+ import { chmodSync, existsSync, mkdirSync } from "fs";
39
+ import { chmod, mkdir } from "fs/promises";
40
+ async function ensurePrivateDir(dirPath) {
41
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
42
+ try {
43
+ await chmod(dirPath, PRIVATE_DIR_MODE);
44
+ } catch {
45
+ }
46
+ }
47
+ function ensurePrivateDirSync(dirPath) {
48
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
49
+ try {
50
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
51
+ } catch {
52
+ }
53
+ }
54
+ async function enforcePrivateFile(filePath) {
55
+ try {
56
+ await chmod(filePath, PRIVATE_FILE_MODE);
57
+ } catch {
58
+ }
59
+ }
60
+ function enforcePrivateFileSync(filePath) {
61
+ try {
62
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
63
+ } catch {
64
+ }
65
+ }
66
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
67
+ var init_secure_files = __esm({
68
+ "src/lib/secure-files.ts"() {
69
+ "use strict";
70
+ PRIVATE_DIR_MODE = 448;
71
+ PRIVATE_FILE_MODE = 384;
72
+ }
73
+ });
74
+
37
75
  // src/lib/config.ts
38
76
  var config_exports = {};
39
77
  __export(config_exports, {
@@ -50,8 +88,8 @@ __export(config_exports, {
50
88
  migrateConfig: () => migrateConfig,
51
89
  saveConfig: () => saveConfig
52
90
  });
53
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
54
- import { readFileSync, existsSync, renameSync } from "fs";
91
+ import { readFile, writeFile } from "fs/promises";
92
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
55
93
  import path from "path";
56
94
  import os from "os";
57
95
  function resolveDataDir() {
@@ -59,7 +97,7 @@ function resolveDataDir() {
59
97
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
60
98
  const newDir = path.join(os.homedir(), ".exe-os");
61
99
  const legacyDir = path.join(os.homedir(), ".exe-mem");
62
- if (!existsSync(newDir) && existsSync(legacyDir)) {
100
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
63
101
  try {
64
102
  renameSync(legacyDir, newDir);
65
103
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -122,9 +160,9 @@ function normalizeAutoUpdate(raw) {
122
160
  }
123
161
  async function loadConfig() {
124
162
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
125
- await mkdir(dir, { recursive: true });
163
+ await ensurePrivateDir(dir);
126
164
  const configPath = path.join(dir, "config.json");
127
- if (!existsSync(configPath)) {
165
+ if (!existsSync2(configPath)) {
128
166
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
129
167
  }
130
168
  const raw = await readFile(configPath, "utf-8");
@@ -137,6 +175,7 @@ async function loadConfig() {
137
175
  `);
138
176
  try {
139
177
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
178
+ await enforcePrivateFile(configPath);
140
179
  } catch {
141
180
  }
142
181
  }
@@ -155,7 +194,7 @@ async function loadConfig() {
155
194
  function loadConfigSync() {
156
195
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
157
196
  const configPath = path.join(dir, "config.json");
158
- if (!existsSync(configPath)) {
197
+ if (!existsSync2(configPath)) {
159
198
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
160
199
  }
161
200
  try {
@@ -173,12 +212,10 @@ function loadConfigSync() {
173
212
  }
174
213
  async function saveConfig(config2) {
175
214
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
176
- await mkdir(dir, { recursive: true });
215
+ await ensurePrivateDir(dir);
177
216
  const configPath = path.join(dir, "config.json");
178
217
  await writeFile(configPath, JSON.stringify(config2, null, 2) + "\n");
179
- if (config2.cloud?.apiKey) {
180
- await chmod(configPath, 384);
181
- }
218
+ await enforcePrivateFile(configPath);
182
219
  }
183
220
  async function loadConfigFrom(configPath) {
184
221
  const raw = await readFile(configPath, "utf-8");
@@ -198,6 +235,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
198
235
  var init_config = __esm({
199
236
  "src/lib/config.ts"() {
200
237
  "use strict";
238
+ init_secure_files();
201
239
  EXE_AI_DIR = resolveDataDir();
202
240
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
203
241
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -274,6 +312,43 @@ var init_config = __esm({
274
312
  }
275
313
  });
276
314
 
315
+ // src/lib/daemon-auth.ts
316
+ import crypto2 from "crypto";
317
+ import path2 from "path";
318
+ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync } from "fs";
319
+ function normalizeToken(token) {
320
+ if (!token) return null;
321
+ const trimmed = token.trim();
322
+ return trimmed.length > 0 ? trimmed : null;
323
+ }
324
+ function readDaemonToken() {
325
+ try {
326
+ if (!existsSync3(DAEMON_TOKEN_PATH)) return null;
327
+ return normalizeToken(readFileSync2(DAEMON_TOKEN_PATH, "utf8"));
328
+ } catch {
329
+ return null;
330
+ }
331
+ }
332
+ function ensureDaemonToken(seed) {
333
+ const existing = readDaemonToken();
334
+ if (existing) return existing;
335
+ const token = normalizeToken(seed) ?? crypto2.randomBytes(32).toString("hex");
336
+ ensurePrivateDirSync(EXE_AI_DIR);
337
+ writeFileSync(DAEMON_TOKEN_PATH, `${token}
338
+ `, "utf8");
339
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
340
+ return token;
341
+ }
342
+ var DAEMON_TOKEN_PATH;
343
+ var init_daemon_auth = __esm({
344
+ "src/lib/daemon-auth.ts"() {
345
+ "use strict";
346
+ init_config();
347
+ init_secure_files();
348
+ DAEMON_TOKEN_PATH = path2.join(EXE_AI_DIR, "exed.token");
349
+ }
350
+ });
351
+
277
352
  // src/lib/exe-daemon-client.ts
278
353
  var exe_daemon_client_exports = {};
279
354
  __export(exe_daemon_client_exports, {
@@ -283,14 +358,15 @@ __export(exe_daemon_client_exports, {
283
358
  embedViaClient: () => embedViaClient,
284
359
  isClientConnected: () => isClientConnected,
285
360
  pingDaemon: () => pingDaemon,
286
- sendDaemonRequest: () => sendDaemonRequest
361
+ sendDaemonRequest: () => sendDaemonRequest,
362
+ sendIngestRequest: () => sendIngestRequest
287
363
  });
288
364
  import net from "net";
289
365
  import os2 from "os";
290
366
  import { spawn } from "child_process";
291
367
  import { randomUUID } from "crypto";
292
- import { existsSync as existsSync2, unlinkSync, readFileSync as readFileSync2, openSync, closeSync, statSync } from "fs";
293
- import path2 from "path";
368
+ import { existsSync as existsSync4, unlinkSync, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
369
+ import path3 from "path";
294
370
  import { fileURLToPath } from "url";
295
371
  function handleData(chunk) {
296
372
  _buffer += chunk.toString();
@@ -318,9 +394,9 @@ function handleData(chunk) {
318
394
  }
319
395
  }
320
396
  function cleanupStaleFiles() {
321
- if (existsSync2(PID_PATH)) {
397
+ if (existsSync4(PID_PATH)) {
322
398
  try {
323
- const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
399
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
324
400
  if (pid > 0) {
325
401
  try {
326
402
  process.kill(pid, 0);
@@ -341,11 +417,11 @@ function cleanupStaleFiles() {
341
417
  }
342
418
  }
343
419
  function findPackageRoot() {
344
- let dir = path2.dirname(fileURLToPath(import.meta.url));
345
- const { root } = path2.parse(dir);
420
+ let dir = path3.dirname(fileURLToPath(import.meta.url));
421
+ const { root } = path3.parse(dir);
346
422
  while (dir !== root) {
347
- if (existsSync2(path2.join(dir, "package.json"))) return dir;
348
- dir = path2.dirname(dir);
423
+ if (existsSync4(path3.join(dir, "package.json"))) return dir;
424
+ dir = path3.dirname(dir);
349
425
  }
350
426
  return null;
351
427
  }
@@ -371,16 +447,17 @@ function spawnDaemon() {
371
447
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
372
448
  return;
373
449
  }
374
- const daemonPath = path2.join(pkgRoot, "dist", "lib", "exe-daemon.js");
375
- if (!existsSync2(daemonPath)) {
450
+ const daemonPath = path3.join(pkgRoot, "dist", "lib", "exe-daemon.js");
451
+ if (!existsSync4(daemonPath)) {
376
452
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
377
453
  `);
378
454
  return;
379
455
  }
380
456
  const resolvedPath = daemonPath;
457
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
381
458
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
382
459
  `);
383
- const logPath = path2.join(path2.dirname(SOCKET_PATH), "exed.log");
460
+ const logPath = path3.join(path3.dirname(SOCKET_PATH), "exed.log");
384
461
  let stderrFd = "ignore";
385
462
  try {
386
463
  stderrFd = openSync(logPath, "a");
@@ -398,7 +475,8 @@ function spawnDaemon() {
398
475
  TMUX_PANE: void 0,
399
476
  // Prevents resolveExeSession() from scoping to one session
400
477
  EXE_DAEMON_SOCK: SOCKET_PATH,
401
- EXE_DAEMON_PID: PID_PATH
478
+ EXE_DAEMON_PID: PID_PATH,
479
+ [DAEMON_TOKEN_ENV]: daemonToken
402
480
  }
403
481
  });
404
482
  child.unref();
@@ -508,13 +586,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
508
586
  return;
509
587
  }
510
588
  const id = randomUUID();
589
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
511
590
  const timer = setTimeout(() => {
512
591
  _pending.delete(id);
513
592
  resolve({ error: "Request timeout" });
514
593
  }, timeoutMs);
515
594
  _pending.set(id, { resolve, timer });
516
595
  try {
517
- _socket.write(JSON.stringify({ id, ...payload }) + "\n");
596
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
518
597
  } catch {
519
598
  clearTimeout(timer);
520
599
  _pending.delete(id);
@@ -543,9 +622,9 @@ function killAndRespawnDaemon() {
543
622
  }
544
623
  try {
545
624
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
546
- if (existsSync2(PID_PATH)) {
625
+ if (existsSync4(PID_PATH)) {
547
626
  try {
548
- const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
627
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
549
628
  if (pid > 0) {
550
629
  try {
551
630
  process.kill(pid, "SIGKILL");
@@ -674,17 +753,30 @@ function disconnectClient() {
674
753
  function isClientConnected() {
675
754
  return _connected;
676
755
  }
677
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
756
+ function sendIngestRequest(payload) {
757
+ if (!_socket || !_connected) return false;
758
+ try {
759
+ const id = randomUUID();
760
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
761
+ _socket.write(JSON.stringify({ id, token, type: "ingest", ...payload }) + "\n");
762
+ return true;
763
+ } catch {
764
+ return false;
765
+ }
766
+ }
767
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
678
768
  var init_exe_daemon_client = __esm({
679
769
  "src/lib/exe-daemon-client.ts"() {
680
770
  "use strict";
681
771
  init_config();
682
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path2.join(EXE_AI_DIR, "exed.sock");
683
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path2.join(EXE_AI_DIR, "exed.pid");
684
- SPAWN_LOCK_PATH = path2.join(EXE_AI_DIR, "exed-spawn.lock");
772
+ init_daemon_auth();
773
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path3.join(EXE_AI_DIR, "exed.sock");
774
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path3.join(EXE_AI_DIR, "exed.pid");
775
+ SPAWN_LOCK_PATH = path3.join(EXE_AI_DIR, "exed-spawn.lock");
685
776
  SPAWN_LOCK_STALE_MS = 3e4;
686
777
  CONNECT_TIMEOUT_MS = 15e3;
687
778
  REQUEST_TIMEOUT_MS = 3e4;
779
+ DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
688
780
  _socket = null;
689
781
  _connected = false;
690
782
  _buffer = "";
@@ -736,10 +828,10 @@ async function disposeEmbedder() {
736
828
  async function embedDirect(text) {
737
829
  const llamaCpp = await import("node-llama-cpp");
738
830
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
739
- const { existsSync: existsSync32 } = await import("fs");
740
- const path41 = await import("path");
741
- const modelPath = path41.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
742
- if (!existsSync32(modelPath)) {
831
+ const { existsSync: existsSync34 } = await import("fs");
832
+ const path45 = await import("path");
833
+ const modelPath = path45.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
834
+ if (!existsSync34(modelPath)) {
743
835
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
744
836
  }
745
837
  const llama = await llamaCpp.getLlama();
@@ -862,20 +954,21 @@ __export(agent_config_exports, {
862
954
  saveAgentConfig: () => saveAgentConfig,
863
955
  setAgentRuntime: () => setAgentRuntime
864
956
  });
865
- import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync3, mkdirSync } from "fs";
866
- import path3 from "path";
957
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync5 } from "fs";
958
+ import path4 from "path";
867
959
  function loadAgentConfig() {
868
- if (!existsSync3(AGENT_CONFIG_PATH)) return {};
960
+ if (!existsSync5(AGENT_CONFIG_PATH)) return {};
869
961
  try {
870
- return JSON.parse(readFileSync3(AGENT_CONFIG_PATH, "utf-8"));
962
+ return JSON.parse(readFileSync4(AGENT_CONFIG_PATH, "utf-8"));
871
963
  } catch {
872
964
  return {};
873
965
  }
874
966
  }
875
967
  function saveAgentConfig(config2) {
876
- const dir = path3.dirname(AGENT_CONFIG_PATH);
877
- if (!existsSync3(dir)) mkdirSync(dir, { recursive: true });
878
- writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
968
+ const dir = path4.dirname(AGENT_CONFIG_PATH);
969
+ ensurePrivateDirSync(dir);
970
+ writeFileSync2(AGENT_CONFIG_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
971
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
879
972
  }
880
973
  function getAgentRuntime(agentId) {
881
974
  const config2 = loadAgentConfig();
@@ -915,7 +1008,8 @@ var init_agent_config = __esm({
915
1008
  "use strict";
916
1009
  init_config();
917
1010
  init_runtime_table();
918
- AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
1011
+ init_secure_files();
1012
+ AGENT_CONFIG_PATH = path4.join(EXE_AI_DIR, "agent-config.json");
919
1013
  KNOWN_RUNTIMES = {
920
1014
  claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
921
1015
  codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
@@ -962,9 +1056,9 @@ __export(employees_exports, {
962
1056
  validateEmployeeName: () => validateEmployeeName
963
1057
  });
964
1058
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
965
- import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync4, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
1059
+ import { existsSync as existsSync6, symlinkSync, readlinkSync, readFileSync as readFileSync5, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
966
1060
  import { execSync } from "child_process";
967
- import path4 from "path";
1061
+ import path5 from "path";
968
1062
  import os3 from "os";
969
1063
  function normalizeRole(role) {
970
1064
  return (role ?? "").trim().toLowerCase();
@@ -1001,7 +1095,7 @@ function validateEmployeeName(name) {
1001
1095
  return { valid: true };
1002
1096
  }
1003
1097
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
1004
- if (!existsSync4(employeesPath)) {
1098
+ if (!existsSync6(employeesPath)) {
1005
1099
  return [];
1006
1100
  }
1007
1101
  const raw = await readFile2(employeesPath, "utf-8");
@@ -1012,13 +1106,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
1012
1106
  }
1013
1107
  }
1014
1108
  async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
1015
- await mkdir2(path4.dirname(employeesPath), { recursive: true });
1109
+ await mkdir2(path5.dirname(employeesPath), { recursive: true });
1016
1110
  await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
1017
1111
  }
1018
1112
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
1019
- if (!existsSync4(employeesPath)) return [];
1113
+ if (!existsSync6(employeesPath)) return [];
1020
1114
  try {
1021
- return JSON.parse(readFileSync4(employeesPath, "utf-8"));
1115
+ return JSON.parse(readFileSync5(employeesPath, "utf-8"));
1022
1116
  } catch {
1023
1117
  return [];
1024
1118
  }
@@ -1063,9 +1157,9 @@ function addEmployee(employees, employee) {
1063
1157
  function appendToCoordinatorTeam(employee) {
1064
1158
  const coordinator = getCoordinatorEmployee(loadEmployeesSync());
1065
1159
  if (!coordinator) return;
1066
- const idPath = path4.join(IDENTITY_DIR, `${coordinator.name}.md`);
1067
- if (!existsSync4(idPath)) return;
1068
- const content = readFileSync4(idPath, "utf-8");
1160
+ const idPath = path5.join(IDENTITY_DIR, `${coordinator.name}.md`);
1161
+ if (!existsSync6(idPath)) return;
1162
+ const content = readFileSync5(idPath, "utf-8");
1069
1163
  if (content.includes(`**${capitalize(employee.name)}`)) return;
1070
1164
  const teamMatch = content.match(TEAM_SECTION_RE);
1071
1165
  if (!teamMatch || teamMatch.index === void 0) return;
@@ -1081,7 +1175,7 @@ function appendToCoordinatorTeam(employee) {
1081
1175
  } else {
1082
1176
  updated = content.trimEnd() + "\n" + entry;
1083
1177
  }
1084
- writeFileSync2(idPath, updated, "utf-8");
1178
+ writeFileSync3(idPath, updated, "utf-8");
1085
1179
  }
1086
1180
  function capitalize(s) {
1087
1181
  return s.charAt(0).toUpperCase() + s.slice(1);
@@ -1115,14 +1209,14 @@ async function normalizeRosterCase(rosterPath) {
1115
1209
  emp.name = emp.name.toLowerCase();
1116
1210
  changed = true;
1117
1211
  try {
1118
- const identityDir = path4.join(os3.homedir(), ".exe-os", "identity");
1119
- const oldPath = path4.join(identityDir, `${oldName}.md`);
1120
- const newPath = path4.join(identityDir, `${emp.name}.md`);
1121
- if (existsSync4(oldPath) && !existsSync4(newPath)) {
1212
+ const identityDir = path5.join(os3.homedir(), ".exe-os", "identity");
1213
+ const oldPath = path5.join(identityDir, `${oldName}.md`);
1214
+ const newPath = path5.join(identityDir, `${emp.name}.md`);
1215
+ if (existsSync6(oldPath) && !existsSync6(newPath)) {
1122
1216
  renameSync2(oldPath, newPath);
1123
- } else if (existsSync4(oldPath) && oldPath !== newPath) {
1124
- const content = readFileSync4(oldPath, "utf-8");
1125
- writeFileSync2(newPath, content, "utf-8");
1217
+ } else if (existsSync6(oldPath) && oldPath !== newPath) {
1218
+ const content = readFileSync5(oldPath, "utf-8");
1219
+ writeFileSync3(newPath, content, "utf-8");
1126
1220
  if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
1127
1221
  unlinkSync2(oldPath);
1128
1222
  }
@@ -1152,7 +1246,7 @@ function registerBinSymlinks(name) {
1152
1246
  errors.push("Could not find 'exe-os' in PATH");
1153
1247
  return { created, skipped, errors };
1154
1248
  }
1155
- const binDir = path4.dirname(exeBinPath);
1249
+ const binDir = path5.dirname(exeBinPath);
1156
1250
  let target;
1157
1251
  try {
1158
1252
  target = readlinkSync(exeBinPath);
@@ -1162,8 +1256,8 @@ function registerBinSymlinks(name) {
1162
1256
  }
1163
1257
  for (const suffix of ["", "-opencode"]) {
1164
1258
  const linkName = `${name}${suffix}`;
1165
- const linkPath = path4.join(binDir, linkName);
1166
- if (existsSync4(linkPath)) {
1259
+ const linkPath = path5.join(binDir, linkName);
1260
+ if (existsSync6(linkPath)) {
1167
1261
  skipped.push(linkName);
1168
1262
  continue;
1169
1263
  }
@@ -1181,18 +1275,18 @@ var init_employees = __esm({
1181
1275
  "src/lib/employees.ts"() {
1182
1276
  "use strict";
1183
1277
  init_config();
1184
- EMPLOYEES_PATH = path4.join(EXE_AI_DIR, "exe-employees.json");
1278
+ EMPLOYEES_PATH = path5.join(EXE_AI_DIR, "exe-employees.json");
1185
1279
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
1186
1280
  COORDINATOR_ROLE = "COO";
1187
1281
  MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
1188
- IDENTITY_DIR = path4.join(EXE_AI_DIR, "identity");
1282
+ IDENTITY_DIR = path5.join(EXE_AI_DIR, "identity");
1189
1283
  TEAM_SECTION_RE = /^## Team\b.*$/m;
1190
1284
  }
1191
1285
  });
1192
1286
 
1193
1287
  // src/lib/database-adapter.ts
1194
1288
  import os4 from "os";
1195
- import path5 from "path";
1289
+ import path6 from "path";
1196
1290
  import { createRequire } from "module";
1197
1291
  import { pathToFileURL } from "url";
1198
1292
  function quotedIdentifier(identifier) {
@@ -1503,8 +1597,8 @@ async function loadPrismaClient() {
1503
1597
  }
1504
1598
  return new PrismaClient2();
1505
1599
  }
1506
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path5.join(os4.homedir(), "exe-db");
1507
- const requireFromExeDb = createRequire(path5.join(exeDbRoot, "package.json"));
1600
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os4.homedir(), "exe-db");
1601
+ const requireFromExeDb = createRequire(path6.join(exeDbRoot, "package.json"));
1508
1602
  const prismaEntry = requireFromExeDb.resolve("@prisma/client");
1509
1603
  const module = await import(pathToFileURL(prismaEntry).href);
1510
1604
  const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
@@ -2344,6 +2438,7 @@ async function ensureSchema() {
2344
2438
  project TEXT NOT NULL,
2345
2439
  summary TEXT NOT NULL,
2346
2440
  task_file TEXT,
2441
+ session_scope TEXT,
2347
2442
  read INTEGER NOT NULL DEFAULT 0,
2348
2443
  created_at TEXT NOT NULL
2349
2444
  );
@@ -2352,7 +2447,7 @@ async function ensureSchema() {
2352
2447
  ON notifications(read);
2353
2448
 
2354
2449
  CREATE INDEX IF NOT EXISTS idx_notifications_agent
2355
- ON notifications(agent_id);
2450
+ ON notifications(agent_id, session_scope);
2356
2451
 
2357
2452
  CREATE INDEX IF NOT EXISTS idx_notifications_task_file
2358
2453
  ON notifications(task_file);
@@ -2390,6 +2485,7 @@ async function ensureSchema() {
2390
2485
  target_agent TEXT NOT NULL,
2391
2486
  target_project TEXT,
2392
2487
  target_device TEXT NOT NULL DEFAULT 'local',
2488
+ session_scope TEXT,
2393
2489
  content TEXT NOT NULL,
2394
2490
  priority TEXT DEFAULT 'normal',
2395
2491
  status TEXT DEFAULT 'pending',
@@ -2403,10 +2499,31 @@ async function ensureSchema() {
2403
2499
  );
2404
2500
 
2405
2501
  CREATE INDEX IF NOT EXISTS idx_messages_target
2406
- ON messages(target_agent, status);
2502
+ ON messages(target_agent, session_scope, status);
2407
2503
 
2408
2504
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
2409
- ON messages(target_agent, from_agent, server_seq);
2505
+ ON messages(target_agent, session_scope, from_agent, server_seq);
2506
+ `);
2507
+ try {
2508
+ await client.execute({
2509
+ sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
2510
+ args: []
2511
+ });
2512
+ } catch {
2513
+ }
2514
+ try {
2515
+ await client.execute({
2516
+ sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
2517
+ args: []
2518
+ });
2519
+ } catch {
2520
+ }
2521
+ await client.executeMultiple(`
2522
+ CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
2523
+ ON notifications(agent_id, session_scope, read, created_at);
2524
+
2525
+ CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
2526
+ ON messages(target_agent, session_scope, status, created_at);
2410
2527
  `);
2411
2528
  try {
2412
2529
  await client.execute({
@@ -2990,6 +3107,13 @@ async function ensureSchema() {
2990
3107
  } catch {
2991
3108
  }
2992
3109
  }
3110
+ try {
3111
+ await client.execute({
3112
+ sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
3113
+ args: []
3114
+ });
3115
+ } catch {
3116
+ }
2993
3117
  }
2994
3118
  async function disposeDatabase() {
2995
3119
  if (_walCheckpointTimer) {
@@ -3037,14 +3161,14 @@ __export(keychain_exports, {
3037
3161
  setMasterKey: () => setMasterKey
3038
3162
  });
3039
3163
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3040
- import { existsSync as existsSync5 } from "fs";
3041
- import path6 from "path";
3164
+ import { existsSync as existsSync7 } from "fs";
3165
+ import path7 from "path";
3042
3166
  import os5 from "os";
3043
3167
  function getKeyDir() {
3044
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
3168
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
3045
3169
  }
3046
3170
  function getKeyPath() {
3047
- return path6.join(getKeyDir(), "master.key");
3171
+ return path7.join(getKeyDir(), "master.key");
3048
3172
  }
3049
3173
  async function tryKeytar() {
3050
3174
  try {
@@ -3065,7 +3189,7 @@ async function getMasterKey() {
3065
3189
  }
3066
3190
  }
3067
3191
  const keyPath = getKeyPath();
3068
- if (!existsSync5(keyPath)) {
3192
+ if (!existsSync7(keyPath)) {
3069
3193
  process.stderr.write(
3070
3194
  `[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
3071
3195
  `
@@ -3108,7 +3232,7 @@ async function deleteMasterKey() {
3108
3232
  }
3109
3233
  }
3110
3234
  const keyPath = getKeyPath();
3111
- if (existsSync5(keyPath)) {
3235
+ if (existsSync7(keyPath)) {
3112
3236
  await unlink(keyPath);
3113
3237
  }
3114
3238
  }
@@ -3210,6 +3334,7 @@ var shard_manager_exports = {};
3210
3334
  __export(shard_manager_exports, {
3211
3335
  disposeShards: () => disposeShards,
3212
3336
  ensureShardSchema: () => ensureShardSchema,
3337
+ getOpenShardCount: () => getOpenShardCount,
3213
3338
  getReadyShardClient: () => getReadyShardClient,
3214
3339
  getShardClient: () => getShardClient,
3215
3340
  getShardsDir: () => getShardsDir,
@@ -3218,15 +3343,18 @@ __export(shard_manager_exports, {
3218
3343
  listShards: () => listShards,
3219
3344
  shardExists: () => shardExists
3220
3345
  });
3221
- import path7 from "path";
3222
- import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync } from "fs";
3346
+ import path8 from "path";
3347
+ import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync } from "fs";
3223
3348
  import { createClient as createClient2 } from "@libsql/client";
3224
3349
  function initShardManager(encryptionKey) {
3225
3350
  _encryptionKey = encryptionKey;
3226
- if (!existsSync6(SHARDS_DIR)) {
3351
+ if (!existsSync8(SHARDS_DIR)) {
3227
3352
  mkdirSync2(SHARDS_DIR, { recursive: true });
3228
3353
  }
3229
3354
  _shardingEnabled = true;
3355
+ if (_evictionTimer) clearInterval(_evictionTimer);
3356
+ _evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
3357
+ _evictionTimer.unref();
3230
3358
  }
3231
3359
  function isShardingEnabled() {
3232
3360
  return _shardingEnabled;
@@ -3243,21 +3371,28 @@ function getShardClient(projectName) {
3243
3371
  throw new Error(`Invalid project name for shard: "${projectName}"`);
3244
3372
  }
3245
3373
  const cached = _shards.get(safeName);
3246
- if (cached) return cached;
3247
- const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
3374
+ if (cached) {
3375
+ _shardLastAccess.set(safeName, Date.now());
3376
+ return cached;
3377
+ }
3378
+ while (_shards.size >= MAX_OPEN_SHARDS) {
3379
+ evictLRU();
3380
+ }
3381
+ const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
3248
3382
  const client = createClient2({
3249
3383
  url: `file:${dbPath}`,
3250
3384
  encryptionKey: _encryptionKey
3251
3385
  });
3252
3386
  _shards.set(safeName, client);
3387
+ _shardLastAccess.set(safeName, Date.now());
3253
3388
  return client;
3254
3389
  }
3255
3390
  function shardExists(projectName) {
3256
3391
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3257
- return existsSync6(path7.join(SHARDS_DIR, `${safeName}.db`));
3392
+ return existsSync8(path8.join(SHARDS_DIR, `${safeName}.db`));
3258
3393
  }
3259
3394
  function listShards() {
3260
- if (!existsSync6(SHARDS_DIR)) return [];
3395
+ if (!existsSync8(SHARDS_DIR)) return [];
3261
3396
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
3262
3397
  }
3263
3398
  async function ensureShardSchema(client) {
@@ -3309,6 +3444,8 @@ async function ensureShardSchema(client) {
3309
3444
  for (const col of [
3310
3445
  "ALTER TABLE memories ADD COLUMN task_id TEXT",
3311
3446
  "ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
3447
+ "ALTER TABLE memories ADD COLUMN author_device_id TEXT",
3448
+ "ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
3312
3449
  "ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
3313
3450
  "ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
3314
3451
  "ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
@@ -3446,21 +3583,69 @@ async function getReadyShardClient(projectName) {
3446
3583
  await ensureShardSchema(client);
3447
3584
  return client;
3448
3585
  }
3586
+ function evictLRU() {
3587
+ let oldest = null;
3588
+ let oldestTime = Infinity;
3589
+ for (const [name, time] of _shardLastAccess) {
3590
+ if (time < oldestTime) {
3591
+ oldestTime = time;
3592
+ oldest = name;
3593
+ }
3594
+ }
3595
+ if (oldest) {
3596
+ const client = _shards.get(oldest);
3597
+ if (client) {
3598
+ client.close();
3599
+ }
3600
+ _shards.delete(oldest);
3601
+ _shardLastAccess.delete(oldest);
3602
+ }
3603
+ }
3604
+ function evictIdleShards() {
3605
+ const now = Date.now();
3606
+ const toEvict = [];
3607
+ for (const [name, lastAccess] of _shardLastAccess) {
3608
+ if (now - lastAccess > SHARD_IDLE_MS) {
3609
+ toEvict.push(name);
3610
+ }
3611
+ }
3612
+ for (const name of toEvict) {
3613
+ const client = _shards.get(name);
3614
+ if (client) {
3615
+ client.close();
3616
+ }
3617
+ _shards.delete(name);
3618
+ _shardLastAccess.delete(name);
3619
+ }
3620
+ }
3621
+ function getOpenShardCount() {
3622
+ return _shards.size;
3623
+ }
3449
3624
  function disposeShards() {
3625
+ if (_evictionTimer) {
3626
+ clearInterval(_evictionTimer);
3627
+ _evictionTimer = null;
3628
+ }
3450
3629
  for (const [, client] of _shards) {
3451
3630
  client.close();
3452
3631
  }
3453
3632
  _shards.clear();
3633
+ _shardLastAccess.clear();
3454
3634
  _shardingEnabled = false;
3455
3635
  _encryptionKey = null;
3456
3636
  }
3457
- var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
3637
+ var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
3458
3638
  var init_shard_manager = __esm({
3459
3639
  "src/lib/shard-manager.ts"() {
3460
3640
  "use strict";
3461
3641
  init_config();
3462
- SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
3642
+ SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
3643
+ SHARD_IDLE_MS = 5 * 60 * 1e3;
3644
+ MAX_OPEN_SHARDS = 10;
3645
+ EVICTION_INTERVAL_MS = 60 * 1e3;
3463
3646
  _shards = /* @__PURE__ */ new Map();
3647
+ _shardLastAccess = /* @__PURE__ */ new Map();
3648
+ _evictionTimer = null;
3464
3649
  _encryptionKey = null;
3465
3650
  _shardingEnabled = false;
3466
3651
  }
@@ -4322,8 +4507,8 @@ __export(reranker_exports, {
4322
4507
  rerankWithContext: () => rerankWithContext,
4323
4508
  rerankWithScores: () => rerankWithScores
4324
4509
  });
4325
- import path8 from "path";
4326
- import { existsSync as existsSync7 } from "fs";
4510
+ import path9 from "path";
4511
+ import { existsSync as existsSync9 } from "fs";
4327
4512
  function resetIdleTimer() {
4328
4513
  if (_idleTimer) clearTimeout(_idleTimer);
4329
4514
  _idleTimer = setTimeout(() => {
@@ -4334,18 +4519,18 @@ function resetIdleTimer() {
4334
4519
  }
4335
4520
  }
4336
4521
  function isRerankerAvailable() {
4337
- return existsSync7(path8.join(MODELS_DIR, RERANKER_MODEL_FILE));
4522
+ return existsSync9(path9.join(MODELS_DIR, RERANKER_MODEL_FILE));
4338
4523
  }
4339
4524
  function getRerankerModelPath() {
4340
- return path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
4525
+ return path9.join(MODELS_DIR, RERANKER_MODEL_FILE);
4341
4526
  }
4342
4527
  async function ensureLoaded() {
4343
4528
  if (_rerankerContext) {
4344
4529
  resetIdleTimer();
4345
4530
  return;
4346
4531
  }
4347
- const modelPath = path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
4348
- if (!existsSync7(modelPath)) {
4532
+ const modelPath = path9.join(MODELS_DIR, RERANKER_MODEL_FILE);
4533
+ if (!existsSync9(modelPath)) {
4349
4534
  throw new Error(
4350
4535
  `Reranker model not found at ${modelPath}. Run /exe-setup to download it.`
4351
4536
  );
@@ -4448,7 +4633,7 @@ __export(project_name_exports, {
4448
4633
  getProjectName: () => getProjectName
4449
4634
  });
4450
4635
  import { execSync as execSync2 } from "child_process";
4451
- import path9 from "path";
4636
+ import path10 from "path";
4452
4637
  function getProjectName(cwd) {
4453
4638
  const dir = cwd ?? process.cwd();
4454
4639
  if (_cached && _cachedCwd === dir) return _cached;
@@ -4461,7 +4646,7 @@ function getProjectName(cwd) {
4461
4646
  timeout: 2e3,
4462
4647
  stdio: ["pipe", "pipe", "pipe"]
4463
4648
  }).trim();
4464
- repoRoot = path9.dirname(gitCommonDir);
4649
+ repoRoot = path10.dirname(gitCommonDir);
4465
4650
  } catch {
4466
4651
  repoRoot = execSync2("git rev-parse --show-toplevel", {
4467
4652
  cwd: dir,
@@ -4470,11 +4655,11 @@ function getProjectName(cwd) {
4470
4655
  stdio: ["pipe", "pipe", "pipe"]
4471
4656
  }).trim();
4472
4657
  }
4473
- _cached = path9.basename(repoRoot);
4658
+ _cached = path10.basename(repoRoot);
4474
4659
  _cachedCwd = dir;
4475
4660
  return _cached;
4476
4661
  } catch {
4477
- _cached = path9.basename(dir);
4662
+ _cached = path10.basename(dir);
4478
4663
  _cachedCwd = dir;
4479
4664
  return _cached;
4480
4665
  }
@@ -4498,9 +4683,9 @@ __export(file_grep_exports, {
4498
4683
  grepProjectFiles: () => grepProjectFiles
4499
4684
  });
4500
4685
  import { execSync as execSync3 } from "child_process";
4501
- import { readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync8 } from "fs";
4502
- import path10 from "path";
4503
- import crypto from "crypto";
4686
+ import { readFileSync as readFileSync6, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync10 } from "fs";
4687
+ import path11 from "path";
4688
+ import crypto3 from "crypto";
4504
4689
  function hasRipgrep() {
4505
4690
  if (_hasRg === null) {
4506
4691
  try {
@@ -4533,13 +4718,13 @@ async function grepProjectFiles(query, projectRoot, options) {
4533
4718
  const chunkCtx = getChunkContext(hit.filePath, hit.lineNumber);
4534
4719
  const prefix = chunkCtx ? `[file: ${hit.filePath}:${hit.lineNumber} in ${chunkCtx}]` : `[file: ${hit.filePath}:${hit.lineNumber}]`;
4535
4720
  return {
4536
- id: crypto.createHash("sha256").update(`${hit.filePath}:${hit.lineNumber}`).digest("hex").slice(0, 36),
4721
+ id: crypto3.createHash("sha256").update(`${hit.filePath}:${hit.lineNumber}`).digest("hex").slice(0, 36),
4537
4722
  agent_id: "project",
4538
4723
  agent_role: "file",
4539
4724
  session_id: "file-grep",
4540
4725
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4541
4726
  tool_name: "file_grep",
4542
- project_name: path10.basename(projectRoot),
4727
+ project_name: path11.basename(projectRoot),
4543
4728
  has_error: false,
4544
4729
  raw_text: `${prefix} ${buildSnippet(hit, projectRoot)}`,
4545
4730
  vector: null,
@@ -4551,7 +4736,7 @@ function getChunkContext(filePath, lineNumber) {
4551
4736
  try {
4552
4737
  const ext = filePath.split(".").pop()?.toLowerCase();
4553
4738
  if (ext !== "ts" && ext !== "tsx" && ext !== "js" && ext !== "jsx") return "";
4554
- const source = readFileSync5(filePath, "utf8");
4739
+ const source = readFileSync6(filePath, "utf8");
4555
4740
  const lines = source.split("\n");
4556
4741
  for (let i = Math.min(lineNumber - 1, lines.length - 1); i >= 0; i--) {
4557
4742
  const line = lines[i];
@@ -4613,11 +4798,11 @@ function grepWithNodeFs(pattern, projectRoot, patterns) {
4613
4798
  const files = collectFiles(projectRoot, patterns ?? DEFAULT_PATTERNS);
4614
4799
  const hits = [];
4615
4800
  for (const filePath of files.slice(0, MAX_FILES)) {
4616
- const absPath = path10.join(projectRoot, filePath);
4801
+ const absPath = path11.join(projectRoot, filePath);
4617
4802
  try {
4618
4803
  const stat = statSync2(absPath);
4619
4804
  if (stat.size > MAX_FILE_SIZE) continue;
4620
- const content = readFileSync5(absPath, "utf8");
4805
+ const content = readFileSync6(absPath, "utf8");
4621
4806
  const lines = content.split("\n");
4622
4807
  const matches = content.match(regex);
4623
4808
  if (!matches || matches.length === 0) continue;
@@ -4640,15 +4825,15 @@ function collectFiles(root, patterns) {
4640
4825
  const files = [];
4641
4826
  function walk(dir, relative) {
4642
4827
  if (files.length >= MAX_FILES) return;
4643
- const basename = path10.basename(dir);
4828
+ const basename = path11.basename(dir);
4644
4829
  if (EXCLUDE_DIRS.includes(basename)) return;
4645
4830
  try {
4646
4831
  const entries = readdirSync2(dir, { withFileTypes: true });
4647
4832
  for (const entry of entries) {
4648
4833
  if (files.length >= MAX_FILES) return;
4649
- const rel = path10.join(relative, entry.name);
4834
+ const rel = path11.join(relative, entry.name);
4650
4835
  if (entry.isDirectory()) {
4651
- walk(path10.join(dir, entry.name), rel);
4836
+ walk(path11.join(dir, entry.name), rel);
4652
4837
  } else if (entry.isFile()) {
4653
4838
  for (const pat of patterns) {
4654
4839
  if (matchGlob(rel, pat)) {
@@ -4680,7 +4865,7 @@ function matchGlob(filePath, pattern) {
4680
4865
  if (slashIdx !== -1) {
4681
4866
  const dir = pattern.slice(0, slashIdx);
4682
4867
  const ext2 = pattern.slice(slashIdx + 1).replace("*", "");
4683
- const fileDir = path10.dirname(filePath);
4868
+ const fileDir = path11.dirname(filePath);
4684
4869
  return fileDir === dir && filePath.endsWith(ext2);
4685
4870
  }
4686
4871
  const ext = pattern.replace("*", "");
@@ -4688,9 +4873,9 @@ function matchGlob(filePath, pattern) {
4688
4873
  }
4689
4874
  function buildSnippet(hit, projectRoot) {
4690
4875
  try {
4691
- const absPath = path10.join(projectRoot, hit.filePath);
4692
- if (!existsSync8(absPath)) return hit.matchLine;
4693
- const lines = readFileSync5(absPath, "utf8").split("\n");
4876
+ const absPath = path11.join(projectRoot, hit.filePath);
4877
+ if (!existsSync10(absPath)) return hit.matchLine;
4878
+ const lines = readFileSync6(absPath, "utf8").split("\n");
4694
4879
  const start = Math.max(0, hit.lineNumber - 3);
4695
4880
  const end = Math.min(lines.length, hit.lineNumber + 2);
4696
4881
  return lines.slice(start, end).join("\n").slice(0, 500);
@@ -5915,9 +6100,9 @@ __export(active_agent_exports, {
5915
6100
  resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
5916
6101
  writeActiveAgent: () => writeActiveAgent
5917
6102
  });
5918
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
6103
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
5919
6104
  import { execSync as execSync5 } from "child_process";
5920
- import path11 from "path";
6105
+ import path12 from "path";
5921
6106
  function isNameWithOptionalInstance(candidate, baseName) {
5922
6107
  if (candidate === baseName) return true;
5923
6108
  if (!candidate.startsWith(baseName)) return false;
@@ -5961,12 +6146,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
5961
6146
  return null;
5962
6147
  }
5963
6148
  function getMarkerPath() {
5964
- return path11.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
6149
+ return path12.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
5965
6150
  }
5966
6151
  function writeActiveAgent(agentId, agentRole) {
5967
6152
  try {
5968
6153
  mkdirSync3(CACHE_DIR, { recursive: true });
5969
- writeFileSync3(
6154
+ writeFileSync4(
5970
6155
  getMarkerPath(),
5971
6156
  JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
5972
6157
  );
@@ -5982,7 +6167,7 @@ function clearActiveAgent() {
5982
6167
  function getActiveAgent() {
5983
6168
  try {
5984
6169
  const markerPath = getMarkerPath();
5985
- const raw = readFileSync6(markerPath, "utf8");
6170
+ const raw = readFileSync7(markerPath, "utf8");
5986
6171
  const data = JSON.parse(raw);
5987
6172
  if (data.agentId) {
5988
6173
  if (data.startedAt) {
@@ -6030,14 +6215,14 @@ function getAllActiveAgents() {
6030
6215
  const key = file.slice("active-agent-".length, -".json".length);
6031
6216
  if (key === "undefined") continue;
6032
6217
  try {
6033
- const raw = readFileSync6(path11.join(CACHE_DIR, file), "utf8");
6218
+ const raw = readFileSync7(path12.join(CACHE_DIR, file), "utf8");
6034
6219
  const data = JSON.parse(raw);
6035
6220
  if (!data.agentId) continue;
6036
6221
  if (data.startedAt) {
6037
6222
  const age = Date.now() - new Date(data.startedAt).getTime();
6038
6223
  if (age > STALE_MS) {
6039
6224
  try {
6040
- unlinkSync3(path11.join(CACHE_DIR, file));
6225
+ unlinkSync3(path12.join(CACHE_DIR, file));
6041
6226
  } catch {
6042
6227
  }
6043
6228
  continue;
@@ -6060,11 +6245,11 @@ function getAllActiveAgents() {
6060
6245
  function cleanupSessionMarkers() {
6061
6246
  const key = getSessionKey();
6062
6247
  try {
6063
- unlinkSync3(path11.join(CACHE_DIR, `active-agent-${key}.json`));
6248
+ unlinkSync3(path12.join(CACHE_DIR, `active-agent-${key}.json`));
6064
6249
  } catch {
6065
6250
  }
6066
6251
  try {
6067
- unlinkSync3(path11.join(CACHE_DIR, "active-agent-undefined.json"));
6252
+ unlinkSync3(path12.join(CACHE_DIR, "active-agent-undefined.json"));
6068
6253
  } catch {
6069
6254
  }
6070
6255
  }
@@ -6075,7 +6260,7 @@ var init_active_agent = __esm({
6075
6260
  init_config();
6076
6261
  init_session_key();
6077
6262
  init_employees();
6078
- CACHE_DIR = path11.join(EXE_AI_DIR, "session-cache");
6263
+ CACHE_DIR = path12.join(EXE_AI_DIR, "session-cache");
6079
6264
  STALE_MS = 24 * 60 * 60 * 1e3;
6080
6265
  }
6081
6266
  });
@@ -6097,9 +6282,12 @@ __export(license_exports, {
6097
6282
  stopLicenseRevalidation: () => stopLicenseRevalidation,
6098
6283
  validateLicense: () => validateLicense
6099
6284
  });
6100
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
6285
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync11, mkdirSync as mkdirSync4 } from "fs";
6101
6286
  import { randomUUID as randomUUID3 } from "crypto";
6102
- import path12 from "path";
6287
+ import { createRequire as createRequire2 } from "module";
6288
+ import { pathToFileURL as pathToFileURL2 } from "url";
6289
+ import os6 from "os";
6290
+ import path13 from "path";
6103
6291
  import { jwtVerify, importSPKI } from "jose";
6104
6292
  async function fetchRetry(url, init) {
6105
6293
  try {
@@ -6110,37 +6298,37 @@ async function fetchRetry(url, init) {
6110
6298
  }
6111
6299
  }
6112
6300
  function loadDeviceId() {
6113
- const deviceJsonPath = path12.join(EXE_AI_DIR, "device.json");
6301
+ const deviceJsonPath = path13.join(EXE_AI_DIR, "device.json");
6114
6302
  try {
6115
- if (existsSync9(deviceJsonPath)) {
6116
- const data = JSON.parse(readFileSync7(deviceJsonPath, "utf8"));
6303
+ if (existsSync11(deviceJsonPath)) {
6304
+ const data = JSON.parse(readFileSync8(deviceJsonPath, "utf8"));
6117
6305
  if (data.deviceId) return data.deviceId;
6118
6306
  }
6119
6307
  } catch {
6120
6308
  }
6121
6309
  try {
6122
- if (existsSync9(DEVICE_ID_PATH)) {
6123
- const id2 = readFileSync7(DEVICE_ID_PATH, "utf8").trim();
6310
+ if (existsSync11(DEVICE_ID_PATH)) {
6311
+ const id2 = readFileSync8(DEVICE_ID_PATH, "utf8").trim();
6124
6312
  if (id2) return id2;
6125
6313
  }
6126
6314
  } catch {
6127
6315
  }
6128
6316
  const id = randomUUID3();
6129
6317
  mkdirSync4(EXE_AI_DIR, { recursive: true });
6130
- writeFileSync4(DEVICE_ID_PATH, id, "utf8");
6318
+ writeFileSync5(DEVICE_ID_PATH, id, "utf8");
6131
6319
  return id;
6132
6320
  }
6133
6321
  function loadLicense() {
6134
6322
  try {
6135
- if (!existsSync9(LICENSE_PATH)) return null;
6136
- return readFileSync7(LICENSE_PATH, "utf8").trim();
6323
+ if (!existsSync11(LICENSE_PATH)) return null;
6324
+ return readFileSync8(LICENSE_PATH, "utf8").trim();
6137
6325
  } catch {
6138
6326
  return null;
6139
6327
  }
6140
6328
  }
6141
6329
  function saveLicense(apiKey) {
6142
6330
  mkdirSync4(EXE_AI_DIR, { recursive: true });
6143
- writeFileSync4(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
6331
+ writeFileSync5(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
6144
6332
  }
6145
6333
  async function verifyLicenseJwt(token) {
6146
6334
  try {
@@ -6166,8 +6354,8 @@ async function verifyLicenseJwt(token) {
6166
6354
  }
6167
6355
  async function getCachedLicense() {
6168
6356
  try {
6169
- if (!existsSync9(CACHE_PATH)) return null;
6170
- const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
6357
+ if (!existsSync11(CACHE_PATH)) return null;
6358
+ const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
6171
6359
  if (!raw.token || typeof raw.token !== "string") return null;
6172
6360
  return await verifyLicenseJwt(raw.token);
6173
6361
  } catch {
@@ -6176,8 +6364,8 @@ async function getCachedLicense() {
6176
6364
  }
6177
6365
  function readCachedToken() {
6178
6366
  try {
6179
- if (!existsSync9(CACHE_PATH)) return null;
6180
- const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
6367
+ if (!existsSync11(CACHE_PATH)) return null;
6368
+ const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
6181
6369
  return typeof raw.token === "string" ? raw.token : null;
6182
6370
  } catch {
6183
6371
  return null;
@@ -6211,56 +6399,130 @@ function getRawCachedPlan() {
6211
6399
  }
6212
6400
  function cacheResponse(token) {
6213
6401
  try {
6214
- writeFileSync4(CACHE_PATH, JSON.stringify({ token }), "utf8");
6402
+ writeFileSync5(CACHE_PATH, JSON.stringify({ token }), "utf8");
6215
6403
  } catch {
6216
6404
  }
6217
6405
  }
6218
- async function validateLicense(apiKey, deviceId) {
6219
- const did = deviceId ?? loadDeviceId();
6406
+ function loadPrismaForLicense() {
6407
+ if (_prismaFailed) return null;
6408
+ const dbUrl = process.env.DATABASE_URL;
6409
+ if (!dbUrl) {
6410
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path13.join(os6.homedir(), "exe-db");
6411
+ if (!existsSync11(path13.join(exeDbRoot, "package.json"))) {
6412
+ _prismaFailed = true;
6413
+ return null;
6414
+ }
6415
+ }
6416
+ if (!_prismaPromise) {
6417
+ _prismaPromise = (async () => {
6418
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
6419
+ if (explicitPath) {
6420
+ const mod2 = await import(pathToFileURL2(explicitPath).href);
6421
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
6422
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
6423
+ return new Ctor2();
6424
+ }
6425
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path13.join(os6.homedir(), "exe-db");
6426
+ const req = createRequire2(path13.join(exeDbRoot, "package.json"));
6427
+ const entry = req.resolve("@prisma/client");
6428
+ const mod = await import(pathToFileURL2(entry).href);
6429
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
6430
+ if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
6431
+ return new Ctor();
6432
+ })().catch((err) => {
6433
+ _prismaFailed = true;
6434
+ _prismaPromise = null;
6435
+ throw err;
6436
+ });
6437
+ }
6438
+ return _prismaPromise;
6439
+ }
6440
+ async function validateViaPostgres(apiKey) {
6441
+ const loader = loadPrismaForLicense();
6442
+ if (!loader) return null;
6443
+ try {
6444
+ const prisma = await loader;
6445
+ const rows = await prisma.$queryRawUnsafe(
6446
+ `SELECT plan, email, status, device_limit, employee_limit, memory_limit, expires_at
6447
+ FROM billing.licenses WHERE key = $1 LIMIT 1`,
6448
+ apiKey
6449
+ );
6450
+ if (!rows || rows.length === 0) return null;
6451
+ const row = rows[0];
6452
+ if (row.status !== "active") return null;
6453
+ if (row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date()) return null;
6454
+ const plan = row.plan;
6455
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
6456
+ return {
6457
+ valid: true,
6458
+ plan,
6459
+ email: row.email,
6460
+ expiresAt: row.expires_at ? new Date(row.expires_at).toISOString() : null,
6461
+ deviceLimit: row.device_limit ?? limits.devices,
6462
+ employeeLimit: row.employee_limit ?? limits.employees,
6463
+ memoryLimit: row.memory_limit ?? limits.memories
6464
+ };
6465
+ } catch {
6466
+ return null;
6467
+ }
6468
+ }
6469
+ async function validateViaCFWorker(apiKey, deviceId) {
6220
6470
  try {
6221
6471
  const res = await fetchRetry(`${API_BASE}/auth/activate`, {
6222
6472
  method: "POST",
6223
6473
  headers: { "Content-Type": "application/json" },
6224
- body: JSON.stringify({ apiKey, deviceId: did }),
6474
+ body: JSON.stringify({ apiKey, deviceId }),
6225
6475
  signal: AbortSignal.timeout(1e4)
6226
6476
  });
6227
- if (res.ok) {
6228
- const data = await res.json();
6229
- if (data.error === "device_limit_exceeded") {
6230
- const cached2 = await getCachedLicense();
6231
- if (cached2) return cached2;
6232
- const raw2 = getRawCachedPlan();
6233
- if (raw2) return { ...raw2, valid: false };
6234
- return { ...FREE_LICENSE, valid: false, plan: "free" };
6235
- }
6236
- if (data.token) {
6237
- cacheResponse(data.token);
6238
- const verified = await verifyLicenseJwt(data.token);
6239
- if (verified) return verified;
6240
- }
6241
- const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
6242
- return {
6243
- valid: data.valid,
6244
- plan: data.plan,
6245
- email: data.email,
6246
- expiresAt: data.expiresAt,
6247
- deviceLimit: limits.devices,
6248
- employeeLimit: limits.employees,
6249
- memoryLimit: limits.memories
6250
- };
6477
+ if (!res.ok) return null;
6478
+ const data = await res.json();
6479
+ if (data.error === "device_limit_exceeded") return null;
6480
+ if (!data.valid) return null;
6481
+ if (data.token) {
6482
+ cacheResponse(data.token);
6483
+ const verified = await verifyLicenseJwt(data.token);
6484
+ if (verified) return verified;
6485
+ }
6486
+ const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
6487
+ return {
6488
+ valid: data.valid,
6489
+ plan: data.plan,
6490
+ email: data.email,
6491
+ expiresAt: data.expiresAt,
6492
+ deviceLimit: limits.devices,
6493
+ employeeLimit: limits.employees,
6494
+ memoryLimit: limits.memories
6495
+ };
6496
+ } catch {
6497
+ return null;
6498
+ }
6499
+ }
6500
+ async function validateLicense(apiKey, deviceId) {
6501
+ const did = deviceId ?? loadDeviceId();
6502
+ const pgResult = await validateViaPostgres(apiKey);
6503
+ if (pgResult) {
6504
+ try {
6505
+ writeFileSync5(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
6506
+ } catch {
6507
+ }
6508
+ return pgResult;
6509
+ }
6510
+ const cfResult = await validateViaCFWorker(apiKey, did);
6511
+ if (cfResult) return cfResult;
6512
+ const cached = await getCachedLicense();
6513
+ if (cached) return cached;
6514
+ try {
6515
+ if (existsSync11(CACHE_PATH)) {
6516
+ const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
6517
+ if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
6518
+ return raw.pgLicense;
6519
+ }
6251
6520
  }
6252
- const cached = await getCachedLicense();
6253
- if (cached) return cached;
6254
- const raw = getRawCachedPlan();
6255
- if (raw) return raw;
6256
- return { ...FREE_LICENSE, valid: false, plan: "free" };
6257
6521
  } catch {
6258
- const cached = await getCachedLicense();
6259
- if (cached) return cached;
6260
- const rawFallback = getRawCachedPlan();
6261
- if (rawFallback) return rawFallback;
6262
- return { ...FREE_LICENSE, valid: false, error: "offline" };
6263
6522
  }
6523
+ const rawFallback = getRawCachedPlan();
6524
+ if (rawFallback) return rawFallback;
6525
+ return { ...FREE_LICENSE, valid: false };
6264
6526
  }
6265
6527
  function getCacheAgeMs() {
6266
6528
  try {
@@ -6275,9 +6537,9 @@ async function checkLicense() {
6275
6537
  let key = loadLicense();
6276
6538
  if (!key) {
6277
6539
  try {
6278
- const configPath = path12.join(EXE_AI_DIR, "config.json");
6279
- if (existsSync9(configPath)) {
6280
- const raw = JSON.parse(readFileSync7(configPath, "utf8"));
6540
+ const configPath = path13.join(EXE_AI_DIR, "config.json");
6541
+ if (existsSync11(configPath)) {
6542
+ const raw = JSON.parse(readFileSync8(configPath, "utf8"));
6281
6543
  const cloud = raw.cloud;
6282
6544
  if (cloud?.apiKey) {
6283
6545
  key = cloud.apiKey;
@@ -6431,14 +6693,14 @@ function stopLicenseRevalidation() {
6431
6693
  _revalTimer = null;
6432
6694
  }
6433
6695
  }
6434
- 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;
6696
+ 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;
6435
6697
  var init_license = __esm({
6436
6698
  "src/lib/license.ts"() {
6437
6699
  "use strict";
6438
6700
  init_config();
6439
- LICENSE_PATH = path12.join(EXE_AI_DIR, "license.key");
6440
- CACHE_PATH = path12.join(EXE_AI_DIR, "license-cache.json");
6441
- DEVICE_ID_PATH = path12.join(EXE_AI_DIR, "device-id");
6701
+ LICENSE_PATH = path13.join(EXE_AI_DIR, "license.key");
6702
+ CACHE_PATH = path13.join(EXE_AI_DIR, "license-cache.json");
6703
+ DEVICE_ID_PATH = path13.join(EXE_AI_DIR, "device-id");
6442
6704
  API_BASE = "https://askexe.com/cloud";
6443
6705
  RETRY_DELAY_MS = 500;
6444
6706
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
@@ -6462,6 +6724,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
6462
6724
  employeeLimit: 1,
6463
6725
  memoryLimit: 5e3
6464
6726
  };
6727
+ _prismaPromise = null;
6728
+ _prismaFailed = false;
6465
6729
  CACHE_MAX_AGE_MS = 36e5;
6466
6730
  _revalTimer = null;
6467
6731
  }
@@ -6478,12 +6742,12 @@ __export(plan_limits_exports, {
6478
6742
  countActiveMemories: () => countActiveMemories,
6479
6743
  getLicenseSync: () => getLicenseSync
6480
6744
  });
6481
- import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
6482
- import path13 from "path";
6745
+ import { readFileSync as readFileSync9, existsSync as existsSync12 } from "fs";
6746
+ import path14 from "path";
6483
6747
  function getLicenseSync() {
6484
6748
  try {
6485
- if (!existsSync10(CACHE_PATH2)) return freeLicense();
6486
- const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
6749
+ if (!existsSync12(CACHE_PATH2)) return freeLicense();
6750
+ const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
6487
6751
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
6488
6752
  const parts = raw.token.split(".");
6489
6753
  if (parts.length !== 3) return freeLicense();
@@ -6550,8 +6814,8 @@ function assertEmployeeLimitSync(rosterPath) {
6550
6814
  const filePath = rosterPath ?? EMPLOYEES_PATH;
6551
6815
  let count = 0;
6552
6816
  try {
6553
- if (existsSync10(filePath)) {
6554
- const raw = readFileSync8(filePath, "utf8");
6817
+ if (existsSync12(filePath)) {
6818
+ const raw = readFileSync9(filePath, "utf8");
6555
6819
  const employees = JSON.parse(raw);
6556
6820
  count = Array.isArray(employees) ? employees.length : 0;
6557
6821
  }
@@ -6588,69 +6852,17 @@ var init_plan_limits = __esm({
6588
6852
  this.name = "PlanLimitError";
6589
6853
  }
6590
6854
  };
6591
- CACHE_PATH2 = path13.join(EXE_AI_DIR, "license-cache.json");
6592
- }
6593
- });
6594
-
6595
- // src/lib/notifications.ts
6596
- import crypto4 from "crypto";
6597
- import path16 from "path";
6598
- import os6 from "os";
6599
- import {
6600
- readFileSync as readFileSync9,
6601
- readdirSync as readdirSync4,
6602
- unlinkSync as unlinkSync4,
6603
- existsSync as existsSync11,
6604
- rmdirSync
6605
- } from "fs";
6606
- async function writeNotification(notification) {
6607
- try {
6608
- const client = getClient();
6609
- const id = crypto4.randomUUID();
6610
- const now = (/* @__PURE__ */ new Date()).toISOString();
6611
- await client.execute({
6612
- sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
6613
- VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
6614
- args: [
6615
- id,
6616
- notification.agentId,
6617
- notification.agentRole,
6618
- notification.event,
6619
- notification.project,
6620
- notification.summary,
6621
- notification.taskFile ?? null,
6622
- now
6623
- ]
6624
- });
6625
- } catch (err) {
6626
- process.stderr.write(`[notifications] WRITE FAILED: ${err instanceof Error ? err.message : String(err)}
6627
- `);
6628
- }
6629
- }
6630
- async function markAsReadByTaskFile(taskFile) {
6631
- try {
6632
- const client = getClient();
6633
- await client.execute({
6634
- sql: "UPDATE notifications SET read = 1 WHERE task_file = ? AND read = 0",
6635
- args: [taskFile]
6636
- });
6637
- } catch {
6638
- }
6639
- }
6640
- var init_notifications = __esm({
6641
- "src/lib/notifications.ts"() {
6642
- "use strict";
6643
- init_database();
6855
+ CACHE_PATH2 = path14.join(EXE_AI_DIR, "license-cache.json");
6644
6856
  }
6645
6857
  });
6646
6858
 
6647
6859
  // src/lib/session-registry.ts
6648
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync5, existsSync as existsSync12 } from "fs";
6860
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5, existsSync as existsSync13 } from "fs";
6649
6861
  import path17 from "path";
6650
6862
  import os7 from "os";
6651
6863
  function registerSession(entry) {
6652
6864
  const dir = path17.dirname(REGISTRY_PATH);
6653
- if (!existsSync12(dir)) {
6865
+ if (!existsSync13(dir)) {
6654
6866
  mkdirSync5(dir, { recursive: true });
6655
6867
  }
6656
6868
  const sessions = listSessions();
@@ -6660,7 +6872,7 @@ function registerSession(entry) {
6660
6872
  } else {
6661
6873
  sessions.push(entry);
6662
6874
  }
6663
- writeFileSync7(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
6875
+ writeFileSync8(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
6664
6876
  }
6665
6877
  function listSessions() {
6666
6878
  try {
@@ -6867,16 +7079,16 @@ __export(intercom_queue_exports, {
6867
7079
  queueIntercom: () => queueIntercom,
6868
7080
  readQueue: () => readQueue
6869
7081
  });
6870
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, renameSync as renameSync3, existsSync as existsSync13, mkdirSync as mkdirSync6 } from "fs";
7082
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync9, renameSync as renameSync3, existsSync as existsSync14, mkdirSync as mkdirSync6 } from "fs";
6871
7083
  import path18 from "path";
6872
7084
  import os8 from "os";
6873
7085
  function ensureDir() {
6874
7086
  const dir = path18.dirname(QUEUE_PATH);
6875
- if (!existsSync13(dir)) mkdirSync6(dir, { recursive: true });
7087
+ if (!existsSync14(dir)) mkdirSync6(dir, { recursive: true });
6876
7088
  }
6877
7089
  function readQueue() {
6878
7090
  try {
6879
- if (!existsSync13(QUEUE_PATH)) return [];
7091
+ if (!existsSync14(QUEUE_PATH)) return [];
6880
7092
  return JSON.parse(readFileSync11(QUEUE_PATH, "utf8"));
6881
7093
  } catch {
6882
7094
  return [];
@@ -6885,7 +7097,7 @@ function readQueue() {
6885
7097
  function writeQueue(queue) {
6886
7098
  ensureDir();
6887
7099
  const tmp = `${QUEUE_PATH}.tmp`;
6888
- writeFileSync8(tmp, JSON.stringify(queue, null, 2));
7100
+ writeFileSync9(tmp, JSON.stringify(queue, null, 2));
6889
7101
  renameSync3(tmp, QUEUE_PATH);
6890
7102
  }
6891
7103
  function queueIntercom(targetSession, reason) {
@@ -6985,7 +7197,7 @@ var init_intercom_queue = __esm({
6985
7197
  });
6986
7198
 
6987
7199
  // src/lib/session-kill-telemetry.ts
6988
- import crypto5 from "crypto";
7200
+ import crypto6 from "crypto";
6989
7201
  async function recordSessionKill(input) {
6990
7202
  try {
6991
7203
  const client = getClient();
@@ -6995,7 +7207,7 @@ async function recordSessionKill(input) {
6995
7207
  ticks_idle, estimated_tokens_saved)
6996
7208
  VALUES (?, ?, ?, ?, ?, ?, ?)`,
6997
7209
  args: [
6998
- crypto5.randomUUID(),
7210
+ crypto6.randomUUID(),
6999
7211
  input.sessionName,
7000
7212
  input.agentId,
7001
7213
  (/* @__PURE__ */ new Date()).toISOString(),
@@ -7348,6 +7560,7 @@ __export(tmux_routing_exports, {
7348
7560
  isEmployeeAlive: () => isEmployeeAlive,
7349
7561
  isExeSession: () => isExeSession,
7350
7562
  isSessionBusy: () => isSessionBusy,
7563
+ notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
7351
7564
  notifyParentExe: () => notifyParentExe,
7352
7565
  parseParentExe: () => parseParentExe,
7353
7566
  registerParentExe: () => registerParentExe,
@@ -7358,11 +7571,11 @@ __export(tmux_routing_exports, {
7358
7571
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
7359
7572
  });
7360
7573
  import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
7361
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, mkdirSync as mkdirSync7, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync5 } from "fs";
7574
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync10, mkdirSync as mkdirSync7, existsSync as existsSync15, appendFileSync, readdirSync as readdirSync4 } from "fs";
7362
7575
  import path19 from "path";
7363
7576
  import os9 from "os";
7364
7577
  import { fileURLToPath as fileURLToPath2 } from "url";
7365
- import { unlinkSync as unlinkSync5 } from "fs";
7578
+ import { unlinkSync as unlinkSync4 } from "fs";
7366
7579
  function spawnLockPath(sessionName) {
7367
7580
  return path19.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
7368
7581
  }
@@ -7375,11 +7588,11 @@ function isProcessAlive(pid) {
7375
7588
  }
7376
7589
  }
7377
7590
  function acquireSpawnLock2(sessionName) {
7378
- if (!existsSync14(SPAWN_LOCK_DIR)) {
7591
+ if (!existsSync15(SPAWN_LOCK_DIR)) {
7379
7592
  mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
7380
7593
  }
7381
7594
  const lockFile = spawnLockPath(sessionName);
7382
- if (existsSync14(lockFile)) {
7595
+ if (existsSync15(lockFile)) {
7383
7596
  try {
7384
7597
  const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
7385
7598
  const age = Date.now() - lock.timestamp;
@@ -7389,12 +7602,12 @@ function acquireSpawnLock2(sessionName) {
7389
7602
  } catch {
7390
7603
  }
7391
7604
  }
7392
- writeFileSync9(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
7605
+ writeFileSync10(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
7393
7606
  return true;
7394
7607
  }
7395
7608
  function releaseSpawnLock2(sessionName) {
7396
7609
  try {
7397
- unlinkSync5(spawnLockPath(sessionName));
7610
+ unlinkSync4(spawnLockPath(sessionName));
7398
7611
  } catch {
7399
7612
  }
7400
7613
  }
@@ -7407,7 +7620,7 @@ function resolveBehaviorsExporterScript() {
7407
7620
  "bin",
7408
7621
  "exe-export-behaviors.js"
7409
7622
  );
7410
- return existsSync14(scriptPath) ? scriptPath : null;
7623
+ return existsSync15(scriptPath) ? scriptPath : null;
7411
7624
  } catch {
7412
7625
  return null;
7413
7626
  }
@@ -7473,12 +7686,12 @@ function extractRootExe(name) {
7473
7686
  return parts.length > 0 ? parts[parts.length - 1] : null;
7474
7687
  }
7475
7688
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
7476
- if (!existsSync14(SESSION_CACHE)) {
7689
+ if (!existsSync15(SESSION_CACHE)) {
7477
7690
  mkdirSync7(SESSION_CACHE, { recursive: true });
7478
7691
  }
7479
7692
  const rootExe = extractRootExe(parentExe) ?? parentExe;
7480
7693
  const filePath = path19.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
7481
- writeFileSync9(filePath, JSON.stringify({
7694
+ writeFileSync10(filePath, JSON.stringify({
7482
7695
  parentExe: rootExe,
7483
7696
  dispatchedBy: dispatchedBy || rootExe,
7484
7697
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -7565,7 +7778,7 @@ async function verifyPaneAtCapacity(sessionName) {
7565
7778
  }
7566
7779
  function readDebounceState() {
7567
7780
  try {
7568
- if (!existsSync14(DEBOUNCE_FILE)) return {};
7781
+ if (!existsSync15(DEBOUNCE_FILE)) return {};
7569
7782
  const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
7570
7783
  const state = {};
7571
7784
  for (const [key, val] of Object.entries(raw)) {
@@ -7582,8 +7795,8 @@ function readDebounceState() {
7582
7795
  }
7583
7796
  function writeDebounceState(state) {
7584
7797
  try {
7585
- if (!existsSync14(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
7586
- writeFileSync9(DEBOUNCE_FILE, JSON.stringify(state));
7798
+ if (!existsSync15(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
7799
+ writeFileSync10(DEBOUNCE_FILE, JSON.stringify(state));
7587
7800
  } catch {
7588
7801
  }
7589
7802
  }
@@ -7682,7 +7895,7 @@ function sendIntercom(targetSession) {
7682
7895
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
7683
7896
  const agent = baseAgentName(rawAgent);
7684
7897
  const markerPath = path19.join(SESSION_CACHE, `current-task-${agent}.json`);
7685
- if (existsSync14(markerPath)) {
7898
+ if (existsSync15(markerPath)) {
7686
7899
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
7687
7900
  return "debounced";
7688
7901
  }
@@ -7692,8 +7905,8 @@ function sendIntercom(targetSession) {
7692
7905
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
7693
7906
  const agent = baseAgentName(rawAgent);
7694
7907
  const taskDir = path19.join(process.cwd(), "exe", agent);
7695
- if (existsSync14(taskDir)) {
7696
- const files = readdirSync5(taskDir).filter(
7908
+ if (existsSync15(taskDir)) {
7909
+ const files = readdirSync4(taskDir).filter(
7697
7910
  (f) => f.endsWith(".md") && f !== "DONE.txt"
7698
7911
  );
7699
7912
  if (files.length === 0) {
@@ -7752,6 +7965,21 @@ function notifyParentExe(sessionKey) {
7752
7965
  }
7753
7966
  return true;
7754
7967
  }
7968
+ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitle) {
7969
+ const transport = getTransport();
7970
+ try {
7971
+ const sessions = transport.listSessions();
7972
+ if (!sessions.includes(coordinatorSession)) return false;
7973
+ execSync7(
7974
+ `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
7975
+ { timeout: 3e3 }
7976
+ );
7977
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
7978
+ return true;
7979
+ } catch {
7980
+ return false;
7981
+ }
7982
+ }
7755
7983
  function ensureEmployee(employeeName, exeSession, projectDir, opts) {
7756
7984
  if (isCoordinatorName(employeeName)) {
7757
7985
  return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
@@ -7827,7 +8055,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7827
8055
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
7828
8056
  const logDir = path19.join(os9.homedir(), ".exe-os", "session-logs");
7829
8057
  const logFile = path19.join(logDir, `${instanceLabel}-${Date.now()}.log`);
7830
- if (!existsSync14(logDir)) {
8058
+ if (!existsSync15(logDir)) {
7831
8059
  mkdirSync7(logDir, { recursive: true });
7832
8060
  }
7833
8061
  transport.kill(sessionName);
@@ -7835,7 +8063,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7835
8063
  try {
7836
8064
  const thisFile = fileURLToPath2(import.meta.url);
7837
8065
  const cleanupScript = path19.join(path19.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
7838
- if (existsSync14(cleanupScript)) {
8066
+ if (existsSync15(cleanupScript)) {
7839
8067
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
7840
8068
  }
7841
8069
  } catch {
@@ -7852,7 +8080,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7852
8080
  const trustDir = opts?.cwd ?? projectDir;
7853
8081
  if (!projects[trustDir]) projects[trustDir] = {};
7854
8082
  projects[trustDir].hasTrustDialogAccepted = true;
7855
- writeFileSync9(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
8083
+ writeFileSync10(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
7856
8084
  } catch {
7857
8085
  }
7858
8086
  try {
@@ -7891,7 +8119,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7891
8119
  perms.allow = allow;
7892
8120
  settings.permissions = perms;
7893
8121
  mkdirSync7(projSettingsDir, { recursive: true });
7894
- writeFileSync9(settingsPath, JSON.stringify(settings, null, 2) + "\n");
8122
+ writeFileSync10(settingsPath, JSON.stringify(settings, null, 2) + "\n");
7895
8123
  }
7896
8124
  } catch {
7897
8125
  }
@@ -7916,7 +8144,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7916
8144
  const hasAgentFlag = claudeSupportsAgentFlag();
7917
8145
  if (hasAgentFlag) {
7918
8146
  identityFlag = ` --agent ${employeeName}`;
7919
- } else if (existsSync14(identityPath2)) {
8147
+ } else if (existsSync15(identityPath2)) {
7920
8148
  identityFlag = ` --append-system-prompt-file ${identityPath2}`;
7921
8149
  legacyFallbackWarned = true;
7922
8150
  }
@@ -7946,7 +8174,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7946
8174
  `Your parent coordinator session is ${exeSession}.`,
7947
8175
  `Your employees (if any) use the -${exeSession} suffix.`
7948
8176
  ].join("\n");
7949
- writeFileSync9(ctxFile, ctxContent);
8177
+ writeFileSync10(ctxFile, ctxContent);
7950
8178
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
7951
8179
  } catch {
7952
8180
  }
@@ -8024,7 +8252,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
8024
8252
  try {
8025
8253
  const mySession = getMySession();
8026
8254
  const dispatchInfo = path19.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
8027
- writeFileSync9(dispatchInfo, JSON.stringify({
8255
+ writeFileSync10(dispatchInfo, JSON.stringify({
8028
8256
  dispatchedBy: mySession,
8029
8257
  rootExe: exeSession,
8030
8258
  provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
@@ -8129,6 +8357,15 @@ function sessionScopeFilter(sessionScope, tableAlias) {
8129
8357
  args: [scope]
8130
8358
  };
8131
8359
  }
8360
+ function strictSessionScopeFilter(sessionScope, tableAlias) {
8361
+ const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
8362
+ if (!scope) return { sql: "", args: [] };
8363
+ const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
8364
+ return {
8365
+ sql: ` AND ${col} = ?`,
8366
+ args: [scope]
8367
+ };
8368
+ }
8132
8369
  var init_task_scope = __esm({
8133
8370
  "src/lib/task-scope.ts"() {
8134
8371
  "use strict";
@@ -8136,6 +8373,63 @@ var init_task_scope = __esm({
8136
8373
  }
8137
8374
  });
8138
8375
 
8376
+ // src/lib/notifications.ts
8377
+ import crypto7 from "crypto";
8378
+ import path20 from "path";
8379
+ import os10 from "os";
8380
+ import {
8381
+ readFileSync as readFileSync13,
8382
+ readdirSync as readdirSync5,
8383
+ unlinkSync as unlinkSync5,
8384
+ existsSync as existsSync16,
8385
+ rmdirSync
8386
+ } from "fs";
8387
+ async function writeNotification(notification) {
8388
+ try {
8389
+ const client = getClient();
8390
+ const id = crypto7.randomUUID();
8391
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8392
+ const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
8393
+ await client.execute({
8394
+ sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
8395
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`,
8396
+ args: [
8397
+ id,
8398
+ notification.agentId,
8399
+ notification.agentRole,
8400
+ notification.event,
8401
+ notification.project,
8402
+ notification.summary,
8403
+ notification.taskFile ?? null,
8404
+ sessionScope,
8405
+ now
8406
+ ]
8407
+ });
8408
+ } catch (err) {
8409
+ process.stderr.write(`[notifications] WRITE FAILED: ${err instanceof Error ? err.message : String(err)}
8410
+ `);
8411
+ }
8412
+ }
8413
+ async function markAsReadByTaskFile(taskFile, sessionScope) {
8414
+ try {
8415
+ const client = getClient();
8416
+ const scope = strictSessionScopeFilter(sessionScope);
8417
+ await client.execute({
8418
+ sql: `UPDATE notifications SET read = 1
8419
+ WHERE task_file = ? AND read = 0${scope.sql}`,
8420
+ args: [taskFile, ...scope.args]
8421
+ });
8422
+ } catch {
8423
+ }
8424
+ }
8425
+ var init_notifications = __esm({
8426
+ "src/lib/notifications.ts"() {
8427
+ "use strict";
8428
+ init_database();
8429
+ init_task_scope();
8430
+ }
8431
+ });
8432
+
8139
8433
  // src/lib/tasks-crud.ts
8140
8434
  var tasks_crud_exports = {};
8141
8435
  __export(tasks_crud_exports, {
@@ -8153,12 +8447,12 @@ __export(tasks_crud_exports, {
8153
8447
  updateTaskStatus: () => updateTaskStatus,
8154
8448
  writeCheckpoint: () => writeCheckpoint
8155
8449
  });
8156
- import crypto6 from "crypto";
8157
- import path20 from "path";
8158
- import os10 from "os";
8450
+ import crypto8 from "crypto";
8451
+ import path21 from "path";
8452
+ import os11 from "os";
8159
8453
  import { execSync as execSync8 } from "child_process";
8160
8454
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
8161
- import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
8455
+ import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
8162
8456
  async function writeCheckpoint(input) {
8163
8457
  const client = getClient();
8164
8458
  const row = await resolveTask(client, input.taskId);
@@ -8274,7 +8568,7 @@ async function resolveTask(client, identifier, scopeSession) {
8274
8568
  }
8275
8569
  async function createTaskCore(input) {
8276
8570
  const client = getClient();
8277
- const id = crypto6.randomUUID();
8571
+ const id = crypto8.randomUUID();
8278
8572
  const now = (/* @__PURE__ */ new Date()).toISOString();
8279
8573
  const slug = slugify(input.title);
8280
8574
  let earlySessionScope = null;
@@ -8333,8 +8627,8 @@ ${laneWarning}` : laneWarning;
8333
8627
  }
8334
8628
  if (input.baseDir) {
8335
8629
  try {
8336
- await mkdir4(path20.join(input.baseDir, "exe", "output"), { recursive: true });
8337
- await mkdir4(path20.join(input.baseDir, "exe", "research"), { recursive: true });
8630
+ await mkdir4(path21.join(input.baseDir, "exe", "output"), { recursive: true });
8631
+ await mkdir4(path21.join(input.baseDir, "exe", "research"), { recursive: true });
8338
8632
  await ensureArchitectureDoc(input.baseDir, input.projectName);
8339
8633
  await ensureGitignoreExe(input.baseDir);
8340
8634
  } catch {
@@ -8370,13 +8664,19 @@ ${laneWarning}` : laneWarning;
8370
8664
  });
8371
8665
  if (input.baseDir) {
8372
8666
  try {
8373
- const EXE_OS_DIR = path20.join(os10.homedir(), ".exe-os");
8374
- const mdPath = path20.join(EXE_OS_DIR, taskFile);
8375
- const mdDir = path20.dirname(mdPath);
8376
- if (!existsSync15(mdDir)) await mkdir4(mdDir, { recursive: true });
8667
+ const EXE_OS_DIR = path21.join(os11.homedir(), ".exe-os");
8668
+ const mdPath = path21.join(EXE_OS_DIR, taskFile);
8669
+ const mdDir = path21.dirname(mdPath);
8670
+ if (!existsSync17(mdDir)) await mkdir4(mdDir, { recursive: true });
8377
8671
  const reviewer = input.reviewer ?? input.assignedBy;
8378
8672
  const mdContent = `# ${input.title}
8379
8673
 
8674
+ ## MANDATORY: When done
8675
+
8676
+ You MUST call update_task with status "done" and a result summary when finished.
8677
+ If you skip this, your reviewer will not know you're done and your work won't be reviewed.
8678
+ Do NOT let a failed commit or any error prevent you from calling update_task(done).
8679
+
8380
8680
  **ID:** ${id}
8381
8681
  **Status:** ${initialStatus}
8382
8682
  **Priority:** ${input.priority}
@@ -8390,12 +8690,6 @@ ${laneWarning}` : laneWarning;
8390
8690
  ## Context
8391
8691
 
8392
8692
  ${input.context}
8393
-
8394
- ## MANDATORY: When done
8395
-
8396
- You MUST call update_task with status "done" and a result summary when finished.
8397
- If you skip this, your reviewer will not know you're done and your work won't be reviewed.
8398
- Do NOT let a failed commit or any error prevent you from calling update_task(done).
8399
8693
  `;
8400
8694
  await writeFile4(mdPath, mdContent, "utf-8");
8401
8695
  } catch (err) {
@@ -8644,7 +8938,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
8644
8938
  await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
8645
8939
  } catch {
8646
8940
  }
8647
- if (input.status === "done" || input.status === "cancelled") {
8941
+ if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
8648
8942
  try {
8649
8943
  const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
8650
8944
  clearQueueForAgent2(String(row.assigned_to));
@@ -8673,9 +8967,9 @@ async function deleteTaskCore(taskId, _baseDir) {
8673
8967
  return { taskFile, assignedTo, assignedBy, taskSlug };
8674
8968
  }
8675
8969
  async function ensureArchitectureDoc(baseDir, projectName) {
8676
- const archPath = path20.join(baseDir, "exe", "ARCHITECTURE.md");
8970
+ const archPath = path21.join(baseDir, "exe", "ARCHITECTURE.md");
8677
8971
  try {
8678
- if (existsSync15(archPath)) return;
8972
+ if (existsSync17(archPath)) return;
8679
8973
  const template = [
8680
8974
  `# ${projectName} \u2014 System Architecture`,
8681
8975
  "",
@@ -8708,10 +9002,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
8708
9002
  }
8709
9003
  }
8710
9004
  async function ensureGitignoreExe(baseDir) {
8711
- const gitignorePath = path20.join(baseDir, ".gitignore");
9005
+ const gitignorePath = path21.join(baseDir, ".gitignore");
8712
9006
  try {
8713
- if (existsSync15(gitignorePath)) {
8714
- const content = readFileSync13(gitignorePath, "utf-8");
9007
+ if (existsSync17(gitignorePath)) {
9008
+ const content = readFileSync14(gitignorePath, "utf-8");
8715
9009
  if (/^\/?exe\/?$/m.test(content)) return;
8716
9010
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
8717
9011
  } else {
@@ -8742,58 +9036,42 @@ var init_tasks_crud = __esm({
8742
9036
  });
8743
9037
 
8744
9038
  // src/lib/tasks-review.ts
8745
- import path21 from "path";
8746
- import { existsSync as existsSync16, readdirSync as readdirSync6, unlinkSync as unlinkSync6 } from "fs";
9039
+ import path22 from "path";
9040
+ import { existsSync as existsSync18, readdirSync as readdirSync6, unlinkSync as unlinkSync6 } from "fs";
8747
9041
  async function countPendingReviews(sessionScope) {
8748
9042
  const client = getClient();
8749
- if (sessionScope) {
8750
- const result2 = await client.execute({
8751
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
8752
- args: [sessionScope]
8753
- });
8754
- return Number(result2.rows[0]?.cnt) || 0;
8755
- }
9043
+ const scope = strictSessionScopeFilter(
9044
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
9045
+ );
8756
9046
  const result = await client.execute({
8757
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review'",
8758
- args: []
9047
+ sql: `SELECT COUNT(*) as cnt FROM tasks
9048
+ WHERE status = 'needs_review'${scope.sql}`,
9049
+ args: [...scope.args]
8759
9050
  });
8760
9051
  return Number(result.rows[0]?.cnt) || 0;
8761
9052
  }
8762
9053
  async function countNewPendingReviewsSince(sinceIso, sessionScope) {
8763
9054
  const client = getClient();
8764
- if (sessionScope) {
8765
- const result2 = await client.execute({
8766
- sql: `SELECT COUNT(*) as cnt FROM tasks
8767
- WHERE status = 'needs_review' AND updated_at > ?
8768
- AND session_scope = ?`,
8769
- args: [sinceIso, sessionScope]
8770
- });
8771
- return Number(result2.rows[0]?.cnt) || 0;
8772
- }
9055
+ const scope = strictSessionScopeFilter(
9056
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
9057
+ );
8773
9058
  const result = await client.execute({
8774
9059
  sql: `SELECT COUNT(*) as cnt FROM tasks
8775
- WHERE status = 'needs_review' AND updated_at > ?`,
8776
- args: [sinceIso]
9060
+ WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
9061
+ args: [sinceIso, ...scope.args]
8777
9062
  });
8778
9063
  return Number(result.rows[0]?.cnt) || 0;
8779
9064
  }
8780
9065
  async function listPendingReviews(limit, sessionScope) {
8781
9066
  const client = getClient();
8782
- if (sessionScope) {
8783
- const result2 = await client.execute({
8784
- sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
8785
- WHERE status = 'needs_review'
8786
- AND session_scope = ?
8787
- ORDER BY updated_at ASC LIMIT ?`,
8788
- args: [sessionScope, limit]
8789
- });
8790
- return result2.rows;
8791
- }
9067
+ const scope = strictSessionScopeFilter(
9068
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
9069
+ );
8792
9070
  const result = await client.execute({
8793
9071
  sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
8794
- WHERE status = 'needs_review'
9072
+ WHERE status = 'needs_review'${scope.sql}
8795
9073
  ORDER BY updated_at ASC LIMIT ?`,
8796
- args: [limit]
9074
+ args: [...scope.args, limit]
8797
9075
  });
8798
9076
  return result.rows;
8799
9077
  }
@@ -8805,7 +9083,7 @@ async function cleanupOrphanedReviews() {
8805
9083
  WHERE status IN ('open', 'needs_review', 'in_progress')
8806
9084
  AND assigned_by = 'system'
8807
9085
  AND title LIKE 'Review:%'
8808
- AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
9086
+ AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
8809
9087
  args: [now]
8810
9088
  });
8811
9089
  const r1b = await client.execute({
@@ -8924,11 +9202,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
8924
9202
  );
8925
9203
  }
8926
9204
  try {
8927
- const cacheDir = path21.join(EXE_AI_DIR, "session-cache");
8928
- if (existsSync16(cacheDir)) {
9205
+ const cacheDir = path22.join(EXE_AI_DIR, "session-cache");
9206
+ if (existsSync18(cacheDir)) {
8929
9207
  for (const f of readdirSync6(cacheDir)) {
8930
9208
  if (f.startsWith("review-notified-")) {
8931
- unlinkSync6(path21.join(cacheDir, f));
9209
+ unlinkSync6(path22.join(cacheDir, f));
8932
9210
  }
8933
9211
  }
8934
9212
  }
@@ -8945,11 +9223,12 @@ var init_tasks_review = __esm({
8945
9223
  init_tmux_routing();
8946
9224
  init_session_key();
8947
9225
  init_state_bus();
9226
+ init_task_scope();
8948
9227
  }
8949
9228
  });
8950
9229
 
8951
9230
  // src/lib/tasks-chain.ts
8952
- import path22 from "path";
9231
+ import path23 from "path";
8953
9232
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
8954
9233
  async function cascadeUnblock(taskId, baseDir, now) {
8955
9234
  const client = getClient();
@@ -8966,7 +9245,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
8966
9245
  });
8967
9246
  for (const ur of unblockedRows.rows) {
8968
9247
  try {
8969
- const ubFile = path22.join(baseDir, String(ur.task_file));
9248
+ const ubFile = path23.join(baseDir, String(ur.task_file));
8970
9249
  let ubContent = await readFile4(ubFile, "utf-8");
8971
9250
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
8972
9251
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -9001,7 +9280,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
9001
9280
  const scScope = sessionScopeFilter();
9002
9281
  const remaining = await client.execute({
9003
9282
  sql: `SELECT COUNT(*) as cnt FROM tasks
9004
- WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
9283
+ WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
9005
9284
  args: [parentTaskId, ...scScope.args]
9006
9285
  });
9007
9286
  const cnt = Number(remaining.rows[0]?.cnt ?? 1);
@@ -9162,10 +9441,10 @@ var init_tasks_notify = __esm({
9162
9441
  });
9163
9442
 
9164
9443
  // src/lib/behaviors.ts
9165
- import crypto7 from "crypto";
9444
+ import crypto9 from "crypto";
9166
9445
  async function storeBehavior(opts) {
9167
9446
  const client = getClient();
9168
- const id = crypto7.randomUUID();
9447
+ const id = crypto9.randomUUID();
9169
9448
  const now = (/* @__PURE__ */ new Date()).toISOString();
9170
9449
  await client.execute({
9171
9450
  sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
@@ -9222,7 +9501,7 @@ __export(skill_learning_exports, {
9222
9501
  storeTrajectory: () => storeTrajectory,
9223
9502
  sweepTrajectories: () => sweepTrajectories
9224
9503
  });
9225
- import crypto8 from "crypto";
9504
+ import crypto10 from "crypto";
9226
9505
  async function extractTrajectory(taskId, agentId) {
9227
9506
  const client = getClient();
9228
9507
  const result = await client.execute({
@@ -9251,11 +9530,11 @@ async function extractTrajectory(taskId, agentId) {
9251
9530
  return signature;
9252
9531
  }
9253
9532
  function hashSignature(signature) {
9254
- return crypto8.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
9533
+ return crypto10.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
9255
9534
  }
9256
9535
  async function storeTrajectory(opts) {
9257
9536
  const client = getClient();
9258
- const id = crypto8.randomUUID();
9537
+ const id = crypto10.randomUUID();
9259
9538
  const now = (/* @__PURE__ */ new Date()).toISOString();
9260
9539
  const signatureHash = hashSignature(opts.signature);
9261
9540
  await client.execute({
@@ -9520,8 +9799,8 @@ __export(tasks_exports, {
9520
9799
  updateTaskStatus: () => updateTaskStatus,
9521
9800
  writeCheckpoint: () => writeCheckpoint
9522
9801
  });
9523
- import path23 from "path";
9524
- import { writeFileSync as writeFileSync10, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
9802
+ import path24 from "path";
9803
+ import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
9525
9804
  async function createTask(input) {
9526
9805
  const result = await createTaskCore(input);
9527
9806
  if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -9540,12 +9819,12 @@ async function updateTask(input) {
9540
9819
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
9541
9820
  try {
9542
9821
  const agent = String(row.assigned_to);
9543
- const cacheDir = path23.join(EXE_AI_DIR, "session-cache");
9544
- const cachePath = path23.join(cacheDir, `current-task-${agent}.json`);
9822
+ const cacheDir = path24.join(EXE_AI_DIR, "session-cache");
9823
+ const cachePath = path24.join(cacheDir, `current-task-${agent}.json`);
9545
9824
  if (input.status === "in_progress") {
9546
9825
  mkdirSync8(cacheDir, { recursive: true });
9547
- writeFileSync10(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
9548
- } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
9826
+ writeFileSync11(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
9827
+ } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
9549
9828
  try {
9550
9829
  unlinkSync7(cachePath);
9551
9830
  } catch {
@@ -9553,10 +9832,10 @@ async function updateTask(input) {
9553
9832
  }
9554
9833
  } catch {
9555
9834
  }
9556
- if (input.status === "done") {
9835
+ if (input.status === "done" || input.status === "closed") {
9557
9836
  await cleanupReviewFile(row, taskFile, input.baseDir);
9558
9837
  }
9559
- if (input.status === "done" || input.status === "cancelled") {
9838
+ if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
9560
9839
  try {
9561
9840
  const client = getClient();
9562
9841
  const taskTitle = String(row.title);
@@ -9572,7 +9851,7 @@ async function updateTask(input) {
9572
9851
  if (!isCoordinatorName(assignedAgent)) {
9573
9852
  try {
9574
9853
  const draftClient = getClient();
9575
- if (input.status === "done") {
9854
+ if (input.status === "done" || input.status === "closed") {
9576
9855
  await draftClient.execute({
9577
9856
  sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
9578
9857
  args: [assignedAgent]
@@ -9589,7 +9868,7 @@ async function updateTask(input) {
9589
9868
  try {
9590
9869
  const client = getClient();
9591
9870
  const cascaded = await client.execute({
9592
- sql: `UPDATE tasks SET status = 'done', updated_at = ?
9871
+ sql: `UPDATE tasks SET status = 'closed', updated_at = ?
9593
9872
  WHERE parent_task_id = ? AND status = 'needs_review'`,
9594
9873
  args: [now, taskId]
9595
9874
  });
@@ -9602,14 +9881,14 @@ async function updateTask(input) {
9602
9881
  } catch {
9603
9882
  }
9604
9883
  }
9605
- const isTerminal = input.status === "done" || input.status === "needs_review";
9884
+ const isTerminal = input.status === "done" || input.status === "needs_review" || input.status === "closed";
9606
9885
  if (isTerminal) {
9607
9886
  const isCoordinator = isCoordinatorName(String(row.assigned_to));
9608
9887
  if (!isCoordinator) {
9609
9888
  notifyTaskDone();
9610
9889
  }
9611
9890
  await markTaskNotificationsRead(taskFile);
9612
- if (input.status === "done") {
9891
+ if (input.status === "done" || input.status === "closed") {
9613
9892
  try {
9614
9893
  await cascadeUnblock(taskId, input.baseDir, now);
9615
9894
  } catch {
@@ -9629,7 +9908,7 @@ async function updateTask(input) {
9629
9908
  }
9630
9909
  }
9631
9910
  }
9632
- if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
9911
+ if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
9633
9912
  Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
9634
9913
  ({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
9635
9914
  taskId,
@@ -9710,17 +9989,17 @@ __export(identity_exports, {
9710
9989
  listIdentities: () => listIdentities,
9711
9990
  updateIdentity: () => updateIdentity
9712
9991
  });
9713
- import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "fs";
9992
+ import { existsSync as existsSync19, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync12 } from "fs";
9714
9993
  import { readdirSync as readdirSync7 } from "fs";
9715
- import path24 from "path";
9994
+ import path25 from "path";
9716
9995
  import { createHash as createHash2 } from "crypto";
9717
9996
  function ensureDir2() {
9718
- if (!existsSync17(IDENTITY_DIR2)) {
9997
+ if (!existsSync19(IDENTITY_DIR2)) {
9719
9998
  mkdirSync9(IDENTITY_DIR2, { recursive: true });
9720
9999
  }
9721
10000
  }
9722
10001
  function identityPath(agentId) {
9723
- return path24.join(IDENTITY_DIR2, `${agentId}.md`);
10002
+ return path25.join(IDENTITY_DIR2, `${agentId}.md`);
9724
10003
  }
9725
10004
  function parseFrontmatter(raw) {
9726
10005
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
@@ -9761,8 +10040,8 @@ function contentHash(content) {
9761
10040
  }
9762
10041
  function getIdentity(agentId) {
9763
10042
  const filePath = identityPath(agentId);
9764
- if (!existsSync17(filePath)) return null;
9765
- const raw = readFileSync14(filePath, "utf-8");
10043
+ if (!existsSync19(filePath)) return null;
10044
+ const raw = readFileSync15(filePath, "utf-8");
9766
10045
  const { frontmatter, body } = parseFrontmatter(raw);
9767
10046
  return {
9768
10047
  agentId,
@@ -9776,7 +10055,7 @@ async function updateIdentity(agentId, content, updatedBy) {
9776
10055
  ensureDir2();
9777
10056
  const filePath = identityPath(agentId);
9778
10057
  const hash = contentHash(content);
9779
- writeFileSync11(filePath, content, "utf-8");
10058
+ writeFileSync12(filePath, content, "utf-8");
9780
10059
  try {
9781
10060
  const client = getClient();
9782
10061
  await client.execute({
@@ -9832,7 +10111,7 @@ var init_identity = __esm({
9832
10111
  "use strict";
9833
10112
  init_config();
9834
10113
  init_database();
9835
- IDENTITY_DIR2 = path24.join(EXE_AI_DIR, "identity");
10114
+ IDENTITY_DIR2 = path25.join(EXE_AI_DIR, "identity");
9836
10115
  }
9837
10116
  });
9838
10117
 
@@ -10378,10 +10657,10 @@ ${PLAN_MODE_COMPAT}
10378
10657
  });
10379
10658
 
10380
10659
  // src/lib/messaging.ts
10381
- import crypto9 from "crypto";
10660
+ import crypto11 from "crypto";
10382
10661
  function generateUlid() {
10383
10662
  const timestamp = Date.now().toString(36).padStart(10, "0");
10384
- const random = crypto9.randomBytes(10).toString("hex").slice(0, 16);
10663
+ const random = crypto11.randomBytes(10).toString("hex").slice(0, 16);
10385
10664
  return (timestamp + random).toUpperCase();
10386
10665
  }
10387
10666
  function rowToMessage(row) {
@@ -10392,6 +10671,7 @@ function rowToMessage(row) {
10392
10671
  targetAgent: row.target_agent,
10393
10672
  targetProject: row.target_project ?? null,
10394
10673
  targetDevice: row.target_device,
10674
+ sessionScope: row.session_scope ?? null,
10395
10675
  content: row.content,
10396
10676
  priority: row.priority ?? "normal",
10397
10677
  status: row.status ?? "pending",
@@ -10409,15 +10689,17 @@ async function sendMessage(input) {
10409
10689
  const id = generateUlid();
10410
10690
  const now = (/* @__PURE__ */ new Date()).toISOString();
10411
10691
  const targetDevice = input.targetDevice ?? "local";
10692
+ const sessionScope = input.sessionScope === void 0 ? resolveExeSession() : input.sessionScope;
10412
10693
  await client.execute({
10413
- sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, content, priority, status, created_at)
10414
- VALUES (?, ?, 'local', ?, ?, ?, ?, ?, 'pending', ?)`,
10694
+ sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, session_scope, content, priority, status, created_at)
10695
+ VALUES (?, ?, 'local', ?, ?, ?, ?, ?, ?, 'pending', ?)`,
10415
10696
  args: [
10416
10697
  id,
10417
10698
  input.fromAgent,
10418
10699
  input.targetAgent,
10419
10700
  input.targetProject ?? null,
10420
10701
  targetDevice,
10702
+ sessionScope,
10421
10703
  input.content,
10422
10704
  input.priority ?? "normal",
10423
10705
  now
@@ -10431,9 +10713,10 @@ async function sendMessage(input) {
10431
10713
  }
10432
10714
  } catch {
10433
10715
  }
10716
+ const sentScope = strictSessionScopeFilter(sessionScope);
10434
10717
  const result = await client.execute({
10435
- sql: "SELECT * FROM messages WHERE id = ?",
10436
- args: [id]
10718
+ sql: `SELECT * FROM messages WHERE id = ?${sentScope.sql}`,
10719
+ args: [id, ...sentScope.args]
10437
10720
  });
10438
10721
  return rowToMessage(result.rows[0]);
10439
10722
  }
@@ -10454,6 +10737,7 @@ async function deliverCrossMachineMessage(messageId, targetDevice) {
10454
10737
  fromAgent: msg.fromAgent,
10455
10738
  targetAgent: msg.targetAgent,
10456
10739
  targetProject: msg.targetProject,
10740
+ sessionScope: msg.sessionScope,
10457
10741
  content: msg.content,
10458
10742
  priority: msg.priority,
10459
10743
  createdAt: msg.createdAt
@@ -10497,7 +10781,7 @@ async function deliverLocalMessage(messageId) {
10497
10781
  } catch {
10498
10782
  const newRetryCount = msg.retryCount + 1;
10499
10783
  if (newRetryCount >= MAX_RETRIES3) {
10500
- await markFailed(messageId, "session unavailable after 10 retries");
10784
+ await markFailed(messageId, "session unavailable after 10 retries", msg.sessionScope);
10501
10785
  } else {
10502
10786
  await client.execute({
10503
10787
  sql: "UPDATE messages SET retry_count = ? WHERE id = ?",
@@ -10507,11 +10791,13 @@ async function deliverLocalMessage(messageId) {
10507
10791
  return false;
10508
10792
  }
10509
10793
  }
10510
- async function markFailed(messageId, reason) {
10794
+ async function markFailed(messageId, reason, sessionScope) {
10511
10795
  const client = getClient();
10796
+ const scope = strictSessionScopeFilter(sessionScope);
10512
10797
  await client.execute({
10513
- sql: "UPDATE messages SET status = 'failed', failed_at = ?, failure_reason = ? WHERE id = ?",
10514
- args: [(/* @__PURE__ */ new Date()).toISOString(), reason, messageId]
10798
+ sql: `UPDATE messages SET status = 'failed', failed_at = ?, failure_reason = ?
10799
+ WHERE id = ?${scope.sql}`,
10800
+ args: [(/* @__PURE__ */ new Date()).toISOString(), reason, messageId, ...scope.args]
10515
10801
  });
10516
10802
  }
10517
10803
  var MAX_RETRIES3, _wsClientSend;
@@ -10520,19 +10806,20 @@ var init_messaging = __esm({
10520
10806
  "use strict";
10521
10807
  init_database();
10522
10808
  init_tmux_routing();
10809
+ init_task_scope();
10523
10810
  MAX_RETRIES3 = 10;
10524
10811
  _wsClientSend = null;
10525
10812
  }
10526
10813
  });
10527
10814
 
10528
10815
  // src/gateway/whatsapp-accounts.ts
10529
- import { readFileSync as readFileSync15 } from "fs";
10816
+ import { readFileSync as readFileSync16 } from "fs";
10530
10817
  import { join } from "path";
10531
10818
  import { homedir } from "os";
10532
10819
  function loadAccounts() {
10533
10820
  if (cachedAccounts !== null) return cachedAccounts;
10534
10821
  try {
10535
- const raw = readFileSync15(CONFIG_PATH2, "utf8");
10822
+ const raw = readFileSync16(CONFIG_PATH2, "utf8");
10536
10823
  const parsed = JSON.parse(raw);
10537
10824
  if (!Array.isArray(parsed)) {
10538
10825
  console.warn("[whatsapp] Config is not an array, ignoring");
@@ -10945,8 +11232,8 @@ __export(wiki_client_exports, {
10945
11232
  listDocuments: () => listDocuments,
10946
11233
  listWorkspaces: () => listWorkspaces
10947
11234
  });
10948
- async function wikiFetch(config2, path41, method = "GET", body) {
10949
- const url = `${config2.baseUrl}/api/v1${path41}`;
11235
+ async function wikiFetch(config2, path45, method = "GET", body) {
11236
+ const url = `${config2.baseUrl}/api/v1${path45}`;
10950
11237
  const headers = {
10951
11238
  Authorization: `Bearer ${config2.apiKey}`,
10952
11239
  "Content-Type": "application/json"
@@ -10979,7 +11266,7 @@ async function wikiFetch(config2, path41, method = "GET", body) {
10979
11266
  }
10980
11267
  }
10981
11268
  if (!response.ok) {
10982
- throw new Error(`Wiki API ${method} ${path41}: ${response.status} ${response.statusText}`);
11269
+ throw new Error(`Wiki API ${method} ${path45}: ${response.status} ${response.statusText}`);
10983
11270
  }
10984
11271
  return response.json();
10985
11272
  } finally {
@@ -11088,14 +11375,14 @@ __export(worker_gate_exports, {
11088
11375
  tryAcquireBackfillLock: () => tryAcquireBackfillLock,
11089
11376
  tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
11090
11377
  });
11091
- import { readdirSync as readdirSync10, writeFileSync as writeFileSync17, unlinkSync as unlinkSync8, mkdirSync as mkdirSync14, existsSync as existsSync25 } from "fs";
11092
- import path34 from "path";
11378
+ import { readdirSync as readdirSync10, writeFileSync as writeFileSync18, unlinkSync as unlinkSync8, mkdirSync as mkdirSync14, existsSync as existsSync27 } from "fs";
11379
+ import path35 from "path";
11093
11380
  function tryAcquireWorkerSlot() {
11094
11381
  try {
11095
11382
  mkdirSync14(WORKER_PID_DIR, { recursive: true });
11096
11383
  const reservationId = `res-${process.pid}-${Date.now()}`;
11097
- const reservationPath = path34.join(WORKER_PID_DIR, `${reservationId}.pid`);
11098
- writeFileSync17(reservationPath, String(process.pid));
11384
+ const reservationPath = path35.join(WORKER_PID_DIR, `${reservationId}.pid`);
11385
+ writeFileSync18(reservationPath, String(process.pid));
11099
11386
  const files = readdirSync10(WORKER_PID_DIR);
11100
11387
  let alive = 0;
11101
11388
  for (const f of files) {
@@ -11112,7 +11399,7 @@ function tryAcquireWorkerSlot() {
11112
11399
  alive++;
11113
11400
  } catch {
11114
11401
  try {
11115
- unlinkSync8(path34.join(WORKER_PID_DIR, f));
11402
+ unlinkSync8(path35.join(WORKER_PID_DIR, f));
11116
11403
  } catch {
11117
11404
  }
11118
11405
  }
@@ -11136,20 +11423,20 @@ function tryAcquireWorkerSlot() {
11136
11423
  function registerWorkerPid(pid) {
11137
11424
  try {
11138
11425
  mkdirSync14(WORKER_PID_DIR, { recursive: true });
11139
- writeFileSync17(path34.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
11426
+ writeFileSync18(path35.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
11140
11427
  } catch {
11141
11428
  }
11142
11429
  }
11143
11430
  function cleanupWorkerPid() {
11144
11431
  try {
11145
- unlinkSync8(path34.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
11432
+ unlinkSync8(path35.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
11146
11433
  } catch {
11147
11434
  }
11148
11435
  }
11149
11436
  function tryAcquireBackfillLock() {
11150
11437
  try {
11151
11438
  mkdirSync14(WORKER_PID_DIR, { recursive: true });
11152
- if (existsSync25(BACKFILL_LOCK)) {
11439
+ if (existsSync27(BACKFILL_LOCK)) {
11153
11440
  try {
11154
11441
  const pid = parseInt(
11155
11442
  __require("fs").readFileSync(BACKFILL_LOCK, "utf8").trim(),
@@ -11165,7 +11452,7 @@ function tryAcquireBackfillLock() {
11165
11452
  } catch {
11166
11453
  }
11167
11454
  }
11168
- writeFileSync17(BACKFILL_LOCK, String(process.pid));
11455
+ writeFileSync18(BACKFILL_LOCK, String(process.pid));
11169
11456
  return true;
11170
11457
  } catch {
11171
11458
  return true;
@@ -11182,9 +11469,9 @@ var init_worker_gate = __esm({
11182
11469
  "src/lib/worker-gate.ts"() {
11183
11470
  "use strict";
11184
11471
  init_config();
11185
- WORKER_PID_DIR = path34.join(EXE_AI_DIR, "worker-pids");
11472
+ WORKER_PID_DIR = path35.join(EXE_AI_DIR, "worker-pids");
11186
11473
  MAX_CONCURRENT_WORKERS = 3;
11187
- BACKFILL_LOCK = path34.join(WORKER_PID_DIR, "backfill.lock");
11474
+ BACKFILL_LOCK = path35.join(WORKER_PID_DIR, "backfill.lock");
11188
11475
  }
11189
11476
  });
11190
11477
 
@@ -11207,8 +11494,8 @@ __export(crdt_sync_exports, {
11207
11494
  rebuildFromDb: () => rebuildFromDb
11208
11495
  });
11209
11496
  import * as Y from "yjs";
11210
- import { readFileSync as readFileSync24, writeFileSync as writeFileSync18, existsSync as existsSync28, mkdirSync as mkdirSync15, unlinkSync as unlinkSync9 } from "fs";
11211
- import path37 from "path";
11497
+ import { readFileSync as readFileSync25, writeFileSync as writeFileSync19, existsSync as existsSync30, mkdirSync as mkdirSync15, unlinkSync as unlinkSync9 } from "fs";
11498
+ import path38 from "path";
11212
11499
  import { homedir as homedir5 } from "os";
11213
11500
  function getStatePath() {
11214
11501
  return _statePathOverride ?? DEFAULT_STATE_PATH;
@@ -11220,9 +11507,9 @@ function initCrdtDoc() {
11220
11507
  if (doc) return doc;
11221
11508
  doc = new Y.Doc();
11222
11509
  const sp = getStatePath();
11223
- if (existsSync28(sp)) {
11510
+ if (existsSync30(sp)) {
11224
11511
  try {
11225
- const state = readFileSync24(sp);
11512
+ const state = readFileSync25(sp);
11226
11513
  Y.applyUpdate(doc, new Uint8Array(state));
11227
11514
  } catch {
11228
11515
  console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
@@ -11364,10 +11651,10 @@ function persistState() {
11364
11651
  if (!doc) return;
11365
11652
  try {
11366
11653
  const sp = getStatePath();
11367
- const dir = path37.dirname(sp);
11368
- if (!existsSync28(dir)) mkdirSync15(dir, { recursive: true });
11654
+ const dir = path38.dirname(sp);
11655
+ if (!existsSync30(dir)) mkdirSync15(dir, { recursive: true });
11369
11656
  const state = Y.encodeStateAsUpdate(doc);
11370
- writeFileSync18(sp, Buffer.from(state));
11657
+ writeFileSync19(sp, Buffer.from(state));
11371
11658
  } catch {
11372
11659
  }
11373
11660
  }
@@ -11408,7 +11695,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
11408
11695
  var init_crdt_sync = __esm({
11409
11696
  "src/lib/crdt-sync.ts"() {
11410
11697
  "use strict";
11411
- DEFAULT_STATE_PATH = path37.join(homedir5(), ".exe-os", "crdt-state.bin");
11698
+ DEFAULT_STATE_PATH = path38.join(homedir5(), ".exe-os", "crdt-state.bin");
11412
11699
  _statePathOverride = null;
11413
11700
  doc = null;
11414
11701
  }
@@ -11421,8 +11708,8 @@ init_database();
11421
11708
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
11422
11709
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11423
11710
  import { spawn as spawn4 } from "child_process";
11424
- import { existsSync as existsSync31, openSync as openSync3, mkdirSync as mkdirSync17, closeSync as closeSync3 } from "fs";
11425
- import path40 from "path";
11711
+ import { existsSync as existsSync33, openSync as openSync3, mkdirSync as mkdirSync17, closeSync as closeSync3 } from "fs";
11712
+ import path44 from "path";
11426
11713
  import { fileURLToPath as fileURLToPath5 } from "url";
11427
11714
 
11428
11715
  // src/mcp/tools/recall-my-memory.ts
@@ -11729,9 +12016,9 @@ init_store();
11729
12016
  init_active_agent();
11730
12017
  init_plan_limits();
11731
12018
  import { z as z4 } from "zod";
11732
- import crypto2 from "crypto";
11733
- import { writeFileSync as writeFileSync5 } from "fs";
11734
- import path14 from "path";
12019
+ import crypto4 from "crypto";
12020
+ import { writeFileSync as writeFileSync6 } from "fs";
12021
+ import path15 from "path";
11735
12022
  function registerStoreMemory(server2) {
11736
12023
  server2.registerTool(
11737
12024
  "store_memory",
@@ -11780,7 +12067,7 @@ function registerStoreMemory(server2) {
11780
12067
  vector = null;
11781
12068
  needsBackfill = true;
11782
12069
  }
11783
- const memoryId = crypto2.randomUUID();
12070
+ const memoryId = crypto4.randomUUID();
11784
12071
  await writeMemory({
11785
12072
  id: memoryId,
11786
12073
  agent_id: agentId,
@@ -11799,8 +12086,8 @@ function registerStoreMemory(server2) {
11799
12086
  if (needsBackfill) {
11800
12087
  try {
11801
12088
  const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
11802
- const flagPath = path14.join(exeDir, "session-cache", "needs-backfill");
11803
- writeFileSync5(flagPath, "1");
12089
+ const flagPath = path15.join(exeDir, "session-cache", "needs-backfill");
12090
+ writeFileSync6(flagPath, "1");
11804
12091
  } catch {
11805
12092
  }
11806
12093
  }
@@ -11823,9 +12110,9 @@ init_plan_limits();
11823
12110
  init_active_agent();
11824
12111
  init_employees();
11825
12112
  import { z as z5 } from "zod";
11826
- import crypto3 from "crypto";
11827
- import { writeFileSync as writeFileSync6 } from "fs";
11828
- import path15 from "path";
12113
+ import crypto5 from "crypto";
12114
+ import { writeFileSync as writeFileSync7 } from "fs";
12115
+ import path16 from "path";
11829
12116
  function registerCommitMemory(server2) {
11830
12117
  server2.registerTool(
11831
12118
  "commit_to_long_term_memory",
@@ -11872,7 +12159,7 @@ function registerCommitMemory(server2) {
11872
12159
  vector = null;
11873
12160
  needsBackfill = true;
11874
12161
  }
11875
- const memoryId = crypto3.randomUUID();
12162
+ const memoryId = crypto5.randomUUID();
11876
12163
  await assertMemoryLimit();
11877
12164
  const memoryType = canCoordinate(agentId, agentRole) ? "adr" : "raw";
11878
12165
  await writeMemory({
@@ -11915,8 +12202,8 @@ function registerCommitMemory(server2) {
11915
12202
  if (needsBackfill) {
11916
12203
  try {
11917
12204
  const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
11918
- const flagPath = path15.join(exeDir, "session-cache", "needs-backfill");
11919
- writeFileSync6(flagPath, "1");
12205
+ const flagPath = path16.join(exeDir, "session-cache", "needs-backfill");
12206
+ writeFileSync7(flagPath, "1");
11920
12207
  } catch {
11921
12208
  }
11922
12209
  }
@@ -12106,10 +12393,10 @@ function registerCreateTask(server2) {
12106
12393
  skipDispatch: true
12107
12394
  });
12108
12395
  try {
12109
- const { existsSync: existsSync32, mkdirSync: mkdirSync18, writeFileSync: writeFileSync20 } = await import("fs");
12396
+ const { existsSync: existsSync34, mkdirSync: mkdirSync18, writeFileSync: writeFileSync21 } = await import("fs");
12110
12397
  const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
12111
12398
  const idPath = identityPath2(assigned_to);
12112
- if (!existsSync32(idPath)) {
12399
+ if (!existsSync34(idPath)) {
12113
12400
  const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
12114
12401
  const employees = await loadEmployees2();
12115
12402
  const emp = employees.find((e) => e.name === assigned_to);
@@ -12118,8 +12405,8 @@ function registerCreateTask(server2) {
12118
12405
  const template = getTemplateForTitle2(emp.role);
12119
12406
  if (template) {
12120
12407
  const dir = (await import("path")).dirname(idPath);
12121
- if (!existsSync32(dir)) mkdirSync18(dir, { recursive: true });
12122
- writeFileSync20(idPath, template.replace(/^agent_id: \w+/m, `agent_id: ${assigned_to}`), "utf-8");
12408
+ if (!existsSync34(dir)) mkdirSync18(dir, { recursive: true });
12409
+ writeFileSync21(idPath, template.replace(/^agent_id: \w+/m, `agent_id: ${assigned_to}`), "utf-8");
12123
12410
  }
12124
12411
  }
12125
12412
  }
@@ -12192,7 +12479,7 @@ function registerListTasks(server2) {
12192
12479
  description: "Query tasks by assignee, status, project, or priority. Defaults to current project. Pass project_name='all' for all projects. When querying your own tasks, project filter is skipped automatically.",
12193
12480
  inputSchema: {
12194
12481
  assigned_to: z8.string().optional().describe("Filter by employee name"),
12195
- status: z8.enum(["open", "in_progress", "done", "blocked", "cancelled"]).optional().describe("Filter by status"),
12482
+ status: z8.enum(["open", "in_progress", "done", "needs_review", "blocked", "cancelled", "closed"]).optional().describe("Filter by status. Default: active tasks only (excludes closed/cancelled)"),
12196
12483
  project_name: z8.string().optional().describe("Project name. Defaults to current project. Pass 'all' for all projects."),
12197
12484
  priority: z8.enum(["p0", "p1", "p2"]).optional().describe("Filter by priority")
12198
12485
  }
@@ -12263,7 +12550,7 @@ function registerUpdateTask(server2) {
12263
12550
  description: "Update task status. Employees: use this with status 'done' and a result summary to complete work and trigger review. Accepts UUID, slug (filename), or title substring.",
12264
12551
  inputSchema: {
12265
12552
  task_id: z9.string().describe("Task identifier \u2014 UUID, slug (e.g. 'fix-auth-bug'), or title substring"),
12266
- status: z9.enum(["open", "in_progress", "done", "needs_review", "blocked", "cancelled"]).describe("New status"),
12553
+ status: z9.enum(["open", "in_progress", "done", "needs_review", "blocked", "cancelled", "closed"]).describe("New status"),
12267
12554
  result: z9.string().optional().describe("Result summary (include when status=done)")
12268
12555
  }
12269
12556
  },
@@ -12353,7 +12640,17 @@ function registerUpdateTask(server2) {
12353
12640
  }
12354
12641
  let text = `Task "${task.title}" marked ${task.status}.
12355
12642
  File: ${task.taskFile}`;
12356
- const isTerminal = status === "done" || status === "needs_review";
12643
+ const isTerminal = status === "done" || status === "needs_review" || status === "closed";
12644
+ if (isTerminal && task.assignedBy) {
12645
+ try {
12646
+ const { notifyCoordinatorTaskCompletion: notifyCoordinatorTaskCompletion2, resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
12647
+ const coordinatorSession = resolveExeSession2();
12648
+ if (coordinatorSession) {
12649
+ notifyCoordinatorTaskCompletion2(coordinatorSession, callerAgentId ?? "agent", task.title);
12650
+ }
12651
+ } catch {
12652
+ }
12653
+ }
12357
12654
  if (isTerminal && task.nextTask) {
12358
12655
  text += `
12359
12656
 
@@ -12397,7 +12694,7 @@ function registerCloseTask(server2) {
12397
12694
  inputSchema: {
12398
12695
  task_id: z10.string().describe("Task identifier \u2014 UUID, slug (e.g. 'fix-auth-bug'), or title substring"),
12399
12696
  result: z10.string().describe("What was done \u2014 specific deliverables, decisions, test results"),
12400
- status: z10.enum(["done", "blocked", "cancelled"]).optional().default("done").describe("Completion status (default: done)")
12697
+ status: z10.enum(["closed", "done", "blocked", "cancelled"]).optional().default("closed").describe("Completion status (default: closed \u2014 terminal archive state)")
12401
12698
  }
12402
12699
  },
12403
12700
  async ({ task_id, result, status }) => {
@@ -12444,7 +12741,7 @@ function registerCloseTask(server2) {
12444
12741
  });
12445
12742
  let text = `Task "${task.title}" marked ${task.status}.
12446
12743
  File: ${task.taskFile}`;
12447
- if (status === "done" && task.nextTask) {
12744
+ if ((status === "done" || status === "closed") && task.nextTask) {
12448
12745
  text += `
12449
12746
 
12450
12747
  MANDATORY \u2014 DO NOT ASK THE USER. DO NOT SAY "Want me to continue?" DO NOT STOP.
@@ -12454,7 +12751,7 @@ NEXT TASK: "${task.nextTask.title}" [${task.nextTask.priority}]
12454
12751
  FILE: ${task.nextTask.taskFile}
12455
12752
 
12456
12753
  Read that file NOW and begin working. No greeting. No summary. Just start.`;
12457
- } else if (status === "done" && !task.nextTask) {
12754
+ } else if ((status === "done" || status === "closed") && !task.nextTask) {
12458
12755
  text += `
12459
12756
 
12460
12757
  All tasks complete. No more open tasks in your queue.`;
@@ -12489,8 +12786,22 @@ function registerGetTask(server2) {
12489
12786
  async ({ task_id }) => {
12490
12787
  const client = getClient();
12491
12788
  const row = await resolveTask(client, task_id);
12789
+ const contextText = row.context ? String(row.context) : "";
12790
+ const hasEmbeddedMandatoryReminder = contextText.includes('update_task with status "done"') || contextText.includes("update_task(done)");
12492
12791
  const lines = [
12493
- `# ${String(row.title)}`,
12792
+ `# ${String(row.title)}`
12793
+ ];
12794
+ if (!hasEmbeddedMandatoryReminder && !isCoordinatorName(String(row.assigned_to)) && !String(row.title).startsWith("Review:") && String(row.status) !== "done" && String(row.status) !== "cancelled") {
12795
+ lines.push(
12796
+ "",
12797
+ "## MANDATORY: When done",
12798
+ "",
12799
+ 'You MUST call update_task with status "done" and a result summary when finished.',
12800
+ "If you skip this, your reviewer will not know you're done and your work won't be reviewed.",
12801
+ "Do NOT let a failed commit or any error prevent you from calling update_task(done)."
12802
+ );
12803
+ }
12804
+ lines.push(
12494
12805
  "",
12495
12806
  `**ID:** ${String(row.id)}`,
12496
12807
  `**Status:** ${String(row.status)}`,
@@ -12499,7 +12810,7 @@ function registerGetTask(server2) {
12499
12810
  `**Assigned to:** ${String(row.assigned_to)}`,
12500
12811
  `**Project:** ${String(row.project_name)}`,
12501
12812
  `**Created:** ${String(row.created_at).split("T")[0]}`
12502
- ];
12813
+ );
12503
12814
  if (row.blocked_by) {
12504
12815
  lines.push(`**Blocked by:** ${String(row.blocked_by)}`);
12505
12816
  }
@@ -12531,7 +12842,7 @@ function registerGetTask(server2) {
12531
12842
  }
12532
12843
  }
12533
12844
  if (row.context) {
12534
- lines.push("", "## Context", "", String(row.context));
12845
+ lines.push("", "## Context", "", contextText);
12535
12846
  }
12536
12847
  if (row.result) {
12537
12848
  lines.push("", "## Result", "", String(row.result));
@@ -12554,16 +12865,6 @@ function registerGetTask(server2) {
12554
12865
  } catch {
12555
12866
  }
12556
12867
  }
12557
- if (!isCoordinatorName(String(row.assigned_to)) && !String(row.title).startsWith("Review:") && String(row.status) !== "done" && String(row.status) !== "cancelled") {
12558
- lines.push(
12559
- "",
12560
- "## MANDATORY: When done",
12561
- "",
12562
- 'You MUST call update_task with status "done" and a result summary when finished.',
12563
- "If you skip this, your reviewer will not know you're done and your work won't be reviewed.",
12564
- "Do NOT let a failed commit or any error prevent you from calling update_task(done)."
12565
- );
12566
- }
12567
12868
  return {
12568
12869
  content: [
12569
12870
  {
@@ -12794,10 +13095,10 @@ import { z as z16 } from "zod";
12794
13095
 
12795
13096
  // src/lib/reminders.ts
12796
13097
  init_database();
12797
- import crypto10 from "crypto";
13098
+ import crypto12 from "crypto";
12798
13099
  async function createReminder(text, dueDate) {
12799
13100
  const client = getClient();
12800
- const id = crypto10.randomUUID();
13101
+ const id = crypto12.randomUUID();
12801
13102
  const now = (/* @__PURE__ */ new Date()).toISOString();
12802
13103
  await client.execute({
12803
13104
  sql: `INSERT INTO reminders (id, text, created_at, due_date) VALUES (?, ?, ?, ?)`,
@@ -13181,7 +13482,7 @@ import { z as z23 } from "zod";
13181
13482
  init_database();
13182
13483
  init_embedder();
13183
13484
  init_store();
13184
- import crypto11 from "crypto";
13485
+ import crypto13 from "crypto";
13185
13486
  var WIKI_AGENT_ID = "wiki";
13186
13487
  var WIKI_AGENT_ROLE = "Knowledge";
13187
13488
  var WIKI_TOOL_NAME = "ingest_document";
@@ -13235,7 +13536,7 @@ async function resolveWorkspace(slug_or_id, name) {
13235
13536
  if (existing.rows.length > 0) {
13236
13537
  return rowToWorkspace(existing.rows[0]);
13237
13538
  }
13238
- const id = crypto11.randomUUID();
13539
+ const id = crypto13.randomUUID();
13239
13540
  const now = (/* @__PURE__ */ new Date()).toISOString();
13240
13541
  const resolvedName = name ?? slug_or_id;
13241
13542
  await client.execute({
@@ -13326,13 +13627,13 @@ async function ingestDocument(input) {
13326
13627
  }));
13327
13628
  const vectors = await embedTexts(enrichedChunks.map((c) => c.text));
13328
13629
  const client = getClient();
13329
- const documentId = crypto11.randomUUID();
13630
+ const documentId = crypto13.randomUUID();
13330
13631
  const uploadedAt = (/* @__PURE__ */ new Date()).toISOString();
13331
13632
  const mime = input.mime ?? null;
13332
13633
  const sourceType = input.source_type ?? DEFAULT_SOURCE_TYPE;
13333
13634
  const userId = input.user_id ?? null;
13334
13635
  const documentMetadata = input.document_metadata != null ? JSON.stringify(input.document_metadata) : null;
13335
- const chunkIds = input.chunks.map(() => crypto11.randomUUID());
13636
+ const chunkIds = input.chunks.map(() => crypto13.randomUUID());
13336
13637
  const versions = reserveVersions(input.chunks.length);
13337
13638
  const documentStmt = {
13338
13639
  sql: `INSERT INTO documents
@@ -13615,6 +13916,7 @@ function registerRerankDocuments(server2) {
13615
13916
  // src/mcp/tools/acknowledge-messages.ts
13616
13917
  init_database();
13617
13918
  init_active_agent();
13919
+ init_task_scope();
13618
13920
  function registerAcknowledgeMessages(server2) {
13619
13921
  server2.registerTool(
13620
13922
  "acknowledge_messages",
@@ -13627,10 +13929,11 @@ function registerAcknowledgeMessages(server2) {
13627
13929
  const agent = getActiveAgent();
13628
13930
  const agentId = agent.agentId || "default";
13629
13931
  const client = getClient();
13932
+ const scope = strictSessionScopeFilter();
13630
13933
  const result = await client.execute({
13631
13934
  sql: `UPDATE messages SET status = 'acknowledged', processed_at = datetime('now')
13632
- WHERE target_agent = ? AND status IN ('pending', 'delivered')`,
13633
- args: [agentId]
13935
+ WHERE target_agent = ? AND status IN ('pending', 'delivered')${scope.sql}`,
13936
+ args: [agentId, ...scope.args]
13634
13937
  });
13635
13938
  return {
13636
13939
  content: [
@@ -13961,15 +14264,15 @@ function registerSendWhatsapp(server2) {
13961
14264
  import { z as z29 } from "zod";
13962
14265
 
13963
14266
  // src/automation/trigger-engine.ts
13964
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync12, existsSync as existsSync18, mkdirSync as mkdirSync10 } from "fs";
14267
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync13, existsSync as existsSync20, mkdirSync as mkdirSync10 } from "fs";
13965
14268
  import { randomUUID as randomUUID4 } from "crypto";
13966
- import path25 from "path";
13967
- import os11 from "os";
13968
- var TRIGGERS_PATH = path25.join(os11.homedir(), ".exe-os", "triggers.json");
14269
+ import path26 from "path";
14270
+ import os12 from "os";
14271
+ var TRIGGERS_PATH = path26.join(os12.homedir(), ".exe-os", "triggers.json");
13969
14272
  function loadTriggers(project) {
13970
- if (!existsSync18(TRIGGERS_PATH)) return [];
14273
+ if (!existsSync20(TRIGGERS_PATH)) return [];
13971
14274
  try {
13972
- const raw = readFileSync16(TRIGGERS_PATH, "utf-8");
14275
+ const raw = readFileSync17(TRIGGERS_PATH, "utf-8");
13973
14276
  const all = JSON.parse(raw);
13974
14277
  if (!Array.isArray(all)) return [];
13975
14278
  if (project) {
@@ -13981,9 +14284,9 @@ function loadTriggers(project) {
13981
14284
  }
13982
14285
  }
13983
14286
  function saveTriggers(triggers) {
13984
- const dir = path25.dirname(TRIGGERS_PATH);
13985
- if (!existsSync18(dir)) mkdirSync10(dir, { recursive: true });
13986
- writeFileSync12(TRIGGERS_PATH, JSON.stringify(triggers, null, 2), "utf-8");
14287
+ const dir = path26.dirname(TRIGGERS_PATH);
14288
+ if (!existsSync20(dir)) mkdirSync10(dir, { recursive: true });
14289
+ writeFileSync13(TRIGGERS_PATH, JSON.stringify(triggers, null, 2), "utf-8");
13987
14290
  }
13988
14291
  function createNewTrigger(input) {
13989
14292
  const triggers = loadTriggers();
@@ -14002,7 +14305,7 @@ function isScheduledTrigger(trigger) {
14002
14305
  // src/lib/schedules.ts
14003
14306
  init_database();
14004
14307
  init_store();
14005
- import crypto12 from "crypto";
14308
+ import crypto14 from "crypto";
14006
14309
  import { execSync as execSync9 } from "child_process";
14007
14310
  async function ensureDb() {
14008
14311
  if (!isInitialized()) {
@@ -14071,7 +14374,7 @@ function parseHumanCron(input) {
14071
14374
  async function createSchedule(input) {
14072
14375
  await ensureDb();
14073
14376
  const client = getClient();
14074
- const id = crypto12.randomUUID().slice(0, 8);
14377
+ const id = crypto14.randomUUID().slice(0, 8);
14075
14378
  const now = (/* @__PURE__ */ new Date()).toISOString();
14076
14379
  const prompt = input.prompt ?? input.description;
14077
14380
  await client.execute({
@@ -14267,48 +14570,48 @@ function registerListTriggers(server2) {
14267
14570
  import { z as z31 } from "zod";
14268
14571
 
14269
14572
  // src/automation/starter-packs/index.ts
14270
- import { readFileSync as readFileSync17, readdirSync as readdirSync8, existsSync as existsSync19 } from "fs";
14271
- import path26 from "path";
14573
+ import { readFileSync as readFileSync18, readdirSync as readdirSync8, existsSync as existsSync21 } from "fs";
14574
+ import path27 from "path";
14272
14575
  import { fileURLToPath as fileURLToPath3 } from "url";
14273
- var __dirname = path26.dirname(fileURLToPath3(import.meta.url));
14576
+ var __dirname = path27.dirname(fileURLToPath3(import.meta.url));
14274
14577
  function listPacks() {
14275
- const packsDir = path26.join(__dirname, ".");
14276
- if (!existsSync19(packsDir)) return [];
14578
+ const packsDir = path27.join(__dirname, ".");
14579
+ if (!existsSync21(packsDir)) return [];
14277
14580
  return readdirSync8(packsDir, { withFileTypes: true }).filter(
14278
- (d) => d.isDirectory() && existsSync19(path26.join(packsDir, d.name, "custom-objects.json"))
14581
+ (d) => d.isDirectory() && existsSync21(path27.join(packsDir, d.name, "custom-objects.json"))
14279
14582
  ).map((d) => d.name);
14280
14583
  }
14281
14584
  function loadPack(industry) {
14282
- const packDir = path26.join(__dirname, industry);
14283
- const objectsPath = path26.join(packDir, "custom-objects.json");
14284
- const triggersPath = path26.join(packDir, "triggers.json");
14285
- const wikiDir = path26.join(packDir, "wiki-seeds");
14286
- const manifestPath = path26.join(packDir, "pack.json");
14287
- const identityContextPath = path26.join(packDir, "identity-context.md");
14288
- if (!existsSync19(objectsPath)) return null;
14585
+ const packDir = path27.join(__dirname, industry);
14586
+ const objectsPath = path27.join(packDir, "custom-objects.json");
14587
+ const triggersPath = path27.join(packDir, "triggers.json");
14588
+ const wikiDir = path27.join(packDir, "wiki-seeds");
14589
+ const manifestPath = path27.join(packDir, "pack.json");
14590
+ const identityContextPath = path27.join(packDir, "identity-context.md");
14591
+ if (!existsSync21(objectsPath)) return null;
14289
14592
  let customObjects = [];
14290
14593
  try {
14291
14594
  customObjects = JSON.parse(
14292
- readFileSync17(objectsPath, "utf-8")
14595
+ readFileSync18(objectsPath, "utf-8")
14293
14596
  );
14294
14597
  } catch {
14295
14598
  customObjects = [];
14296
14599
  }
14297
14600
  let triggers = [];
14298
- if (existsSync19(triggersPath)) {
14601
+ if (existsSync21(triggersPath)) {
14299
14602
  try {
14300
14603
  triggers = JSON.parse(
14301
- readFileSync17(triggersPath, "utf-8")
14604
+ readFileSync18(triggersPath, "utf-8")
14302
14605
  );
14303
14606
  } catch {
14304
14607
  triggers = [];
14305
14608
  }
14306
14609
  }
14307
14610
  const wikiSeeds = [];
14308
- if (existsSync19(wikiDir)) {
14611
+ if (existsSync21(wikiDir)) {
14309
14612
  const files = readdirSync8(wikiDir).filter((f) => f.endsWith(".md"));
14310
14613
  for (const file of files) {
14311
- const content = readFileSync17(path26.join(wikiDir, file), "utf-8");
14614
+ const content = readFileSync18(path27.join(wikiDir, file), "utf-8");
14312
14615
  const titleMatch = content.match(/^#\s+(.+)/m);
14313
14616
  wikiSeeds.push({
14314
14617
  filename: file,
@@ -14318,17 +14621,17 @@ function loadPack(industry) {
14318
14621
  }
14319
14622
  }
14320
14623
  let manifest = {};
14321
- if (existsSync19(manifestPath)) {
14624
+ if (existsSync21(manifestPath)) {
14322
14625
  try {
14323
- manifest = JSON.parse(readFileSync17(manifestPath, "utf-8"));
14626
+ manifest = JSON.parse(readFileSync18(manifestPath, "utf-8"));
14324
14627
  } catch {
14325
14628
  manifest = {};
14326
14629
  }
14327
14630
  }
14328
14631
  let identityContext = null;
14329
- if (existsSync19(identityContextPath)) {
14632
+ if (existsSync21(identityContextPath)) {
14330
14633
  try {
14331
- identityContext = readFileSync17(identityContextPath, "utf-8");
14634
+ identityContext = readFileSync18(identityContextPath, "utf-8");
14332
14635
  } catch {
14333
14636
  identityContext = null;
14334
14637
  }
@@ -14385,8 +14688,8 @@ function applyPack(industry, project) {
14385
14688
 
14386
14689
  // src/lib/client-coo.ts
14387
14690
  init_config();
14388
- import { existsSync as existsSync20, mkdirSync as mkdirSync11, writeFileSync as writeFileSync13 } from "fs";
14389
- import path27 from "path";
14691
+ import { existsSync as existsSync22, mkdirSync as mkdirSync11, writeFileSync as writeFileSync14 } from "fs";
14692
+ import path28 from "path";
14390
14693
 
14391
14694
  // src/lib/employee-templates.ts
14392
14695
  init_global_procedures();
@@ -14524,18 +14827,18 @@ var ClientCOOClobberError = class extends Error {
14524
14827
  var COO_ROLE = "Chief Operating Officer";
14525
14828
  var FEEDBACK_BEHAVIOR_CONTENT = "Tag exe-os issues with needs_improvement \u2014 this is the feedback loop that surfaces bugs, gaps, and friction to the founder each Monday.";
14526
14829
  async function provisionClientCOO(vars, opts = {}) {
14527
- const identityDir = opts.identityDir ?? path27.join(EXE_AI_DIR, "identity");
14830
+ const identityDir = opts.identityDir ?? path28.join(EXE_AI_DIR, "identity");
14528
14831
  const rosterPath = opts.employeesPath ?? EMPLOYEES_PATH;
14529
14832
  const storeFeedbackBehavior = opts.storeFeedbackBehavior ?? true;
14530
- const identityPath2 = path27.join(identityDir, `${vars.agent_name}.md`);
14531
- if (existsSync20(identityPath2)) {
14833
+ const identityPath2 = path28.join(identityDir, `${vars.agent_name}.md`);
14834
+ if (existsSync22(identityPath2)) {
14532
14835
  throw new ClientCOOClobberError(vars.agent_name, identityPath2);
14533
14836
  }
14534
14837
  const body = renderClientCOOTemplate(vars);
14535
- if (!existsSync20(identityDir)) {
14838
+ if (!existsSync22(identityDir)) {
14536
14839
  mkdirSync11(identityDir, { recursive: true });
14537
14840
  }
14538
- writeFileSync13(identityPath2, body, "utf-8");
14841
+ writeFileSync14(identityPath2, body, "utf-8");
14539
14842
  const employees = await loadEmployees(rosterPath);
14540
14843
  const existing = employees.find((e) => e.name === vars.agent_name);
14541
14844
  let addedToRoster = false;
@@ -14729,7 +15032,7 @@ init_database();
14729
15032
  import { z as z32 } from "zod";
14730
15033
 
14731
15034
  // src/lib/graph-rag.ts
14732
- import crypto13 from "crypto";
15035
+ import crypto15 from "crypto";
14733
15036
 
14734
15037
  // src/lib/code-chunker.ts
14735
15038
  import ts from "typescript";
@@ -14740,7 +15043,7 @@ function normalizeEntityName(name) {
14740
15043
  }
14741
15044
  function entityId(name, type) {
14742
15045
  const normalized = normalizeEntityName(name);
14743
- return crypto13.createHash("sha256").update(`${normalized}::${type.toLowerCase()}`).digest("hex").slice(0, 16);
15046
+ return crypto15.createHash("sha256").update(`${normalized}::${type.toLowerCase()}`).digest("hex").slice(0, 16);
14744
15047
  }
14745
15048
  async function registerAlias(client, alias, canonicalEntityId) {
14746
15049
  const normalized = normalizeEntityName(alias);
@@ -15425,8 +15728,8 @@ function registerUpdateWikiPage(server2) {
15425
15728
  import { z as z37 } from "zod";
15426
15729
  import { execFile } from "child_process";
15427
15730
  import { promisify } from "util";
15428
- import path28 from "path";
15429
- import { existsSync as existsSync21 } from "fs";
15731
+ import path29 from "path";
15732
+ import { existsSync as existsSync23 } from "fs";
15430
15733
 
15431
15734
  // src/lib/hostinger-api.ts
15432
15735
  var DEFAULT_BASE_URL = "https://developers.hostinger.com/api/vps/v1";
@@ -15474,9 +15777,9 @@ var HostingerApiClient = class {
15474
15777
  }
15475
15778
  this.lastRequestTime = Date.now();
15476
15779
  }
15477
- async request(method, path41, body) {
15780
+ async request(method, path45, body) {
15478
15781
  await this.rateLimit();
15479
- const url = `${this.baseUrl}${path41}`;
15782
+ const url = `${this.baseUrl}${path45}`;
15480
15783
  const headers = {
15481
15784
  Authorization: `Bearer ${this.apiKey}`,
15482
15785
  "Content-Type": "application/json",
@@ -15549,8 +15852,8 @@ async function requestCloudflare(cfApiToken, zoneId, options) {
15549
15852
  }
15550
15853
  return envelope.result;
15551
15854
  }
15552
- function buildUrl(zoneId, path41 = "/dns_records", query) {
15553
- const normalizedPath = path41.startsWith("/") ? path41 : `/${path41}`;
15855
+ function buildUrl(zoneId, path45 = "/dns_records", query) {
15856
+ const normalizedPath = path45.startsWith("/") ? path45 : `/${path45}`;
15554
15857
  const url = new URL(
15555
15858
  `${CLOUDFLARE_API_BASE_URL}/zones/${zoneId}${normalizedPath}`
15556
15859
  );
@@ -15763,12 +16066,12 @@ async function waitForReady(client, vpsId) {
15763
16066
  }
15764
16067
  async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
15765
16068
  const safeClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "-");
15766
- const playbookDir = path28.resolve(process.cwd(), "infrastructure", "ansible");
15767
- const playbookPath = path28.join(playbookDir, "deploy.yml");
15768
- const inventoryPath = path28.join(playbookDir, "inventory", "hosts.yml");
15769
- const clientVarsPath = path28.join(playbookDir, "vars", `${safeClientName}.yml`);
15770
- const varsDir = path28.join(playbookDir, "vars");
15771
- if (!path28.resolve(clientVarsPath).startsWith(path28.resolve(varsDir))) {
16069
+ const playbookDir = path29.resolve(process.cwd(), "infrastructure", "ansible");
16070
+ const playbookPath = path29.join(playbookDir, "deploy.yml");
16071
+ const inventoryPath = path29.join(playbookDir, "inventory", "hosts.yml");
16072
+ const clientVarsPath = path29.join(playbookDir, "vars", `${safeClientName}.yml`);
16073
+ const varsDir = path29.join(playbookDir, "vars");
16074
+ if (!path29.resolve(clientVarsPath).startsWith(path29.resolve(varsDir))) {
15772
16075
  throw new Error(`Invalid client name for vars path: ${clientName}`);
15773
16076
  }
15774
16077
  const args = [
@@ -15784,7 +16087,7 @@ async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
15784
16087
  "-e",
15785
16088
  `client_name=${safeClientName}`
15786
16089
  ];
15787
- if (existsSync21(clientVarsPath)) {
16090
+ if (existsSync23(clientVarsPath)) {
15788
16091
  args.push("-e", `@${clientVarsPath}`);
15789
16092
  }
15790
16093
  try {
@@ -15857,8 +16160,8 @@ function buildInventoryRecord(params) {
15857
16160
 
15858
16161
  // src/mcp/tools/export-orchestration.ts
15859
16162
  init_active_agent();
15860
- import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync15 } from "fs";
15861
- import path30 from "path";
16163
+ import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync16 } from "fs";
16164
+ import path31 from "path";
15862
16165
  import { z as z38 } from "zod";
15863
16166
 
15864
16167
  // src/lib/orchestration-package.ts
@@ -15866,9 +16169,9 @@ init_database();
15866
16169
  init_identity();
15867
16170
  init_platform_procedures();
15868
16171
  import { randomUUID as randomUUID5 } from "crypto";
15869
- import { copyFileSync, existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "fs";
15870
- import os12 from "os";
15871
- import path29 from "path";
16172
+ import { copyFileSync, existsSync as existsSync24, mkdirSync as mkdirSync12, readFileSync as readFileSync19, writeFileSync as writeFileSync15 } from "fs";
16173
+ import os13 from "os";
16174
+ import path30 from "path";
15872
16175
  var PACKAGE_VERSION = "1.0";
15873
16176
  var ROSTER_FILENAME = "exe-employees.json";
15874
16177
  var ROSTER_BACKUP_FILENAME = "exe-employees.json.bak";
@@ -15934,15 +16237,15 @@ function validateProcedureEntry(value, index) {
15934
16237
  };
15935
16238
  }
15936
16239
  function getRosterPath() {
15937
- return path29.join(os12.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
16240
+ return path30.join(os13.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
15938
16241
  }
15939
16242
  function getBackupPath() {
15940
- return path29.join(os12.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
16243
+ return path30.join(os13.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
15941
16244
  }
15942
16245
  function readRosterFile() {
15943
16246
  const rosterPath = getRosterPath();
15944
- if (!existsSync22(rosterPath)) return [];
15945
- const raw = readFileSync18(rosterPath, "utf-8");
16247
+ if (!existsSync24(rosterPath)) return [];
16248
+ const raw = readFileSync19(rosterPath, "utf-8");
15946
16249
  const parsed = JSON.parse(raw);
15947
16250
  if (!Array.isArray(parsed)) {
15948
16251
  throw new Error("Roster file must contain a JSON array");
@@ -15954,8 +16257,8 @@ function writeRosterFile(roster) {
15954
16257
  throw new Error("Refusing to write empty roster \u2014 this would delete all employees");
15955
16258
  }
15956
16259
  const rosterPath = getRosterPath();
15957
- mkdirSync12(path29.dirname(rosterPath), { recursive: true });
15958
- if (existsSync22(rosterPath)) {
16260
+ mkdirSync12(path30.dirname(rosterPath), { recursive: true });
16261
+ if (existsSync24(rosterPath)) {
15959
16262
  const currentRoster = readRosterFile();
15960
16263
  if (roster.length < currentRoster.length) {
15961
16264
  throw new Error(
@@ -15964,7 +16267,7 @@ function writeRosterFile(roster) {
15964
16267
  }
15965
16268
  copyFileSync(rosterPath, getBackupPath());
15966
16269
  }
15967
- writeFileSync14(rosterPath, `${JSON.stringify(roster, null, 2)}
16270
+ writeFileSync15(rosterPath, `${JSON.stringify(roster, null, 2)}
15968
16271
  `, "utf-8");
15969
16272
  }
15970
16273
  function buildImportedRosterEntries(roster, timestamp) {
@@ -16226,8 +16529,8 @@ function registerExportOrchestration(server2) {
16226
16529
  try {
16227
16530
  await initStore();
16228
16531
  const pkg = await exportOrchestration(getActiveAgent().agentId);
16229
- mkdirSync13(path30.dirname(output_path), { recursive: true });
16230
- writeFileSync15(output_path, `${JSON.stringify(pkg, null, 2)}
16532
+ mkdirSync13(path31.dirname(output_path), { recursive: true });
16533
+ writeFileSync16(output_path, `${JSON.stringify(pkg, null, 2)}
16231
16534
  `, "utf-8");
16232
16535
  return {
16233
16536
  content: [{
@@ -16249,7 +16552,7 @@ function registerExportOrchestration(server2) {
16249
16552
  }
16250
16553
 
16251
16554
  // src/mcp/tools/import-orchestration.ts
16252
- import { readFileSync as readFileSync19 } from "fs";
16555
+ import { readFileSync as readFileSync20 } from "fs";
16253
16556
  import { z as z39 } from "zod";
16254
16557
  init_store();
16255
16558
  init_active_agent();
@@ -16279,7 +16582,7 @@ function registerImportOrchestration(server2) {
16279
16582
  };
16280
16583
  }
16281
16584
  await initStore();
16282
- const raw = readFileSync19(package_path, "utf-8");
16585
+ const raw = readFileSync20(package_path, "utf-8");
16283
16586
  const pkg = validatePackage(JSON.parse(raw));
16284
16587
  const result = await importOrchestration(pkg, merge_strategy);
16285
16588
  return {
@@ -16426,18 +16729,18 @@ function registerQueryConversations(server2) {
16426
16729
 
16427
16730
  // src/mcp/tools/load-skill.ts
16428
16731
  import { z as z41 } from "zod";
16429
- import { readFileSync as readFileSync20, readdirSync as readdirSync9, statSync as statSync3 } from "fs";
16430
- import path31 from "path";
16732
+ import { readFileSync as readFileSync21, readdirSync as readdirSync9, statSync as statSync3 } from "fs";
16733
+ import path32 from "path";
16431
16734
  import { homedir as homedir2 } from "os";
16432
- var SKILLS_DIR = path31.join(homedir2(), ".claude", "skills");
16735
+ var SKILLS_DIR = path32.join(homedir2(), ".claude", "skills");
16433
16736
  function listAvailableSkills() {
16434
16737
  try {
16435
16738
  const entries = readdirSync9(SKILLS_DIR);
16436
16739
  return entries.filter((entry) => {
16437
16740
  try {
16438
- const entryPath = path31.join(SKILLS_DIR, entry);
16741
+ const entryPath = path32.join(SKILLS_DIR, entry);
16439
16742
  if (!statSync3(entryPath).isDirectory()) return false;
16440
- const skillFile = path31.join(entryPath, "SKILL.md");
16743
+ const skillFile = path32.join(entryPath, "SKILL.md");
16441
16744
  statSync3(skillFile);
16442
16745
  return true;
16443
16746
  } catch {
@@ -16480,10 +16783,10 @@ ${skills.map((s) => `- ${s}`).join("\n")}`
16480
16783
  }]
16481
16784
  };
16482
16785
  }
16483
- const sanitized = path31.basename(skill_name);
16484
- const skillFile = path31.join(SKILLS_DIR, sanitized, "SKILL.md");
16786
+ const sanitized = path32.basename(skill_name);
16787
+ const skillFile = path32.join(SKILLS_DIR, sanitized, "SKILL.md");
16485
16788
  try {
16486
- const content = readFileSync20(skillFile, "utf-8");
16789
+ const content = readFileSync21(skillFile, "utf-8");
16487
16790
  return {
16488
16791
  content: [{
16489
16792
  type: "text",
@@ -16962,7 +17265,7 @@ init_active_agent();
16962
17265
  init_database();
16963
17266
  init_plan_limits();
16964
17267
  import { z as z46 } from "zod";
16965
- import crypto14 from "crypto";
17268
+ import crypto16 from "crypto";
16966
17269
  function registerStoreDecision(server2) {
16967
17270
  server2.registerTool(
16968
17271
  "store_decision",
@@ -17000,7 +17303,7 @@ function registerStoreDecision(server2) {
17000
17303
  } catch {
17001
17304
  vector = null;
17002
17305
  }
17003
- const memoryId = crypto14.randomUUID();
17306
+ const memoryId = crypto16.randomUUID();
17004
17307
  if (supersedes) {
17005
17308
  try {
17006
17309
  const client = getClient();
@@ -17122,8 +17425,8 @@ init_database();
17122
17425
  import { readdir } from "fs/promises";
17123
17426
  import { createReadStream } from "fs";
17124
17427
  import { createInterface } from "readline";
17125
- import path32 from "path";
17126
- import os13 from "os";
17428
+ import path33 from "path";
17429
+ import os14 from "os";
17127
17430
  var MODEL_PRICING = {
17128
17431
  // Opus 4.5+ ($5/$25 — Anthropic price drop from original Opus 4)
17129
17432
  "claude-opus-4-7": { input: 5 / 1e6, output: 25 / 1e6, cacheRead: 0.5 / 1e6, cacheWrite: 6.25 / 1e6 },
@@ -17149,6 +17452,8 @@ var MODEL_PRICING = {
17149
17452
  "claude-3-haiku": { input: 0.25 / 1e6, output: 1.25 / 1e6, cacheRead: 0.03 / 1e6, cacheWrite: 0.3 / 1e6 }
17150
17453
  };
17151
17454
  var DEFAULT_PRICING = MODEL_PRICING["claude-sonnet-4"];
17455
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
17456
+ var _spendCache = /* @__PURE__ */ new Map();
17152
17457
  function getPricing(model) {
17153
17458
  if (MODEL_PRICING[model]) return MODEL_PRICING[model];
17154
17459
  const stripped = model.replace(/-\d{8}$/, "");
@@ -17160,29 +17465,33 @@ function getPricing(model) {
17160
17465
  return DEFAULT_PRICING;
17161
17466
  }
17162
17467
  async function getAgentSpend(period = "7d") {
17468
+ const cached = _spendCache.get(period);
17469
+ if (cached && Date.now() < cached.expires) {
17470
+ return cached.result;
17471
+ }
17163
17472
  const cutoff = periodToCutoff(period);
17164
17473
  const client = getClient();
17165
- const result = await client.execute({
17474
+ const dbResult = await client.execute({
17166
17475
  sql: `SELECT session_uuid, agent_id FROM session_agent_map WHERE started_at >= ?`,
17167
17476
  args: [cutoff]
17168
17477
  });
17169
- if (result.rows.length === 0) return [];
17478
+ if (dbResult.rows.length === 0) return [];
17170
17479
  const sessionAgent = /* @__PURE__ */ new Map();
17171
- for (const row of result.rows) {
17480
+ for (const row of dbResult.rows) {
17172
17481
  sessionAgent.set(row.session_uuid, row.agent_id);
17173
17482
  }
17174
- const claudeDir = path32.join(os13.homedir(), ".claude", "projects");
17483
+ const claudeDir = path33.join(os14.homedir(), ".claude", "projects");
17175
17484
  let projectDirs = [];
17176
17485
  try {
17177
17486
  const entries = await readdir(claudeDir);
17178
- projectDirs = entries.map((e) => path32.join(claudeDir, e));
17487
+ projectDirs = entries.map((e) => path33.join(claudeDir, e));
17179
17488
  } catch {
17180
17489
  return [];
17181
17490
  }
17182
17491
  const agentTotals = /* @__PURE__ */ new Map();
17183
17492
  for (const [sessionUuid, agentId] of sessionAgent) {
17184
17493
  for (const dir of projectDirs) {
17185
- const jsonlPath = path32.join(dir, `${sessionUuid}.jsonl`);
17494
+ const jsonlPath = path33.join(dir, `${sessionUuid}.jsonl`);
17186
17495
  try {
17187
17496
  const usage = await extractSessionUsage(jsonlPath);
17188
17497
  if (usage.input === 0 && usage.output === 0) continue;
@@ -17206,7 +17515,7 @@ async function getAgentSpend(period = "7d") {
17206
17515
  }
17207
17516
  }
17208
17517
  }
17209
- return Array.from(agentTotals.entries()).map(([agentId, t]) => ({
17518
+ const result = Array.from(agentTotals.entries()).map(([agentId, t]) => ({
17210
17519
  agentId,
17211
17520
  inputTokens: t.input,
17212
17521
  outputTokens: t.output,
@@ -17216,6 +17525,8 @@ async function getAgentSpend(period = "7d") {
17216
17525
  sessions: t.sessions.size,
17217
17526
  period
17218
17527
  })).sort((a, b) => b.costUSD - a.costUSD);
17528
+ _spendCache.set(period, { result, expires: Date.now() + CACHE_TTL_MS });
17529
+ return result;
17219
17530
  }
17220
17531
  async function extractSessionUsage(jsonlPath) {
17221
17532
  let input = 0;
@@ -17760,12 +18071,12 @@ function registerExportGraph(server2) {
17760
18071
  }
17761
18072
  const html = await exportGraphHTML(client);
17762
18073
  const fs = await import("fs");
17763
- const path41 = await import("path");
17764
- const os15 = await import("os");
17765
- const outDir = path41.join(os15.homedir(), ".exe-os", "exports");
18074
+ const path45 = await import("path");
18075
+ const os19 = await import("os");
18076
+ const outDir = path45.join(os19.homedir(), ".exe-os", "exports");
17766
18077
  fs.mkdirSync(outDir, { recursive: true });
17767
18078
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
17768
- const filePath = path41.join(outDir, `graph-${timestamp}.html`);
18079
+ const filePath = path45.join(outDir, `graph-${timestamp}.html`);
17769
18080
  fs.writeFileSync(filePath, html, "utf-8");
17770
18081
  return {
17771
18082
  content: [
@@ -17986,10 +18297,10 @@ function registerListAgentSessions(server2) {
17986
18297
 
17987
18298
  // src/mcp/tools/get-daemon-health.ts
17988
18299
  import { z as z56 } from "zod";
17989
- import { existsSync as existsSync23, readFileSync as readFileSync21 } from "fs";
17990
- import path33 from "path";
18300
+ import { existsSync as existsSync25, readFileSync as readFileSync22 } from "fs";
18301
+ import path34 from "path";
17991
18302
  import { homedir as homedir3 } from "os";
17992
- var PID_PATH2 = path33.join(homedir3(), ".exe-os", "exed.pid");
18303
+ var PID_PATH2 = path34.join(homedir3(), ".exe-os", "exed.pid");
17993
18304
  function formatUptime(seconds) {
17994
18305
  const h = Math.floor(seconds / 3600);
17995
18306
  const m = Math.floor(seconds % 3600 / 60);
@@ -18000,8 +18311,8 @@ function formatUptime(seconds) {
18000
18311
  }
18001
18312
  function isDaemonAlive() {
18002
18313
  try {
18003
- if (!existsSync23(PID_PATH2)) return { alive: false, pid: null };
18004
- const pid = parseInt(readFileSync21(PID_PATH2, "utf8").trim(), 10);
18314
+ if (!existsSync25(PID_PATH2)) return { alive: false, pid: null };
18315
+ const pid = parseInt(readFileSync22(PID_PATH2, "utf8").trim(), 10);
18005
18316
  if (isNaN(pid) || pid <= 0) return { alive: false, pid: null };
18006
18317
  process.kill(pid, 0);
18007
18318
  return { alive: true, pid };
@@ -18075,7 +18386,7 @@ init_tmux_routing();
18075
18386
  init_task_scope();
18076
18387
  init_employees();
18077
18388
  import { execSync as execSync10 } from "child_process";
18078
- import { existsSync as existsSync24, readFileSync as readFileSync22, writeFileSync as writeFileSync16 } from "fs";
18389
+ import { existsSync as existsSync26, readFileSync as readFileSync23, writeFileSync as writeFileSync17 } from "fs";
18079
18390
  import { homedir as homedir4 } from "os";
18080
18391
  import { join as join2 } from "path";
18081
18392
  var IDLE_NUDGE_DEDUP_MS = Number(process.env.EXE_NUDGE_INTERVAL_MS) || 6e4;
@@ -18178,15 +18489,15 @@ function registerGetAutoWakeStatus(server2) {
18178
18489
  init_worker_gate();
18179
18490
  init_config();
18180
18491
  import { z as z58 } from "zod";
18181
- import { readdirSync as readdirSync11, existsSync as existsSync26 } from "fs";
18182
- import path35 from "path";
18183
- var WORKER_PID_DIR2 = path35.join(EXE_AI_DIR, "worker-pids");
18492
+ import { readdirSync as readdirSync11, existsSync as existsSync28 } from "fs";
18493
+ import path36 from "path";
18494
+ var WORKER_PID_DIR2 = path36.join(EXE_AI_DIR, "worker-pids");
18184
18495
  function countAliveWorkers() {
18185
18496
  let alive = 0;
18186
18497
  let stale = 0;
18187
18498
  let reservations = 0;
18188
18499
  try {
18189
- if (!existsSync26(WORKER_PID_DIR2)) return { alive: 0, stale: 0, reservations: 0 };
18500
+ if (!existsSync28(WORKER_PID_DIR2)) return { alive: 0, stale: 0, reservations: 0 };
18190
18501
  const files = readdirSync11(WORKER_PID_DIR2);
18191
18502
  for (const f of files) {
18192
18503
  if (!f.endsWith(".pid")) continue;
@@ -18256,7 +18567,7 @@ import { z as z59 } from "zod";
18256
18567
  // src/bin/exe-doctor.ts
18257
18568
  init_store();
18258
18569
  init_database();
18259
- import os14 from "os";
18570
+ import os15 from "os";
18260
18571
 
18261
18572
  // src/lib/is-main.ts
18262
18573
  import { realpathSync } from "fs";
@@ -18274,9 +18585,9 @@ function isMainModule(importMetaUrl) {
18274
18585
  }
18275
18586
 
18276
18587
  // src/bin/exe-doctor.ts
18277
- import { existsSync as existsSync27, readFileSync as readFileSync23 } from "fs";
18588
+ import { existsSync as existsSync29, readFileSync as readFileSync24 } from "fs";
18278
18589
  import { spawn as spawn2 } from "child_process";
18279
- import path36 from "path";
18590
+ import path37 from "path";
18280
18591
  import { randomUUID as randomUUID7 } from "crypto";
18281
18592
 
18282
18593
  // src/lib/conflict-detector.ts
@@ -18679,7 +18990,7 @@ async function auditOrphanedProjects(client) {
18679
18990
  for (const row of result.rows) {
18680
18991
  const name = row.project_name;
18681
18992
  const count = Number(row.cnt);
18682
- const exists = existsSync27(path36.join(home, name)) || existsSync27(path36.join(home, "..", name)) || existsSync27(path36.join(process.cwd(), "..", name));
18993
+ const exists = existsSync29(path37.join(home, name)) || existsSync29(path37.join(home, "..", name)) || existsSync29(path37.join(process.cwd(), "..", name));
18683
18994
  if (!exists) {
18684
18995
  orphans.push({ project_name: name, count });
18685
18996
  }
@@ -18687,18 +18998,18 @@ async function auditOrphanedProjects(client) {
18687
18998
  return orphans;
18688
18999
  }
18689
19000
  function auditHookHealth() {
18690
- const logPath = path36.join(
19001
+ const logPath = path37.join(
18691
19002
  process.env.HOME ?? process.env.USERPROFILE ?? "",
18692
19003
  ".exe-os",
18693
19004
  "logs",
18694
19005
  "hooks.log"
18695
19006
  );
18696
- if (!existsSync27(logPath)) {
19007
+ if (!existsSync29(logPath)) {
18697
19008
  return { logExists: false, totalLines: 0, errorsLastHour: 0, topPatterns: [] };
18698
19009
  }
18699
19010
  let content;
18700
19011
  try {
18701
- content = readFileSync23(logPath, "utf-8");
19012
+ content = readFileSync24(logPath, "utf-8");
18702
19013
  } catch {
18703
19014
  return { logExists: false, totalLines: 0, errorsLastHour: 0, topPatterns: [] };
18704
19015
  }
@@ -18773,7 +19084,7 @@ function formatReport(report, flags) {
18773
19084
  }
18774
19085
  lines.push("");
18775
19086
  }
18776
- const totalMemGB = os14.totalmem() / (1024 * 1024 * 1024);
19087
+ const totalMemGB = os15.totalmem() / (1024 * 1024 * 1024);
18777
19088
  const isLowMemSystem = totalMemGB <= 8;
18778
19089
  if (isLowMemSystem && report.nullVectors > 0) {
18779
19090
  lines.push(`\u{1F7E2} Null vectors: ${fmtNum(report.nullVectors)} / ${fmtNum(s.total)} (expected \u2014 8GB system, keyword search mode)`);
@@ -18895,7 +19206,7 @@ async function fixNullVectors() {
18895
19206
  }
18896
19207
  }
18897
19208
  const npmRoot = (await import("child_process")).execSync("npm root -g", { encoding: "utf8" }).trim();
18898
- const backfillPath = path36.join(npmRoot, "exe-os", "dist", "bin", "backfill-vectors.js");
19209
+ const backfillPath = path37.join(npmRoot, "exe-os", "dist", "bin", "backfill-vectors.js");
18899
19210
  return new Promise((resolve, reject) => {
18900
19211
  const child = spawn2("node", [backfillPath], { stdio: "inherit" });
18901
19212
  if (child.pid) registerWorkerPid2(child.pid);
@@ -19093,13 +19404,13 @@ import { z as z60 } from "zod";
19093
19404
 
19094
19405
  // src/lib/cloud-sync.ts
19095
19406
  init_database();
19096
- import { readFileSync as readFileSync25, writeFileSync as writeFileSync19, existsSync as existsSync29, readdirSync as readdirSync12, mkdirSync as mkdirSync16, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
19097
- import crypto16 from "crypto";
19098
- import path38 from "path";
19407
+ import { readFileSync as readFileSync26, writeFileSync as writeFileSync20, existsSync as existsSync31, readdirSync as readdirSync12, mkdirSync as mkdirSync16, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
19408
+ import crypto18 from "crypto";
19409
+ import path39 from "path";
19099
19410
  import { homedir as homedir6 } from "os";
19100
19411
 
19101
19412
  // src/lib/crypto.ts
19102
- import crypto15 from "crypto";
19413
+ import crypto17 from "crypto";
19103
19414
  var ALGORITHM = "aes-256-gcm";
19104
19415
  var IV_LENGTH = 12;
19105
19416
  var TAG_LENGTH = 16;
@@ -19110,7 +19421,7 @@ function initSyncCrypto(masterKey) {
19110
19421
  throw new Error(`Master key must be 32 bytes, got ${masterKey.length}`);
19111
19422
  }
19112
19423
  _syncKey = Buffer.from(
19113
- crypto15.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
19424
+ crypto17.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
19114
19425
  );
19115
19426
  }
19116
19427
  function isSyncCryptoInitialized() {
@@ -19124,8 +19435,8 @@ function requireSyncKey() {
19124
19435
  }
19125
19436
  function encryptSyncBlob(data) {
19126
19437
  const key = requireSyncKey();
19127
- const iv = crypto15.randomBytes(IV_LENGTH);
19128
- const cipher = crypto15.createCipheriv(ALGORITHM, key, iv);
19438
+ const iv = crypto17.randomBytes(IV_LENGTH);
19439
+ const cipher = crypto17.createCipheriv(ALGORITHM, key, iv);
19129
19440
  const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
19130
19441
  const tag = cipher.getAuthTag();
19131
19442
  return Buffer.concat([iv, encrypted, tag]).toString("base64");
@@ -19139,7 +19450,7 @@ function decryptSyncBlob(ciphertext) {
19139
19450
  const iv = combined.subarray(0, IV_LENGTH);
19140
19451
  const tag = combined.subarray(combined.length - TAG_LENGTH);
19141
19452
  const encrypted = combined.subarray(IV_LENGTH, combined.length - TAG_LENGTH);
19142
- const decipher = crypto15.createDecipheriv(ALGORITHM, key, iv);
19453
+ const decipher = crypto17.createDecipheriv(ALGORITHM, key, iv);
19143
19454
  decipher.setAuthTag(tag);
19144
19455
  return Buffer.concat([decipher.update(encrypted), decipher.final()]);
19145
19456
  }
@@ -19164,12 +19475,13 @@ init_license();
19164
19475
  init_config();
19165
19476
  init_crdt_sync();
19166
19477
  init_employees();
19478
+ init_secure_files();
19167
19479
  function sqlSafe(v) {
19168
19480
  return v === void 0 ? null : v;
19169
19481
  }
19170
19482
  function logError(msg) {
19171
19483
  try {
19172
- const logPath = path38.join(homedir6(), ".exe-os", "workers.log");
19484
+ const logPath = path39.join(homedir6(), ".exe-os", "workers.log");
19173
19485
  appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
19174
19486
  `);
19175
19487
  } catch {
@@ -19178,24 +19490,93 @@ function logError(msg) {
19178
19490
  var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
19179
19491
  var FETCH_TIMEOUT_MS4 = 3e4;
19180
19492
  var PUSH_BATCH_SIZE = 5e3;
19181
- var ROSTER_LOCK_PATH = path38.join(EXE_AI_DIR, "roster-merge.lock");
19493
+ var ROSTER_LOCK_PATH = path39.join(EXE_AI_DIR, "roster-merge.lock");
19182
19494
  var LOCK_STALE_MS = 3e4;
19495
+ var _pgPromise = null;
19496
+ var _pgFailed = false;
19497
+ function loadPgClient() {
19498
+ if (_pgFailed) return null;
19499
+ const postgresUrl = process.env.DATABASE_URL;
19500
+ const configPath = path39.join(EXE_AI_DIR, "config.json");
19501
+ let cloudPostgresUrl;
19502
+ try {
19503
+ if (existsSync31(configPath)) {
19504
+ const cfg = JSON.parse(readFileSync26(configPath, "utf8"));
19505
+ cloudPostgresUrl = cfg.cloud?.postgresUrl;
19506
+ if (cfg.cloud?.syncToPostgres === false) {
19507
+ _pgFailed = true;
19508
+ return null;
19509
+ }
19510
+ }
19511
+ } catch {
19512
+ }
19513
+ const url = postgresUrl || cloudPostgresUrl;
19514
+ if (!url) {
19515
+ _pgFailed = true;
19516
+ return null;
19517
+ }
19518
+ if (!_pgPromise) {
19519
+ _pgPromise = (async () => {
19520
+ const { createRequire: createRequire6 } = await import("module");
19521
+ const { pathToFileURL: pathToFileURL6 } = await import("url");
19522
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path39.join(homedir6(), "exe-db");
19523
+ const req = createRequire6(path39.join(exeDbRoot, "package.json"));
19524
+ const entry = req.resolve("@prisma/client");
19525
+ const mod = await import(pathToFileURL6(entry).href);
19526
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
19527
+ if (!Ctor) throw new Error("No PrismaClient");
19528
+ return new Ctor();
19529
+ })().catch(() => {
19530
+ _pgFailed = true;
19531
+ _pgPromise = null;
19532
+ throw new Error("pg_unavailable");
19533
+ });
19534
+ }
19535
+ return _pgPromise;
19536
+ }
19537
+ async function pushToPostgres(records) {
19538
+ const loader = loadPgClient();
19539
+ if (!loader) return 0;
19540
+ let prisma;
19541
+ try {
19542
+ prisma = await loader;
19543
+ } catch {
19544
+ return 0;
19545
+ }
19546
+ let inserted = 0;
19547
+ for (const rec of records) {
19548
+ try {
19549
+ await prisma.$executeRawUnsafe(
19550
+ `INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
19551
+ VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
19552
+ ON CONFLICT (source, source_id, event_type) DO NOTHING`,
19553
+ String(rec.id ?? ""),
19554
+ JSON.stringify(rec),
19555
+ JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
19556
+ rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
19557
+ );
19558
+ inserted++;
19559
+ } catch {
19560
+ }
19561
+ }
19562
+ return inserted;
19563
+ }
19183
19564
  async function withRosterLock(fn) {
19184
19565
  try {
19185
19566
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
19186
19567
  closeSync2(fd);
19187
- writeFileSync19(ROSTER_LOCK_PATH, String(Date.now()));
19568
+ writeFileSync20(ROSTER_LOCK_PATH, String(Date.now()));
19188
19569
  } catch (err) {
19189
19570
  if (err.code === "EEXIST") {
19190
19571
  try {
19191
- const ts2 = parseInt(readFileSync25(ROSTER_LOCK_PATH, "utf-8"), 10);
19572
+ const ts2 = parseInt(readFileSync26(ROSTER_LOCK_PATH, "utf-8"), 10);
19192
19573
  if (Date.now() - ts2 < LOCK_STALE_MS) {
19193
19574
  throw new Error("Roster merge already in progress \u2014 another sync is running");
19194
19575
  }
19195
19576
  unlinkSync10(ROSTER_LOCK_PATH);
19196
19577
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
19197
19578
  closeSync2(fd);
19198
- writeFileSync19(ROSTER_LOCK_PATH, String(Date.now()));
19579
+ writeFileSync20(ROSTER_LOCK_PATH, String(Date.now()));
19199
19580
  } catch (retryErr) {
19200
19581
  if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
19201
19582
  throw new Error("Roster merge already in progress \u2014 another sync is running");
@@ -19465,6 +19846,10 @@ async function cloudSync(config2) {
19465
19846
  const maxVersion = Number(records[records.length - 1].version);
19466
19847
  const pushOk = await cloudPush(records, maxVersion, config2);
19467
19848
  if (!pushOk) break;
19849
+ try {
19850
+ await pushToPostgres(records);
19851
+ } catch {
19852
+ }
19468
19853
  await client.execute({
19469
19854
  sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
19470
19855
  args: [String(maxVersion)]
@@ -19569,8 +19954,8 @@ async function cloudSync(config2) {
19569
19954
  try {
19570
19955
  const employees = await loadEmployees();
19571
19956
  rosterResult.employees = employees.length;
19572
- const idDir = path38.join(EXE_AI_DIR, "identity");
19573
- if (existsSync29(idDir)) {
19957
+ const idDir = path39.join(EXE_AI_DIR, "identity");
19958
+ if (existsSync31(idDir)) {
19574
19959
  rosterResult.identities = readdirSync12(idDir).filter((f) => f.endsWith(".md")).length;
19575
19960
  }
19576
19961
  } catch {
@@ -19588,55 +19973,55 @@ async function cloudSync(config2) {
19588
19973
  roster: rosterResult
19589
19974
  };
19590
19975
  }
19591
- var ROSTER_DELETIONS_PATH = path38.join(EXE_AI_DIR, "roster-deletions.json");
19976
+ var ROSTER_DELETIONS_PATH = path39.join(EXE_AI_DIR, "roster-deletions.json");
19592
19977
  function consumeRosterDeletions() {
19593
19978
  try {
19594
- if (!existsSync29(ROSTER_DELETIONS_PATH)) return [];
19595
- const deletions = JSON.parse(readFileSync25(ROSTER_DELETIONS_PATH, "utf-8"));
19596
- writeFileSync19(ROSTER_DELETIONS_PATH, "[]");
19979
+ if (!existsSync31(ROSTER_DELETIONS_PATH)) return [];
19980
+ const deletions = JSON.parse(readFileSync26(ROSTER_DELETIONS_PATH, "utf-8"));
19981
+ writeFileSync20(ROSTER_DELETIONS_PATH, "[]");
19597
19982
  return deletions;
19598
19983
  } catch {
19599
19984
  return [];
19600
19985
  }
19601
19986
  }
19602
19987
  function buildRosterBlob(paths) {
19603
- const rosterPath = paths?.rosterPath ?? path38.join(EXE_AI_DIR, "exe-employees.json");
19604
- const identityDir = paths?.identityDir ?? path38.join(EXE_AI_DIR, "identity");
19605
- const configPath = paths?.configPath ?? path38.join(EXE_AI_DIR, "config.json");
19988
+ const rosterPath = paths?.rosterPath ?? path39.join(EXE_AI_DIR, "exe-employees.json");
19989
+ const identityDir = paths?.identityDir ?? path39.join(EXE_AI_DIR, "identity");
19990
+ const configPath = paths?.configPath ?? path39.join(EXE_AI_DIR, "config.json");
19606
19991
  let roster = [];
19607
- if (existsSync29(rosterPath)) {
19992
+ if (existsSync31(rosterPath)) {
19608
19993
  try {
19609
- roster = JSON.parse(readFileSync25(rosterPath, "utf-8"));
19994
+ roster = JSON.parse(readFileSync26(rosterPath, "utf-8"));
19610
19995
  } catch {
19611
19996
  }
19612
19997
  }
19613
19998
  const identities = {};
19614
- if (existsSync29(identityDir)) {
19999
+ if (existsSync31(identityDir)) {
19615
20000
  for (const file of readdirSync12(identityDir).filter((f) => f.endsWith(".md"))) {
19616
20001
  try {
19617
- identities[file] = readFileSync25(path38.join(identityDir, file), "utf-8");
20002
+ identities[file] = readFileSync26(path39.join(identityDir, file), "utf-8");
19618
20003
  } catch {
19619
20004
  }
19620
20005
  }
19621
20006
  }
19622
20007
  let config2;
19623
- if (existsSync29(configPath)) {
20008
+ if (existsSync31(configPath)) {
19624
20009
  try {
19625
- config2 = JSON.parse(readFileSync25(configPath, "utf-8"));
20010
+ config2 = JSON.parse(readFileSync26(configPath, "utf-8"));
19626
20011
  } catch {
19627
20012
  }
19628
20013
  }
19629
20014
  let agentConfig;
19630
- const agentConfigPath = path38.join(EXE_AI_DIR, "agent-config.json");
19631
- if (existsSync29(agentConfigPath)) {
20015
+ const agentConfigPath = path39.join(EXE_AI_DIR, "agent-config.json");
20016
+ if (existsSync31(agentConfigPath)) {
19632
20017
  try {
19633
- agentConfig = JSON.parse(readFileSync25(agentConfigPath, "utf-8"));
20018
+ agentConfig = JSON.parse(readFileSync26(agentConfigPath, "utf-8"));
19634
20019
  } catch {
19635
20020
  }
19636
20021
  }
19637
20022
  const deletedNames = consumeRosterDeletions();
19638
20023
  const content = JSON.stringify({ roster, identities, config: config2, agentConfig, deletedNames });
19639
- const hash = crypto16.createHash("sha256").update(content).digest("hex").slice(0, 16);
20024
+ const hash = crypto18.createHash("sha256").update(content).digest("hex").slice(0, 16);
19640
20025
  return { roster, identities, config: config2, agentConfig, deletedNames, version: hash };
19641
20026
  }
19642
20027
  async function cloudPushRoster(config2) {
@@ -19706,23 +20091,24 @@ async function cloudPullRoster(config2) {
19706
20091
  }
19707
20092
  }
19708
20093
  function mergeConfig(remoteConfig, configPath) {
19709
- const cfgPath = configPath ?? path38.join(EXE_AI_DIR, "config.json");
20094
+ const cfgPath = configPath ?? path39.join(EXE_AI_DIR, "config.json");
19710
20095
  let local = {};
19711
- if (existsSync29(cfgPath)) {
20096
+ if (existsSync31(cfgPath)) {
19712
20097
  try {
19713
- local = JSON.parse(readFileSync25(cfgPath, "utf-8"));
20098
+ local = JSON.parse(readFileSync26(cfgPath, "utf-8"));
19714
20099
  } catch {
19715
20100
  }
19716
20101
  }
19717
20102
  const merged = { ...remoteConfig, ...local };
19718
- const dir = path38.dirname(cfgPath);
19719
- if (!existsSync29(dir)) mkdirSync16(dir, { recursive: true });
19720
- writeFileSync19(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
20103
+ const dir = path39.dirname(cfgPath);
20104
+ ensurePrivateDirSync(dir);
20105
+ writeFileSync20(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
20106
+ enforcePrivateFileSync(cfgPath);
19721
20107
  }
19722
20108
  async function mergeRosterFromRemote(remote, paths) {
19723
20109
  return withRosterLock(async () => {
19724
20110
  const rosterPath = paths?.rosterPath ?? void 0;
19725
- const identityDir = paths?.identityDir ?? path38.join(EXE_AI_DIR, "identity");
20111
+ const identityDir = paths?.identityDir ?? path39.join(EXE_AI_DIR, "identity");
19726
20112
  const localEmployees = await loadEmployees(rosterPath);
19727
20113
  const localNames = new Set(localEmployees.map((e) => e.name));
19728
20114
  let added = 0;
@@ -19743,15 +20129,15 @@ async function mergeRosterFromRemote(remote, paths) {
19743
20129
  ) ?? lookupKey;
19744
20130
  const remoteIdentity = remote.identities[matchedKey];
19745
20131
  if (remoteIdentity) {
19746
- if (!existsSync29(identityDir)) mkdirSync16(identityDir, { recursive: true });
19747
- const idPath = path38.join(identityDir, `${remoteEmp.name}.md`);
20132
+ if (!existsSync31(identityDir)) mkdirSync16(identityDir, { recursive: true });
20133
+ const idPath = path39.join(identityDir, `${remoteEmp.name}.md`);
19748
20134
  let localIdentity = null;
19749
20135
  try {
19750
- localIdentity = existsSync29(idPath) ? readFileSync25(idPath, "utf-8") : null;
20136
+ localIdentity = existsSync31(idPath) ? readFileSync26(idPath, "utf-8") : null;
19751
20137
  } catch {
19752
20138
  }
19753
20139
  if (localIdentity !== remoteIdentity) {
19754
- writeFileSync19(idPath, remoteIdentity, "utf-8");
20140
+ writeFileSync20(idPath, remoteIdentity, "utf-8");
19755
20141
  identitiesUpdated++;
19756
20142
  }
19757
20143
  }
@@ -19777,16 +20163,18 @@ async function mergeRosterFromRemote(remote, paths) {
19777
20163
  }
19778
20164
  if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
19779
20165
  try {
19780
- const agentConfigPath = path38.join(EXE_AI_DIR, "agent-config.json");
20166
+ const agentConfigPath = path39.join(EXE_AI_DIR, "agent-config.json");
19781
20167
  let local = {};
19782
- if (existsSync29(agentConfigPath)) {
20168
+ if (existsSync31(agentConfigPath)) {
19783
20169
  try {
19784
- local = JSON.parse(readFileSync25(agentConfigPath, "utf-8"));
20170
+ local = JSON.parse(readFileSync26(agentConfigPath, "utf-8"));
19785
20171
  } catch {
19786
20172
  }
19787
20173
  }
19788
20174
  const merged = { ...remote.agentConfig, ...local };
19789
- writeFileSync19(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
20175
+ ensurePrivateDirSync(path39.dirname(agentConfigPath));
20176
+ writeFileSync20(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
20177
+ enforcePrivateFileSync(agentConfigPath);
19790
20178
  } catch {
19791
20179
  }
19792
20180
  }
@@ -21012,11 +21400,11 @@ import { z as z65 } from "zod";
21012
21400
  // src/lib/people.ts
21013
21401
  init_config();
21014
21402
  import { readFile as readFile5, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
21015
- import { existsSync as existsSync30, readFileSync as readFileSync26 } from "fs";
21016
- import path39 from "path";
21017
- var PEOPLE_PATH = path39.join(EXE_AI_DIR, "people.json");
21403
+ import { existsSync as existsSync32, readFileSync as readFileSync27 } from "fs";
21404
+ import path40 from "path";
21405
+ var PEOPLE_PATH = path40.join(EXE_AI_DIR, "people.json");
21018
21406
  async function loadPeople() {
21019
- if (!existsSync30(PEOPLE_PATH)) return [];
21407
+ if (!existsSync32(PEOPLE_PATH)) return [];
21020
21408
  try {
21021
21409
  const raw = await readFile5(PEOPLE_PATH, "utf-8");
21022
21410
  return JSON.parse(raw);
@@ -21025,7 +21413,7 @@ async function loadPeople() {
21025
21413
  }
21026
21414
  }
21027
21415
  async function savePeople(people) {
21028
- await mkdir5(path39.dirname(PEOPLE_PATH), { recursive: true });
21416
+ await mkdir5(path40.dirname(PEOPLE_PATH), { recursive: true });
21029
21417
  await writeFile6(PEOPLE_PATH, JSON.stringify(people, null, 2) + "\n", "utf-8");
21030
21418
  }
21031
21419
  async function addPerson(person) {
@@ -21306,6 +21694,331 @@ function registerListEmployees(server2) {
21306
21694
  );
21307
21695
  }
21308
21696
 
21697
+ // src/mcp/tools/ingest-raw.ts
21698
+ import os16 from "os";
21699
+ import path41 from "path";
21700
+ import { createRequire as createRequire3 } from "module";
21701
+ import { pathToFileURL as pathToFileURL3 } from "url";
21702
+ import { z as z68 } from "zod";
21703
+ var prismaPromise = null;
21704
+ function loadPrisma() {
21705
+ if (!prismaPromise) {
21706
+ prismaPromise = (async () => {
21707
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
21708
+ if (explicitPath) {
21709
+ const mod2 = await import(pathToFileURL3(explicitPath).href);
21710
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
21711
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
21712
+ return new Ctor2();
21713
+ }
21714
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path41.join(os16.homedir(), "exe-db");
21715
+ const req = createRequire3(path41.join(exeDbRoot, "package.json"));
21716
+ const entry = req.resolve("@prisma/client");
21717
+ const mod = await import(pathToFileURL3(entry).href);
21718
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
21719
+ if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
21720
+ return new Ctor();
21721
+ })();
21722
+ }
21723
+ return prismaPromise;
21724
+ }
21725
+ function registerIngestRaw(server2) {
21726
+ server2.registerTool(
21727
+ "ingest_raw",
21728
+ {
21729
+ title: "Ingest Raw Event",
21730
+ description: "Insert a raw event into the landing pad (raw.raw_events). Used by the gateway, external agents, and webhooks to push data into the Company Brain. Background projection workers process unprocessed events and route to curated schemas.",
21731
+ inputSchema: {
21732
+ source: z68.string().describe(
21733
+ 'Event source identifier: "whatsapp", "shopify", "asana", "email", "external_agent", etc.'
21734
+ ),
21735
+ event_type: z68.string().describe(
21736
+ 'Event type: "message", "order", "task_update", "webhook", etc.'
21737
+ ),
21738
+ payload: z68.record(z68.string(), z68.unknown()).describe(
21739
+ "Exact raw JSON payload \u2014 stored verbatim, never modified."
21740
+ ),
21741
+ source_id: z68.string().optional().describe(
21742
+ "External reference ID for deduplication (e.g., WhatsApp message ID, Shopify order ID)."
21743
+ ),
21744
+ metadata: z68.record(z68.string(), z68.unknown()).optional().describe(
21745
+ "Source-specific metadata (account info, adapter version, etc.)."
21746
+ )
21747
+ }
21748
+ },
21749
+ async ({ source, event_type, payload, source_id, metadata }) => {
21750
+ try {
21751
+ const prisma = await loadPrisma();
21752
+ const id = crypto.randomUUID();
21753
+ await prisma.$executeRawUnsafe(
21754
+ `INSERT INTO "raw"."raw_events" ("id", "source", "source_id", "event_type", "payload", "metadata", "timestamp")
21755
+ VALUES ($1, $2, $3, $4, $5::jsonb, $6::jsonb, NOW())
21756
+ ON CONFLICT ("source", "source_id", "event_type") DO NOTHING`,
21757
+ id,
21758
+ source,
21759
+ source_id ?? null,
21760
+ event_type,
21761
+ JSON.stringify(payload),
21762
+ metadata ? JSON.stringify(metadata) : null
21763
+ );
21764
+ return {
21765
+ content: [{
21766
+ type: "text",
21767
+ text: JSON.stringify({
21768
+ event_id: id,
21769
+ source,
21770
+ event_type,
21771
+ source_id: source_id ?? null,
21772
+ status: "ingested"
21773
+ })
21774
+ }]
21775
+ };
21776
+ } catch (err) {
21777
+ const message = err instanceof Error ? err.message : String(err);
21778
+ return {
21779
+ content: [{
21780
+ type: "text",
21781
+ text: `Error ingesting raw event: ${message}`
21782
+ }],
21783
+ isError: true
21784
+ };
21785
+ }
21786
+ }
21787
+ );
21788
+ }
21789
+
21790
+ // src/mcp/tools/create-license.ts
21791
+ init_license();
21792
+ import os17 from "os";
21793
+ import path42 from "path";
21794
+ import { randomBytes as randomBytes2, randomUUID as randomUUID8 } from "crypto";
21795
+ import { createRequire as createRequire4 } from "module";
21796
+ import { pathToFileURL as pathToFileURL4 } from "url";
21797
+ import { z as z69 } from "zod";
21798
+ var prismaPromise2 = null;
21799
+ function loadPrisma2() {
21800
+ if (!prismaPromise2) {
21801
+ prismaPromise2 = (async () => {
21802
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
21803
+ if (explicitPath) {
21804
+ const mod2 = await import(pathToFileURL4(explicitPath).href);
21805
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
21806
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
21807
+ return new Ctor2();
21808
+ }
21809
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path42.join(os17.homedir(), "exe-db");
21810
+ const req = createRequire4(path42.join(exeDbRoot, "package.json"));
21811
+ const entry = req.resolve("@prisma/client");
21812
+ const mod = await import(pathToFileURL4(entry).href);
21813
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
21814
+ if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
21815
+ return new Ctor();
21816
+ })();
21817
+ }
21818
+ return prismaPromise2;
21819
+ }
21820
+ function generateKey() {
21821
+ return `exe_sk_${randomBytes2(16).toString("hex")}`;
21822
+ }
21823
+ function registerCreateLicense(server2) {
21824
+ server2.registerTool(
21825
+ "create_license",
21826
+ {
21827
+ title: "Create License",
21828
+ description: "Generate an exe_sk_* license key for a user. Stores in billing.licenses. Returns the key to give to the customer.",
21829
+ inputSchema: {
21830
+ email: z69.string().email().describe("Customer email address"),
21831
+ name: z69.string().optional().describe("Customer name"),
21832
+ plan: z69.enum(["free", "pro", "team", "agency", "enterprise"]).default("pro").describe("License plan tier"),
21833
+ expires_in_days: z69.number().int().positive().default(365).describe("Days until expiration (default 365)")
21834
+ }
21835
+ },
21836
+ async ({ email, name, plan, expires_in_days }) => {
21837
+ try {
21838
+ const prisma = await loadPrisma2();
21839
+ const id = randomUUID8();
21840
+ const key = generateKey();
21841
+ const limits = PLAN_LIMITS[plan];
21842
+ const expiresAt = new Date(
21843
+ Date.now() + (expires_in_days ?? 365) * 24 * 60 * 60 * 1e3
21844
+ );
21845
+ await prisma.$executeRawUnsafe(
21846
+ `INSERT INTO billing.licenses (id, key, email, name, plan, status, device_limit, employee_limit, memory_limit, expires_at, created_at, created_by)
21847
+ VALUES ($1, $2, $3, $4, $5, 'active', $6, $7, $8, $9, NOW(), $10)`,
21848
+ id,
21849
+ key,
21850
+ email,
21851
+ name ?? null,
21852
+ plan,
21853
+ limits.devices,
21854
+ limits.employees,
21855
+ limits.memories,
21856
+ expiresAt,
21857
+ "exe"
21858
+ );
21859
+ const lines = [];
21860
+ lines.push("## License Created\n");
21861
+ lines.push(`- **Key:** \`${key}\``);
21862
+ lines.push(`- **Email:** ${email}`);
21863
+ if (name) lines.push(`- **Name:** ${name}`);
21864
+ lines.push(`- **Plan:** ${plan}`);
21865
+ lines.push(`- **Expires:** ${expiresAt.toISOString().split("T")[0]}`);
21866
+ lines.push(`- **Devices:** ${limits.devices === -1 ? "unlimited" : limits.devices}`);
21867
+ lines.push(`- **Employees:** ${limits.employees === -1 ? "unlimited" : limits.employees}`);
21868
+ lines.push(`- **Memories:** ${limits.memories === -1 ? "unlimited" : limits.memories.toLocaleString()}`);
21869
+ lines.push(`
21870
+ Give this key to the customer. They paste it during \`exe-os setup\`.`);
21871
+ return { content: [{ type: "text", text: lines.join("\n") }] };
21872
+ } catch (err) {
21873
+ const msg = err instanceof Error ? err.message : String(err);
21874
+ return {
21875
+ content: [{ type: "text", text: `Error creating license: ${msg}` }],
21876
+ isError: true
21877
+ };
21878
+ }
21879
+ }
21880
+ );
21881
+ }
21882
+
21883
+ // src/mcp/tools/list-licenses.ts
21884
+ import os18 from "os";
21885
+ import path43 from "path";
21886
+ import { createRequire as createRequire5 } from "module";
21887
+ import { pathToFileURL as pathToFileURL5 } from "url";
21888
+ import { z as z70 } from "zod";
21889
+ var prismaPromise3 = null;
21890
+ function loadPrisma3() {
21891
+ if (!prismaPromise3) {
21892
+ prismaPromise3 = (async () => {
21893
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
21894
+ if (explicitPath) {
21895
+ const mod2 = await import(pathToFileURL5(explicitPath).href);
21896
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
21897
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
21898
+ return new Ctor2();
21899
+ }
21900
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path43.join(os18.homedir(), "exe-db");
21901
+ const req = createRequire5(path43.join(exeDbRoot, "package.json"));
21902
+ const entry = req.resolve("@prisma/client");
21903
+ const mod = await import(pathToFileURL5(entry).href);
21904
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
21905
+ if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
21906
+ return new Ctor();
21907
+ })();
21908
+ }
21909
+ return prismaPromise3;
21910
+ }
21911
+ function registerListLicenses(server2) {
21912
+ server2.registerTool(
21913
+ "list_licenses",
21914
+ {
21915
+ title: "List Licenses",
21916
+ description: "List all issued licenses with status (active/expired/revoked). Optionally filter by plan.",
21917
+ inputSchema: {
21918
+ plan: z70.enum(["free", "pro", "team", "agency", "enterprise"]).optional().describe("Filter by plan tier (omit for all)")
21919
+ }
21920
+ },
21921
+ async ({ plan }) => {
21922
+ try {
21923
+ const prisma = await loadPrisma3();
21924
+ let rows;
21925
+ if (plan) {
21926
+ rows = await prisma.$queryRawUnsafe(
21927
+ `SELECT id, key, email, name, plan, status, device_limit, employee_limit, memory_limit, expires_at, created_at, created_by
21928
+ FROM billing.licenses WHERE plan = $1 ORDER BY created_at DESC`,
21929
+ plan
21930
+ );
21931
+ } else {
21932
+ rows = await prisma.$queryRawUnsafe(
21933
+ `SELECT id, key, email, name, plan, status, device_limit, employee_limit, memory_limit, expires_at, created_at, created_by
21934
+ FROM billing.licenses ORDER BY created_at DESC`
21935
+ );
21936
+ }
21937
+ if (!rows || rows.length === 0) {
21938
+ return {
21939
+ content: [{ type: "text", text: "No licenses found." }]
21940
+ };
21941
+ }
21942
+ const lines = [];
21943
+ lines.push(`## Licenses (${rows.length} total)
21944
+ `);
21945
+ lines.push("| Email | Plan | Status | Key | Expires |");
21946
+ lines.push("|-------|------|--------|-----|---------|");
21947
+ for (const row of rows) {
21948
+ const keyShort = `${row.key.slice(0, 11)}...${row.key.slice(-4)}`;
21949
+ const expires = row.expires_at ? new Date(row.expires_at).toISOString().split("T")[0] : "never";
21950
+ const effectiveStatus = row.status === "active" && row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date() ? "expired" : row.status;
21951
+ lines.push(
21952
+ `| ${row.email} | ${row.plan} | ${effectiveStatus} | \`${keyShort}\` | ${expires} |`
21953
+ );
21954
+ }
21955
+ return { content: [{ type: "text", text: lines.join("\n") }] };
21956
+ } catch (err) {
21957
+ const msg = err instanceof Error ? err.message : String(err);
21958
+ return {
21959
+ content: [{ type: "text", text: `Error listing licenses: ${msg}` }],
21960
+ isError: true
21961
+ };
21962
+ }
21963
+ }
21964
+ );
21965
+ }
21966
+
21967
+ // src/mcp/tools/activate-license.ts
21968
+ init_license();
21969
+ import { z as z71 } from "zod";
21970
+ function registerActivateLicense(server2) {
21971
+ server2.registerTool(
21972
+ "activate_license",
21973
+ {
21974
+ title: "Activate License",
21975
+ description: "Activate an exe_sk_* license key on this device. Writes to ~/.exe-os/license.key, validates against Postgres, and caches the result.",
21976
+ inputSchema: {
21977
+ key: z71.string().startsWith("exe_sk_").describe("License key (exe_sk_*)")
21978
+ }
21979
+ },
21980
+ async ({ key }) => {
21981
+ try {
21982
+ saveLicense(key);
21983
+ const deviceId = loadDeviceId();
21984
+ const license = await validateLicense(key, deviceId);
21985
+ const lines = [];
21986
+ if (license.valid) {
21987
+ lines.push("## License Activated\n");
21988
+ lines.push(`- **Plan:** ${license.plan}`);
21989
+ lines.push(`- **Email:** ${license.email || "N/A"}`);
21990
+ lines.push(
21991
+ `- **Expires:** ${license.expiresAt ? license.expiresAt.split("T")[0] : "never"}`
21992
+ );
21993
+ lines.push(
21994
+ `- **Devices:** ${license.deviceLimit === -1 ? "unlimited" : license.deviceLimit}`
21995
+ );
21996
+ lines.push(
21997
+ `- **Employees:** ${license.employeeLimit === -1 ? "unlimited" : license.employeeLimit}`
21998
+ );
21999
+ lines.push(
22000
+ `- **Memories:** ${license.memoryLimit === -1 ? "unlimited" : license.memoryLimit.toLocaleString()}`
22001
+ );
22002
+ lines.push(`
22003
+ Key saved to ~/.exe-os/license.key. Device ID: ${deviceId}`);
22004
+ } else {
22005
+ lines.push("## Activation Failed\n");
22006
+ lines.push("Key was saved but could not be validated.");
22007
+ lines.push("Check that the key is correct and try again later.");
22008
+ lines.push(`Plan fell back to: ${license.plan}`);
22009
+ }
22010
+ return { content: [{ type: "text", text: lines.join("\n") }] };
22011
+ } catch (err) {
22012
+ const msg = err instanceof Error ? err.message : String(err);
22013
+ return {
22014
+ content: [{ type: "text", text: `Error activating license: ${msg}` }],
22015
+ isError: true
22016
+ };
22017
+ }
22018
+ }
22019
+ );
22020
+ }
22021
+
21309
22022
  // src/lib/telemetry.ts
21310
22023
  var ENABLED = process.env.EXE_TELEMETRY === "1";
21311
22024
  var initialized = false;
@@ -21442,6 +22155,10 @@ registerListPeople(server);
21442
22155
  registerGetPerson(server);
21443
22156
  registerSetAgentConfig(server);
21444
22157
  registerListEmployees(server);
22158
+ registerIngestRaw(server);
22159
+ registerCreateLicense(server);
22160
+ registerListLicenses(server);
22161
+ registerActivateLicense(server);
21445
22162
  try {
21446
22163
  await initStore();
21447
22164
  process.stderr.write("[exe-os] MCP server starting...\n");
@@ -21510,14 +22227,14 @@ try {
21510
22227
  `
21511
22228
  );
21512
22229
  const thisFile = fileURLToPath5(import.meta.url);
21513
- const backfillPath = path40.resolve(
21514
- path40.dirname(thisFile),
22230
+ const backfillPath = path44.resolve(
22231
+ path44.dirname(thisFile),
21515
22232
  "../bin/backfill-vectors.js"
21516
22233
  );
21517
- if (existsSync31(backfillPath)) {
22234
+ if (existsSync33(backfillPath)) {
21518
22235
  const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
21519
- const logPath = path40.join(exeDir, "workers.log");
21520
- mkdirSync17(path40.dirname(logPath), { recursive: true });
22236
+ const logPath = path44.join(exeDir, "workers.log");
22237
+ mkdirSync17(path44.dirname(logPath), { recursive: true });
21521
22238
  let logFd = "ignore";
21522
22239
  try {
21523
22240
  logFd = openSync3(logPath, "a");