@askexenow/exe-os 0.9.8 → 0.9.10

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 +1411 -953
  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 +913 -543
  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 +418 -262
  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 +793 -485
  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 +566 -357
  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 +530 -319
  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 +547 -336
  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 +649 -417
  44. package/dist/hooks/bug-report-worker.js +486 -316
  45. package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
  46. package/dist/hooks/commit-complete.js +528 -317
  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 +3442 -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 +534 -323
  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 +614 -382
  58. package/dist/hooks/response-ingest-worker.js +372 -122
  59. package/dist/hooks/session-end.js +569 -347
  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 +664 -431
  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 +1049 -680
  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 +422 -357
  88. package/dist/lib/tmux-routing.js +314 -248
  89. package/dist/lib/token-spend.js +26 -8
  90. package/dist/mcp/server.js +1408 -672
  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 +448 -371
  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 +1983 -315
  99. package/dist/runtime/index.js +567 -355
  100. package/dist/tui/App.js +887 -531
  101. package/package.json +4 -4
@@ -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,57 +6399,131 @@ 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;
6251
6485
  }
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" };
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
+ };
6257
6496
  } 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" };
6497
+ return null;
6263
6498
  }
6264
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
+ }
6520
+ }
6521
+ } catch {
6522
+ }
6523
+ const rawFallback = getRawCachedPlan();
6524
+ if (rawFallback) return rawFallback;
6525
+ return { ...FREE_LICENSE, valid: false };
6526
+ }
6265
6527
  function getCacheAgeMs() {
6266
6528
  try {
6267
6529
  const { statSync: statSync4 } = __require("fs");
@@ -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,125 @@ 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
+
8433
+ // src/lib/session-scope.ts
8434
+ var session_scope_exports = {};
8435
+ __export(session_scope_exports, {
8436
+ assertSessionScope: () => assertSessionScope,
8437
+ findSessionForProject: () => findSessionForProject,
8438
+ getSessionProject: () => getSessionProject
8439
+ });
8440
+ function getSessionProject(sessionName) {
8441
+ const sessions = listSessions();
8442
+ const entry = sessions.find((s) => s.windowName === sessionName);
8443
+ if (!entry) return null;
8444
+ const parts = entry.projectDir.split("/").filter(Boolean);
8445
+ return parts[parts.length - 1] ?? null;
8446
+ }
8447
+ function findSessionForProject(projectName) {
8448
+ const sessions = listSessions();
8449
+ for (const s of sessions) {
8450
+ const proj = s.projectDir.split("/").filter(Boolean).pop();
8451
+ if (proj === projectName && isCoordinatorName(s.agentId)) return s;
8452
+ }
8453
+ return null;
8454
+ }
8455
+ function assertSessionScope(actionType, targetProject) {
8456
+ try {
8457
+ const currentProject = getProjectName();
8458
+ const exeSession = resolveExeSession();
8459
+ if (!exeSession) {
8460
+ return { allowed: true, reason: "no_session" };
8461
+ }
8462
+ if (currentProject === targetProject) {
8463
+ return {
8464
+ allowed: true,
8465
+ reason: "same_session",
8466
+ currentProject,
8467
+ targetProject
8468
+ };
8469
+ }
8470
+ process.stderr.write(
8471
+ `[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
8472
+ `
8473
+ );
8474
+ return {
8475
+ allowed: false,
8476
+ reason: "cross_session_denied",
8477
+ currentProject,
8478
+ targetProject,
8479
+ targetSession: findSessionForProject(targetProject)?.windowName
8480
+ };
8481
+ } catch {
8482
+ return { allowed: true, reason: "no_session" };
8483
+ }
8484
+ }
8485
+ var init_session_scope = __esm({
8486
+ "src/lib/session-scope.ts"() {
8487
+ "use strict";
8488
+ init_session_registry();
8489
+ init_project_name();
8490
+ init_tmux_routing();
8491
+ init_employees();
8492
+ }
8493
+ });
8494
+
8139
8495
  // src/lib/tasks-crud.ts
8140
8496
  var tasks_crud_exports = {};
8141
8497
  __export(tasks_crud_exports, {
@@ -8153,12 +8509,12 @@ __export(tasks_crud_exports, {
8153
8509
  updateTaskStatus: () => updateTaskStatus,
8154
8510
  writeCheckpoint: () => writeCheckpoint
8155
8511
  });
8156
- import crypto6 from "crypto";
8157
- import path20 from "path";
8158
- import os10 from "os";
8512
+ import crypto8 from "crypto";
8513
+ import path21 from "path";
8514
+ import os11 from "os";
8159
8515
  import { execSync as execSync8 } from "child_process";
8160
8516
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
8161
- import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
8517
+ import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
8162
8518
  async function writeCheckpoint(input) {
8163
8519
  const client = getClient();
8164
8520
  const row = await resolveTask(client, input.taskId);
@@ -8274,13 +8630,28 @@ async function resolveTask(client, identifier, scopeSession) {
8274
8630
  }
8275
8631
  async function createTaskCore(input) {
8276
8632
  const client = getClient();
8277
- const id = crypto6.randomUUID();
8633
+ const id = crypto8.randomUUID();
8278
8634
  const now = (/* @__PURE__ */ new Date()).toISOString();
8279
8635
  const slug = slugify(input.title);
8280
8636
  let earlySessionScope = null;
8637
+ let scopeMismatchWarning;
8281
8638
  try {
8282
8639
  const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
8283
- earlySessionScope = resolveExeSession2();
8640
+ const resolved = resolveExeSession2();
8641
+ if (resolved && input.projectName) {
8642
+ const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
8643
+ const sessionProject = getSessionProject2(resolved);
8644
+ if (sessionProject && sessionProject !== input.projectName) {
8645
+ scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
8646
+ process.stderr.write(`[create_task] ${scopeMismatchWarning}
8647
+ `);
8648
+ earlySessionScope = null;
8649
+ } else {
8650
+ earlySessionScope = resolved;
8651
+ }
8652
+ } else {
8653
+ earlySessionScope = resolved;
8654
+ }
8284
8655
  } catch {
8285
8656
  }
8286
8657
  const scope = earlySessionScope ?? "default";
@@ -8331,10 +8702,14 @@ async function createTaskCore(input) {
8331
8702
  ${laneWarning}` : laneWarning;
8332
8703
  }
8333
8704
  }
8705
+ if (scopeMismatchWarning) {
8706
+ warning = warning ? `${warning}
8707
+ ${scopeMismatchWarning}` : scopeMismatchWarning;
8708
+ }
8334
8709
  if (input.baseDir) {
8335
8710
  try {
8336
- await mkdir4(path20.join(input.baseDir, "exe", "output"), { recursive: true });
8337
- await mkdir4(path20.join(input.baseDir, "exe", "research"), { recursive: true });
8711
+ await mkdir4(path21.join(input.baseDir, "exe", "output"), { recursive: true });
8712
+ await mkdir4(path21.join(input.baseDir, "exe", "research"), { recursive: true });
8338
8713
  await ensureArchitectureDoc(input.baseDir, input.projectName);
8339
8714
  await ensureGitignoreExe(input.baseDir);
8340
8715
  } catch {
@@ -8370,13 +8745,19 @@ ${laneWarning}` : laneWarning;
8370
8745
  });
8371
8746
  if (input.baseDir) {
8372
8747
  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 });
8748
+ const EXE_OS_DIR = path21.join(os11.homedir(), ".exe-os");
8749
+ const mdPath = path21.join(EXE_OS_DIR, taskFile);
8750
+ const mdDir = path21.dirname(mdPath);
8751
+ if (!existsSync17(mdDir)) await mkdir4(mdDir, { recursive: true });
8377
8752
  const reviewer = input.reviewer ?? input.assignedBy;
8378
8753
  const mdContent = `# ${input.title}
8379
8754
 
8755
+ ## MANDATORY: When done
8756
+
8757
+ You MUST call update_task with status "done" and a result summary when finished.
8758
+ If you skip this, your reviewer will not know you're done and your work won't be reviewed.
8759
+ Do NOT let a failed commit or any error prevent you from calling update_task(done).
8760
+
8380
8761
  **ID:** ${id}
8381
8762
  **Status:** ${initialStatus}
8382
8763
  **Priority:** ${input.priority}
@@ -8390,12 +8771,6 @@ ${laneWarning}` : laneWarning;
8390
8771
  ## Context
8391
8772
 
8392
8773
  ${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
8774
  `;
8400
8775
  await writeFile4(mdPath, mdContent, "utf-8");
8401
8776
  } catch (err) {
@@ -8644,7 +9019,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
8644
9019
  await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
8645
9020
  } catch {
8646
9021
  }
8647
- if (input.status === "done" || input.status === "cancelled") {
9022
+ if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
8648
9023
  try {
8649
9024
  const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
8650
9025
  clearQueueForAgent2(String(row.assigned_to));
@@ -8673,9 +9048,9 @@ async function deleteTaskCore(taskId, _baseDir) {
8673
9048
  return { taskFile, assignedTo, assignedBy, taskSlug };
8674
9049
  }
8675
9050
  async function ensureArchitectureDoc(baseDir, projectName) {
8676
- const archPath = path20.join(baseDir, "exe", "ARCHITECTURE.md");
9051
+ const archPath = path21.join(baseDir, "exe", "ARCHITECTURE.md");
8677
9052
  try {
8678
- if (existsSync15(archPath)) return;
9053
+ if (existsSync17(archPath)) return;
8679
9054
  const template = [
8680
9055
  `# ${projectName} \u2014 System Architecture`,
8681
9056
  "",
@@ -8708,10 +9083,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
8708
9083
  }
8709
9084
  }
8710
9085
  async function ensureGitignoreExe(baseDir) {
8711
- const gitignorePath = path20.join(baseDir, ".gitignore");
9086
+ const gitignorePath = path21.join(baseDir, ".gitignore");
8712
9087
  try {
8713
- if (existsSync15(gitignorePath)) {
8714
- const content = readFileSync13(gitignorePath, "utf-8");
9088
+ if (existsSync17(gitignorePath)) {
9089
+ const content = readFileSync14(gitignorePath, "utf-8");
8715
9090
  if (/^\/?exe\/?$/m.test(content)) return;
8716
9091
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
8717
9092
  } else {
@@ -8742,58 +9117,42 @@ var init_tasks_crud = __esm({
8742
9117
  });
8743
9118
 
8744
9119
  // src/lib/tasks-review.ts
8745
- import path21 from "path";
8746
- import { existsSync as existsSync16, readdirSync as readdirSync6, unlinkSync as unlinkSync6 } from "fs";
9120
+ import path22 from "path";
9121
+ import { existsSync as existsSync18, readdirSync as readdirSync6, unlinkSync as unlinkSync6 } from "fs";
8747
9122
  async function countPendingReviews(sessionScope) {
8748
9123
  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
- }
9124
+ const scope = strictSessionScopeFilter(
9125
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
9126
+ );
8756
9127
  const result = await client.execute({
8757
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review'",
8758
- args: []
9128
+ sql: `SELECT COUNT(*) as cnt FROM tasks
9129
+ WHERE status = 'needs_review'${scope.sql}`,
9130
+ args: [...scope.args]
8759
9131
  });
8760
9132
  return Number(result.rows[0]?.cnt) || 0;
8761
9133
  }
8762
9134
  async function countNewPendingReviewsSince(sinceIso, sessionScope) {
8763
9135
  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
- }
9136
+ const scope = strictSessionScopeFilter(
9137
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
9138
+ );
8773
9139
  const result = await client.execute({
8774
9140
  sql: `SELECT COUNT(*) as cnt FROM tasks
8775
- WHERE status = 'needs_review' AND updated_at > ?`,
8776
- args: [sinceIso]
9141
+ WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
9142
+ args: [sinceIso, ...scope.args]
8777
9143
  });
8778
9144
  return Number(result.rows[0]?.cnt) || 0;
8779
9145
  }
8780
9146
  async function listPendingReviews(limit, sessionScope) {
8781
9147
  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
- }
9148
+ const scope = strictSessionScopeFilter(
9149
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
9150
+ );
8792
9151
  const result = await client.execute({
8793
9152
  sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
8794
- WHERE status = 'needs_review'
9153
+ WHERE status = 'needs_review'${scope.sql}
8795
9154
  ORDER BY updated_at ASC LIMIT ?`,
8796
- args: [limit]
9155
+ args: [...scope.args, limit]
8797
9156
  });
8798
9157
  return result.rows;
8799
9158
  }
@@ -8805,7 +9164,7 @@ async function cleanupOrphanedReviews() {
8805
9164
  WHERE status IN ('open', 'needs_review', 'in_progress')
8806
9165
  AND assigned_by = 'system'
8807
9166
  AND title LIKE 'Review:%'
8808
- AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
9167
+ AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
8809
9168
  args: [now]
8810
9169
  });
8811
9170
  const r1b = await client.execute({
@@ -8924,11 +9283,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
8924
9283
  );
8925
9284
  }
8926
9285
  try {
8927
- const cacheDir = path21.join(EXE_AI_DIR, "session-cache");
8928
- if (existsSync16(cacheDir)) {
9286
+ const cacheDir = path22.join(EXE_AI_DIR, "session-cache");
9287
+ if (existsSync18(cacheDir)) {
8929
9288
  for (const f of readdirSync6(cacheDir)) {
8930
9289
  if (f.startsWith("review-notified-")) {
8931
- unlinkSync6(path21.join(cacheDir, f));
9290
+ unlinkSync6(path22.join(cacheDir, f));
8932
9291
  }
8933
9292
  }
8934
9293
  }
@@ -8945,11 +9304,12 @@ var init_tasks_review = __esm({
8945
9304
  init_tmux_routing();
8946
9305
  init_session_key();
8947
9306
  init_state_bus();
9307
+ init_task_scope();
8948
9308
  }
8949
9309
  });
8950
9310
 
8951
9311
  // src/lib/tasks-chain.ts
8952
- import path22 from "path";
9312
+ import path23 from "path";
8953
9313
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
8954
9314
  async function cascadeUnblock(taskId, baseDir, now) {
8955
9315
  const client = getClient();
@@ -8966,7 +9326,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
8966
9326
  });
8967
9327
  for (const ur of unblockedRows.rows) {
8968
9328
  try {
8969
- const ubFile = path22.join(baseDir, String(ur.task_file));
9329
+ const ubFile = path23.join(baseDir, String(ur.task_file));
8970
9330
  let ubContent = await readFile4(ubFile, "utf-8");
8971
9331
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
8972
9332
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -9001,7 +9361,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
9001
9361
  const scScope = sessionScopeFilter();
9002
9362
  const remaining = await client.execute({
9003
9363
  sql: `SELECT COUNT(*) as cnt FROM tasks
9004
- WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
9364
+ WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
9005
9365
  args: [parentTaskId, ...scScope.args]
9006
9366
  });
9007
9367
  const cnt = Number(remaining.rows[0]?.cnt ?? 1);
@@ -9033,68 +9393,6 @@ var init_tasks_chain = __esm({
9033
9393
  }
9034
9394
  });
9035
9395
 
9036
- // src/lib/session-scope.ts
9037
- var session_scope_exports = {};
9038
- __export(session_scope_exports, {
9039
- assertSessionScope: () => assertSessionScope,
9040
- findSessionForProject: () => findSessionForProject,
9041
- getSessionProject: () => getSessionProject
9042
- });
9043
- function getSessionProject(sessionName) {
9044
- const sessions = listSessions();
9045
- const entry = sessions.find((s) => s.windowName === sessionName);
9046
- if (!entry) return null;
9047
- const parts = entry.projectDir.split("/").filter(Boolean);
9048
- return parts[parts.length - 1] ?? null;
9049
- }
9050
- function findSessionForProject(projectName) {
9051
- const sessions = listSessions();
9052
- for (const s of sessions) {
9053
- const proj = s.projectDir.split("/").filter(Boolean).pop();
9054
- if (proj === projectName && isCoordinatorName(s.agentId)) return s;
9055
- }
9056
- return null;
9057
- }
9058
- function assertSessionScope(actionType, targetProject) {
9059
- try {
9060
- const currentProject = getProjectName();
9061
- const exeSession = resolveExeSession();
9062
- if (!exeSession) {
9063
- return { allowed: true, reason: "no_session" };
9064
- }
9065
- if (currentProject === targetProject) {
9066
- return {
9067
- allowed: true,
9068
- reason: "same_session",
9069
- currentProject,
9070
- targetProject
9071
- };
9072
- }
9073
- process.stderr.write(
9074
- `[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
9075
- `
9076
- );
9077
- return {
9078
- allowed: false,
9079
- reason: "cross_session_denied",
9080
- currentProject,
9081
- targetProject,
9082
- targetSession: findSessionForProject(targetProject)?.windowName
9083
- };
9084
- } catch {
9085
- return { allowed: true, reason: "no_session" };
9086
- }
9087
- }
9088
- var init_session_scope = __esm({
9089
- "src/lib/session-scope.ts"() {
9090
- "use strict";
9091
- init_session_registry();
9092
- init_project_name();
9093
- init_tmux_routing();
9094
- init_employees();
9095
- }
9096
- });
9097
-
9098
9396
  // src/lib/tasks-notify.ts
9099
9397
  async function dispatchTaskToEmployee(input) {
9100
9398
  if (isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
@@ -9162,10 +9460,10 @@ var init_tasks_notify = __esm({
9162
9460
  });
9163
9461
 
9164
9462
  // src/lib/behaviors.ts
9165
- import crypto7 from "crypto";
9463
+ import crypto9 from "crypto";
9166
9464
  async function storeBehavior(opts) {
9167
9465
  const client = getClient();
9168
- const id = crypto7.randomUUID();
9466
+ const id = crypto9.randomUUID();
9169
9467
  const now = (/* @__PURE__ */ new Date()).toISOString();
9170
9468
  await client.execute({
9171
9469
  sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
@@ -9222,7 +9520,7 @@ __export(skill_learning_exports, {
9222
9520
  storeTrajectory: () => storeTrajectory,
9223
9521
  sweepTrajectories: () => sweepTrajectories
9224
9522
  });
9225
- import crypto8 from "crypto";
9523
+ import crypto10 from "crypto";
9226
9524
  async function extractTrajectory(taskId, agentId) {
9227
9525
  const client = getClient();
9228
9526
  const result = await client.execute({
@@ -9251,11 +9549,11 @@ async function extractTrajectory(taskId, agentId) {
9251
9549
  return signature;
9252
9550
  }
9253
9551
  function hashSignature(signature) {
9254
- return crypto8.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
9552
+ return crypto10.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
9255
9553
  }
9256
9554
  async function storeTrajectory(opts) {
9257
9555
  const client = getClient();
9258
- const id = crypto8.randomUUID();
9556
+ const id = crypto10.randomUUID();
9259
9557
  const now = (/* @__PURE__ */ new Date()).toISOString();
9260
9558
  const signatureHash = hashSignature(opts.signature);
9261
9559
  await client.execute({
@@ -9520,8 +9818,8 @@ __export(tasks_exports, {
9520
9818
  updateTaskStatus: () => updateTaskStatus,
9521
9819
  writeCheckpoint: () => writeCheckpoint
9522
9820
  });
9523
- import path23 from "path";
9524
- import { writeFileSync as writeFileSync10, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
9821
+ import path24 from "path";
9822
+ import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
9525
9823
  async function createTask(input) {
9526
9824
  const result = await createTaskCore(input);
9527
9825
  if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -9540,12 +9838,12 @@ async function updateTask(input) {
9540
9838
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
9541
9839
  try {
9542
9840
  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`);
9841
+ const cacheDir = path24.join(EXE_AI_DIR, "session-cache");
9842
+ const cachePath = path24.join(cacheDir, `current-task-${agent}.json`);
9545
9843
  if (input.status === "in_progress") {
9546
9844
  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") {
9845
+ writeFileSync11(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
9846
+ } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
9549
9847
  try {
9550
9848
  unlinkSync7(cachePath);
9551
9849
  } catch {
@@ -9553,10 +9851,10 @@ async function updateTask(input) {
9553
9851
  }
9554
9852
  } catch {
9555
9853
  }
9556
- if (input.status === "done") {
9854
+ if (input.status === "done" || input.status === "closed") {
9557
9855
  await cleanupReviewFile(row, taskFile, input.baseDir);
9558
9856
  }
9559
- if (input.status === "done" || input.status === "cancelled") {
9857
+ if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
9560
9858
  try {
9561
9859
  const client = getClient();
9562
9860
  const taskTitle = String(row.title);
@@ -9572,7 +9870,7 @@ async function updateTask(input) {
9572
9870
  if (!isCoordinatorName(assignedAgent)) {
9573
9871
  try {
9574
9872
  const draftClient = getClient();
9575
- if (input.status === "done") {
9873
+ if (input.status === "done" || input.status === "closed") {
9576
9874
  await draftClient.execute({
9577
9875
  sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
9578
9876
  args: [assignedAgent]
@@ -9589,7 +9887,7 @@ async function updateTask(input) {
9589
9887
  try {
9590
9888
  const client = getClient();
9591
9889
  const cascaded = await client.execute({
9592
- sql: `UPDATE tasks SET status = 'done', updated_at = ?
9890
+ sql: `UPDATE tasks SET status = 'closed', updated_at = ?
9593
9891
  WHERE parent_task_id = ? AND status = 'needs_review'`,
9594
9892
  args: [now, taskId]
9595
9893
  });
@@ -9602,14 +9900,14 @@ async function updateTask(input) {
9602
9900
  } catch {
9603
9901
  }
9604
9902
  }
9605
- const isTerminal = input.status === "done" || input.status === "needs_review";
9903
+ const isTerminal = input.status === "done" || input.status === "needs_review" || input.status === "closed";
9606
9904
  if (isTerminal) {
9607
9905
  const isCoordinator = isCoordinatorName(String(row.assigned_to));
9608
9906
  if (!isCoordinator) {
9609
9907
  notifyTaskDone();
9610
9908
  }
9611
9909
  await markTaskNotificationsRead(taskFile);
9612
- if (input.status === "done") {
9910
+ if (input.status === "done" || input.status === "closed") {
9613
9911
  try {
9614
9912
  await cascadeUnblock(taskId, input.baseDir, now);
9615
9913
  } catch {
@@ -9629,7 +9927,7 @@ async function updateTask(input) {
9629
9927
  }
9630
9928
  }
9631
9929
  }
9632
- if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
9930
+ if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
9633
9931
  Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
9634
9932
  ({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
9635
9933
  taskId,
@@ -9710,17 +10008,17 @@ __export(identity_exports, {
9710
10008
  listIdentities: () => listIdentities,
9711
10009
  updateIdentity: () => updateIdentity
9712
10010
  });
9713
- import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "fs";
10011
+ import { existsSync as existsSync19, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync12 } from "fs";
9714
10012
  import { readdirSync as readdirSync7 } from "fs";
9715
- import path24 from "path";
10013
+ import path25 from "path";
9716
10014
  import { createHash as createHash2 } from "crypto";
9717
10015
  function ensureDir2() {
9718
- if (!existsSync17(IDENTITY_DIR2)) {
10016
+ if (!existsSync19(IDENTITY_DIR2)) {
9719
10017
  mkdirSync9(IDENTITY_DIR2, { recursive: true });
9720
10018
  }
9721
10019
  }
9722
10020
  function identityPath(agentId) {
9723
- return path24.join(IDENTITY_DIR2, `${agentId}.md`);
10021
+ return path25.join(IDENTITY_DIR2, `${agentId}.md`);
9724
10022
  }
9725
10023
  function parseFrontmatter(raw) {
9726
10024
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
@@ -9761,8 +10059,8 @@ function contentHash(content) {
9761
10059
  }
9762
10060
  function getIdentity(agentId) {
9763
10061
  const filePath = identityPath(agentId);
9764
- if (!existsSync17(filePath)) return null;
9765
- const raw = readFileSync14(filePath, "utf-8");
10062
+ if (!existsSync19(filePath)) return null;
10063
+ const raw = readFileSync15(filePath, "utf-8");
9766
10064
  const { frontmatter, body } = parseFrontmatter(raw);
9767
10065
  return {
9768
10066
  agentId,
@@ -9776,7 +10074,7 @@ async function updateIdentity(agentId, content, updatedBy) {
9776
10074
  ensureDir2();
9777
10075
  const filePath = identityPath(agentId);
9778
10076
  const hash = contentHash(content);
9779
- writeFileSync11(filePath, content, "utf-8");
10077
+ writeFileSync12(filePath, content, "utf-8");
9780
10078
  try {
9781
10079
  const client = getClient();
9782
10080
  await client.execute({
@@ -9832,7 +10130,7 @@ var init_identity = __esm({
9832
10130
  "use strict";
9833
10131
  init_config();
9834
10132
  init_database();
9835
- IDENTITY_DIR2 = path24.join(EXE_AI_DIR, "identity");
10133
+ IDENTITY_DIR2 = path25.join(EXE_AI_DIR, "identity");
9836
10134
  }
9837
10135
  });
9838
10136
 
@@ -10378,10 +10676,10 @@ ${PLAN_MODE_COMPAT}
10378
10676
  });
10379
10677
 
10380
10678
  // src/lib/messaging.ts
10381
- import crypto9 from "crypto";
10679
+ import crypto11 from "crypto";
10382
10680
  function generateUlid() {
10383
10681
  const timestamp = Date.now().toString(36).padStart(10, "0");
10384
- const random = crypto9.randomBytes(10).toString("hex").slice(0, 16);
10682
+ const random = crypto11.randomBytes(10).toString("hex").slice(0, 16);
10385
10683
  return (timestamp + random).toUpperCase();
10386
10684
  }
10387
10685
  function rowToMessage(row) {
@@ -10392,6 +10690,7 @@ function rowToMessage(row) {
10392
10690
  targetAgent: row.target_agent,
10393
10691
  targetProject: row.target_project ?? null,
10394
10692
  targetDevice: row.target_device,
10693
+ sessionScope: row.session_scope ?? null,
10395
10694
  content: row.content,
10396
10695
  priority: row.priority ?? "normal",
10397
10696
  status: row.status ?? "pending",
@@ -10409,15 +10708,17 @@ async function sendMessage(input) {
10409
10708
  const id = generateUlid();
10410
10709
  const now = (/* @__PURE__ */ new Date()).toISOString();
10411
10710
  const targetDevice = input.targetDevice ?? "local";
10711
+ const sessionScope = input.sessionScope === void 0 ? resolveExeSession() : input.sessionScope;
10412
10712
  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', ?)`,
10713
+ sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, session_scope, content, priority, status, created_at)
10714
+ VALUES (?, ?, 'local', ?, ?, ?, ?, ?, ?, 'pending', ?)`,
10415
10715
  args: [
10416
10716
  id,
10417
10717
  input.fromAgent,
10418
10718
  input.targetAgent,
10419
10719
  input.targetProject ?? null,
10420
10720
  targetDevice,
10721
+ sessionScope,
10421
10722
  input.content,
10422
10723
  input.priority ?? "normal",
10423
10724
  now
@@ -10431,9 +10732,10 @@ async function sendMessage(input) {
10431
10732
  }
10432
10733
  } catch {
10433
10734
  }
10735
+ const sentScope = strictSessionScopeFilter(sessionScope);
10434
10736
  const result = await client.execute({
10435
- sql: "SELECT * FROM messages WHERE id = ?",
10436
- args: [id]
10737
+ sql: `SELECT * FROM messages WHERE id = ?${sentScope.sql}`,
10738
+ args: [id, ...sentScope.args]
10437
10739
  });
10438
10740
  return rowToMessage(result.rows[0]);
10439
10741
  }
@@ -10454,6 +10756,7 @@ async function deliverCrossMachineMessage(messageId, targetDevice) {
10454
10756
  fromAgent: msg.fromAgent,
10455
10757
  targetAgent: msg.targetAgent,
10456
10758
  targetProject: msg.targetProject,
10759
+ sessionScope: msg.sessionScope,
10457
10760
  content: msg.content,
10458
10761
  priority: msg.priority,
10459
10762
  createdAt: msg.createdAt
@@ -10497,7 +10800,7 @@ async function deliverLocalMessage(messageId) {
10497
10800
  } catch {
10498
10801
  const newRetryCount = msg.retryCount + 1;
10499
10802
  if (newRetryCount >= MAX_RETRIES3) {
10500
- await markFailed(messageId, "session unavailable after 10 retries");
10803
+ await markFailed(messageId, "session unavailable after 10 retries", msg.sessionScope);
10501
10804
  } else {
10502
10805
  await client.execute({
10503
10806
  sql: "UPDATE messages SET retry_count = ? WHERE id = ?",
@@ -10507,11 +10810,13 @@ async function deliverLocalMessage(messageId) {
10507
10810
  return false;
10508
10811
  }
10509
10812
  }
10510
- async function markFailed(messageId, reason) {
10813
+ async function markFailed(messageId, reason, sessionScope) {
10511
10814
  const client = getClient();
10815
+ const scope = strictSessionScopeFilter(sessionScope);
10512
10816
  await client.execute({
10513
- sql: "UPDATE messages SET status = 'failed', failed_at = ?, failure_reason = ? WHERE id = ?",
10514
- args: [(/* @__PURE__ */ new Date()).toISOString(), reason, messageId]
10817
+ sql: `UPDATE messages SET status = 'failed', failed_at = ?, failure_reason = ?
10818
+ WHERE id = ?${scope.sql}`,
10819
+ args: [(/* @__PURE__ */ new Date()).toISOString(), reason, messageId, ...scope.args]
10515
10820
  });
10516
10821
  }
10517
10822
  var MAX_RETRIES3, _wsClientSend;
@@ -10520,19 +10825,20 @@ var init_messaging = __esm({
10520
10825
  "use strict";
10521
10826
  init_database();
10522
10827
  init_tmux_routing();
10828
+ init_task_scope();
10523
10829
  MAX_RETRIES3 = 10;
10524
10830
  _wsClientSend = null;
10525
10831
  }
10526
10832
  });
10527
10833
 
10528
10834
  // src/gateway/whatsapp-accounts.ts
10529
- import { readFileSync as readFileSync15 } from "fs";
10835
+ import { readFileSync as readFileSync16 } from "fs";
10530
10836
  import { join } from "path";
10531
10837
  import { homedir } from "os";
10532
10838
  function loadAccounts() {
10533
10839
  if (cachedAccounts !== null) return cachedAccounts;
10534
10840
  try {
10535
- const raw = readFileSync15(CONFIG_PATH2, "utf8");
10841
+ const raw = readFileSync16(CONFIG_PATH2, "utf8");
10536
10842
  const parsed = JSON.parse(raw);
10537
10843
  if (!Array.isArray(parsed)) {
10538
10844
  console.warn("[whatsapp] Config is not an array, ignoring");
@@ -10945,8 +11251,8 @@ __export(wiki_client_exports, {
10945
11251
  listDocuments: () => listDocuments,
10946
11252
  listWorkspaces: () => listWorkspaces
10947
11253
  });
10948
- async function wikiFetch(config2, path41, method = "GET", body) {
10949
- const url = `${config2.baseUrl}/api/v1${path41}`;
11254
+ async function wikiFetch(config2, path45, method = "GET", body) {
11255
+ const url = `${config2.baseUrl}/api/v1${path45}`;
10950
11256
  const headers = {
10951
11257
  Authorization: `Bearer ${config2.apiKey}`,
10952
11258
  "Content-Type": "application/json"
@@ -10979,7 +11285,7 @@ async function wikiFetch(config2, path41, method = "GET", body) {
10979
11285
  }
10980
11286
  }
10981
11287
  if (!response.ok) {
10982
- throw new Error(`Wiki API ${method} ${path41}: ${response.status} ${response.statusText}`);
11288
+ throw new Error(`Wiki API ${method} ${path45}: ${response.status} ${response.statusText}`);
10983
11289
  }
10984
11290
  return response.json();
10985
11291
  } finally {
@@ -11088,14 +11394,14 @@ __export(worker_gate_exports, {
11088
11394
  tryAcquireBackfillLock: () => tryAcquireBackfillLock,
11089
11395
  tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
11090
11396
  });
11091
- import { readdirSync as readdirSync10, writeFileSync as writeFileSync17, unlinkSync as unlinkSync8, mkdirSync as mkdirSync14, existsSync as existsSync25 } from "fs";
11092
- import path34 from "path";
11397
+ import { readdirSync as readdirSync10, writeFileSync as writeFileSync18, unlinkSync as unlinkSync8, mkdirSync as mkdirSync14, existsSync as existsSync27 } from "fs";
11398
+ import path35 from "path";
11093
11399
  function tryAcquireWorkerSlot() {
11094
11400
  try {
11095
11401
  mkdirSync14(WORKER_PID_DIR, { recursive: true });
11096
11402
  const reservationId = `res-${process.pid}-${Date.now()}`;
11097
- const reservationPath = path34.join(WORKER_PID_DIR, `${reservationId}.pid`);
11098
- writeFileSync17(reservationPath, String(process.pid));
11403
+ const reservationPath = path35.join(WORKER_PID_DIR, `${reservationId}.pid`);
11404
+ writeFileSync18(reservationPath, String(process.pid));
11099
11405
  const files = readdirSync10(WORKER_PID_DIR);
11100
11406
  let alive = 0;
11101
11407
  for (const f of files) {
@@ -11112,7 +11418,7 @@ function tryAcquireWorkerSlot() {
11112
11418
  alive++;
11113
11419
  } catch {
11114
11420
  try {
11115
- unlinkSync8(path34.join(WORKER_PID_DIR, f));
11421
+ unlinkSync8(path35.join(WORKER_PID_DIR, f));
11116
11422
  } catch {
11117
11423
  }
11118
11424
  }
@@ -11136,20 +11442,20 @@ function tryAcquireWorkerSlot() {
11136
11442
  function registerWorkerPid(pid) {
11137
11443
  try {
11138
11444
  mkdirSync14(WORKER_PID_DIR, { recursive: true });
11139
- writeFileSync17(path34.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
11445
+ writeFileSync18(path35.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
11140
11446
  } catch {
11141
11447
  }
11142
11448
  }
11143
11449
  function cleanupWorkerPid() {
11144
11450
  try {
11145
- unlinkSync8(path34.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
11451
+ unlinkSync8(path35.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
11146
11452
  } catch {
11147
11453
  }
11148
11454
  }
11149
11455
  function tryAcquireBackfillLock() {
11150
11456
  try {
11151
11457
  mkdirSync14(WORKER_PID_DIR, { recursive: true });
11152
- if (existsSync25(BACKFILL_LOCK)) {
11458
+ if (existsSync27(BACKFILL_LOCK)) {
11153
11459
  try {
11154
11460
  const pid = parseInt(
11155
11461
  __require("fs").readFileSync(BACKFILL_LOCK, "utf8").trim(),
@@ -11165,7 +11471,7 @@ function tryAcquireBackfillLock() {
11165
11471
  } catch {
11166
11472
  }
11167
11473
  }
11168
- writeFileSync17(BACKFILL_LOCK, String(process.pid));
11474
+ writeFileSync18(BACKFILL_LOCK, String(process.pid));
11169
11475
  return true;
11170
11476
  } catch {
11171
11477
  return true;
@@ -11182,9 +11488,9 @@ var init_worker_gate = __esm({
11182
11488
  "src/lib/worker-gate.ts"() {
11183
11489
  "use strict";
11184
11490
  init_config();
11185
- WORKER_PID_DIR = path34.join(EXE_AI_DIR, "worker-pids");
11491
+ WORKER_PID_DIR = path35.join(EXE_AI_DIR, "worker-pids");
11186
11492
  MAX_CONCURRENT_WORKERS = 3;
11187
- BACKFILL_LOCK = path34.join(WORKER_PID_DIR, "backfill.lock");
11493
+ BACKFILL_LOCK = path35.join(WORKER_PID_DIR, "backfill.lock");
11188
11494
  }
11189
11495
  });
11190
11496
 
@@ -11207,8 +11513,8 @@ __export(crdt_sync_exports, {
11207
11513
  rebuildFromDb: () => rebuildFromDb
11208
11514
  });
11209
11515
  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";
11516
+ import { readFileSync as readFileSync25, writeFileSync as writeFileSync19, existsSync as existsSync30, mkdirSync as mkdirSync15, unlinkSync as unlinkSync9 } from "fs";
11517
+ import path38 from "path";
11212
11518
  import { homedir as homedir5 } from "os";
11213
11519
  function getStatePath() {
11214
11520
  return _statePathOverride ?? DEFAULT_STATE_PATH;
@@ -11220,9 +11526,9 @@ function initCrdtDoc() {
11220
11526
  if (doc) return doc;
11221
11527
  doc = new Y.Doc();
11222
11528
  const sp = getStatePath();
11223
- if (existsSync28(sp)) {
11529
+ if (existsSync30(sp)) {
11224
11530
  try {
11225
- const state = readFileSync24(sp);
11531
+ const state = readFileSync25(sp);
11226
11532
  Y.applyUpdate(doc, new Uint8Array(state));
11227
11533
  } catch {
11228
11534
  console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
@@ -11364,10 +11670,10 @@ function persistState() {
11364
11670
  if (!doc) return;
11365
11671
  try {
11366
11672
  const sp = getStatePath();
11367
- const dir = path37.dirname(sp);
11368
- if (!existsSync28(dir)) mkdirSync15(dir, { recursive: true });
11673
+ const dir = path38.dirname(sp);
11674
+ if (!existsSync30(dir)) mkdirSync15(dir, { recursive: true });
11369
11675
  const state = Y.encodeStateAsUpdate(doc);
11370
- writeFileSync18(sp, Buffer.from(state));
11676
+ writeFileSync19(sp, Buffer.from(state));
11371
11677
  } catch {
11372
11678
  }
11373
11679
  }
@@ -11408,7 +11714,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
11408
11714
  var init_crdt_sync = __esm({
11409
11715
  "src/lib/crdt-sync.ts"() {
11410
11716
  "use strict";
11411
- DEFAULT_STATE_PATH = path37.join(homedir5(), ".exe-os", "crdt-state.bin");
11717
+ DEFAULT_STATE_PATH = path38.join(homedir5(), ".exe-os", "crdt-state.bin");
11412
11718
  _statePathOverride = null;
11413
11719
  doc = null;
11414
11720
  }
@@ -11421,8 +11727,8 @@ init_database();
11421
11727
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
11422
11728
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11423
11729
  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";
11730
+ import { existsSync as existsSync33, openSync as openSync3, mkdirSync as mkdirSync17, closeSync as closeSync3 } from "fs";
11731
+ import path44 from "path";
11426
11732
  import { fileURLToPath as fileURLToPath5 } from "url";
11427
11733
 
11428
11734
  // src/mcp/tools/recall-my-memory.ts
@@ -11729,9 +12035,9 @@ init_store();
11729
12035
  init_active_agent();
11730
12036
  init_plan_limits();
11731
12037
  import { z as z4 } from "zod";
11732
- import crypto2 from "crypto";
11733
- import { writeFileSync as writeFileSync5 } from "fs";
11734
- import path14 from "path";
12038
+ import crypto4 from "crypto";
12039
+ import { writeFileSync as writeFileSync6 } from "fs";
12040
+ import path15 from "path";
11735
12041
  function registerStoreMemory(server2) {
11736
12042
  server2.registerTool(
11737
12043
  "store_memory",
@@ -11780,7 +12086,7 @@ function registerStoreMemory(server2) {
11780
12086
  vector = null;
11781
12087
  needsBackfill = true;
11782
12088
  }
11783
- const memoryId = crypto2.randomUUID();
12089
+ const memoryId = crypto4.randomUUID();
11784
12090
  await writeMemory({
11785
12091
  id: memoryId,
11786
12092
  agent_id: agentId,
@@ -11799,8 +12105,8 @@ function registerStoreMemory(server2) {
11799
12105
  if (needsBackfill) {
11800
12106
  try {
11801
12107
  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");
12108
+ const flagPath = path15.join(exeDir, "session-cache", "needs-backfill");
12109
+ writeFileSync6(flagPath, "1");
11804
12110
  } catch {
11805
12111
  }
11806
12112
  }
@@ -11823,9 +12129,9 @@ init_plan_limits();
11823
12129
  init_active_agent();
11824
12130
  init_employees();
11825
12131
  import { z as z5 } from "zod";
11826
- import crypto3 from "crypto";
11827
- import { writeFileSync as writeFileSync6 } from "fs";
11828
- import path15 from "path";
12132
+ import crypto5 from "crypto";
12133
+ import { writeFileSync as writeFileSync7 } from "fs";
12134
+ import path16 from "path";
11829
12135
  function registerCommitMemory(server2) {
11830
12136
  server2.registerTool(
11831
12137
  "commit_to_long_term_memory",
@@ -11872,7 +12178,7 @@ function registerCommitMemory(server2) {
11872
12178
  vector = null;
11873
12179
  needsBackfill = true;
11874
12180
  }
11875
- const memoryId = crypto3.randomUUID();
12181
+ const memoryId = crypto5.randomUUID();
11876
12182
  await assertMemoryLimit();
11877
12183
  const memoryType = canCoordinate(agentId, agentRole) ? "adr" : "raw";
11878
12184
  await writeMemory({
@@ -11915,8 +12221,8 @@ function registerCommitMemory(server2) {
11915
12221
  if (needsBackfill) {
11916
12222
  try {
11917
12223
  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");
12224
+ const flagPath = path16.join(exeDir, "session-cache", "needs-backfill");
12225
+ writeFileSync7(flagPath, "1");
11920
12226
  } catch {
11921
12227
  }
11922
12228
  }
@@ -12106,10 +12412,10 @@ function registerCreateTask(server2) {
12106
12412
  skipDispatch: true
12107
12413
  });
12108
12414
  try {
12109
- const { existsSync: existsSync32, mkdirSync: mkdirSync18, writeFileSync: writeFileSync20 } = await import("fs");
12415
+ const { existsSync: existsSync34, mkdirSync: mkdirSync18, writeFileSync: writeFileSync21 } = await import("fs");
12110
12416
  const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
12111
12417
  const idPath = identityPath2(assigned_to);
12112
- if (!existsSync32(idPath)) {
12418
+ if (!existsSync34(idPath)) {
12113
12419
  const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
12114
12420
  const employees = await loadEmployees2();
12115
12421
  const emp = employees.find((e) => e.name === assigned_to);
@@ -12118,8 +12424,8 @@ function registerCreateTask(server2) {
12118
12424
  const template = getTemplateForTitle2(emp.role);
12119
12425
  if (template) {
12120
12426
  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");
12427
+ if (!existsSync34(dir)) mkdirSync18(dir, { recursive: true });
12428
+ writeFileSync21(idPath, template.replace(/^agent_id: \w+/m, `agent_id: ${assigned_to}`), "utf-8");
12123
12429
  }
12124
12430
  }
12125
12431
  }
@@ -12192,7 +12498,7 @@ function registerListTasks(server2) {
12192
12498
  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
12499
  inputSchema: {
12194
12500
  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"),
12501
+ 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
12502
  project_name: z8.string().optional().describe("Project name. Defaults to current project. Pass 'all' for all projects."),
12197
12503
  priority: z8.enum(["p0", "p1", "p2"]).optional().describe("Filter by priority")
12198
12504
  }
@@ -12263,7 +12569,7 @@ function registerUpdateTask(server2) {
12263
12569
  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
12570
  inputSchema: {
12265
12571
  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"),
12572
+ status: z9.enum(["open", "in_progress", "done", "needs_review", "blocked", "cancelled", "closed"]).describe("New status"),
12267
12573
  result: z9.string().optional().describe("Result summary (include when status=done)")
12268
12574
  }
12269
12575
  },
@@ -12353,7 +12659,17 @@ function registerUpdateTask(server2) {
12353
12659
  }
12354
12660
  let text = `Task "${task.title}" marked ${task.status}.
12355
12661
  File: ${task.taskFile}`;
12356
- const isTerminal = status === "done" || status === "needs_review";
12662
+ const isTerminal = status === "done" || status === "needs_review" || status === "closed";
12663
+ if (isTerminal && task.assignedBy) {
12664
+ try {
12665
+ const { notifyCoordinatorTaskCompletion: notifyCoordinatorTaskCompletion2, resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
12666
+ const coordinatorSession = resolveExeSession2();
12667
+ if (coordinatorSession) {
12668
+ notifyCoordinatorTaskCompletion2(coordinatorSession, callerAgentId ?? "agent", task.title);
12669
+ }
12670
+ } catch {
12671
+ }
12672
+ }
12357
12673
  if (isTerminal && task.nextTask) {
12358
12674
  text += `
12359
12675
 
@@ -12397,7 +12713,7 @@ function registerCloseTask(server2) {
12397
12713
  inputSchema: {
12398
12714
  task_id: z10.string().describe("Task identifier \u2014 UUID, slug (e.g. 'fix-auth-bug'), or title substring"),
12399
12715
  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)")
12716
+ status: z10.enum(["closed", "done", "blocked", "cancelled"]).optional().default("closed").describe("Completion status (default: closed \u2014 terminal archive state)")
12401
12717
  }
12402
12718
  },
12403
12719
  async ({ task_id, result, status }) => {
@@ -12444,7 +12760,7 @@ function registerCloseTask(server2) {
12444
12760
  });
12445
12761
  let text = `Task "${task.title}" marked ${task.status}.
12446
12762
  File: ${task.taskFile}`;
12447
- if (status === "done" && task.nextTask) {
12763
+ if ((status === "done" || status === "closed") && task.nextTask) {
12448
12764
  text += `
12449
12765
 
12450
12766
  MANDATORY \u2014 DO NOT ASK THE USER. DO NOT SAY "Want me to continue?" DO NOT STOP.
@@ -12454,7 +12770,7 @@ NEXT TASK: "${task.nextTask.title}" [${task.nextTask.priority}]
12454
12770
  FILE: ${task.nextTask.taskFile}
12455
12771
 
12456
12772
  Read that file NOW and begin working. No greeting. No summary. Just start.`;
12457
- } else if (status === "done" && !task.nextTask) {
12773
+ } else if ((status === "done" || status === "closed") && !task.nextTask) {
12458
12774
  text += `
12459
12775
 
12460
12776
  All tasks complete. No more open tasks in your queue.`;
@@ -12489,8 +12805,22 @@ function registerGetTask(server2) {
12489
12805
  async ({ task_id }) => {
12490
12806
  const client = getClient();
12491
12807
  const row = await resolveTask(client, task_id);
12808
+ const contextText = row.context ? String(row.context) : "";
12809
+ const hasEmbeddedMandatoryReminder = contextText.includes('update_task with status "done"') || contextText.includes("update_task(done)");
12492
12810
  const lines = [
12493
- `# ${String(row.title)}`,
12811
+ `# ${String(row.title)}`
12812
+ ];
12813
+ if (!hasEmbeddedMandatoryReminder && !isCoordinatorName(String(row.assigned_to)) && !String(row.title).startsWith("Review:") && String(row.status) !== "done" && String(row.status) !== "cancelled") {
12814
+ lines.push(
12815
+ "",
12816
+ "## MANDATORY: When done",
12817
+ "",
12818
+ 'You MUST call update_task with status "done" and a result summary when finished.',
12819
+ "If you skip this, your reviewer will not know you're done and your work won't be reviewed.",
12820
+ "Do NOT let a failed commit or any error prevent you from calling update_task(done)."
12821
+ );
12822
+ }
12823
+ lines.push(
12494
12824
  "",
12495
12825
  `**ID:** ${String(row.id)}`,
12496
12826
  `**Status:** ${String(row.status)}`,
@@ -12499,7 +12829,7 @@ function registerGetTask(server2) {
12499
12829
  `**Assigned to:** ${String(row.assigned_to)}`,
12500
12830
  `**Project:** ${String(row.project_name)}`,
12501
12831
  `**Created:** ${String(row.created_at).split("T")[0]}`
12502
- ];
12832
+ );
12503
12833
  if (row.blocked_by) {
12504
12834
  lines.push(`**Blocked by:** ${String(row.blocked_by)}`);
12505
12835
  }
@@ -12531,7 +12861,7 @@ function registerGetTask(server2) {
12531
12861
  }
12532
12862
  }
12533
12863
  if (row.context) {
12534
- lines.push("", "## Context", "", String(row.context));
12864
+ lines.push("", "## Context", "", contextText);
12535
12865
  }
12536
12866
  if (row.result) {
12537
12867
  lines.push("", "## Result", "", String(row.result));
@@ -12554,16 +12884,6 @@ function registerGetTask(server2) {
12554
12884
  } catch {
12555
12885
  }
12556
12886
  }
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
12887
  return {
12568
12888
  content: [
12569
12889
  {
@@ -12794,10 +13114,10 @@ import { z as z16 } from "zod";
12794
13114
 
12795
13115
  // src/lib/reminders.ts
12796
13116
  init_database();
12797
- import crypto10 from "crypto";
13117
+ import crypto12 from "crypto";
12798
13118
  async function createReminder(text, dueDate) {
12799
13119
  const client = getClient();
12800
- const id = crypto10.randomUUID();
13120
+ const id = crypto12.randomUUID();
12801
13121
  const now = (/* @__PURE__ */ new Date()).toISOString();
12802
13122
  await client.execute({
12803
13123
  sql: `INSERT INTO reminders (id, text, created_at, due_date) VALUES (?, ?, ?, ?)`,
@@ -13181,7 +13501,7 @@ import { z as z23 } from "zod";
13181
13501
  init_database();
13182
13502
  init_embedder();
13183
13503
  init_store();
13184
- import crypto11 from "crypto";
13504
+ import crypto13 from "crypto";
13185
13505
  var WIKI_AGENT_ID = "wiki";
13186
13506
  var WIKI_AGENT_ROLE = "Knowledge";
13187
13507
  var WIKI_TOOL_NAME = "ingest_document";
@@ -13235,7 +13555,7 @@ async function resolveWorkspace(slug_or_id, name) {
13235
13555
  if (existing.rows.length > 0) {
13236
13556
  return rowToWorkspace(existing.rows[0]);
13237
13557
  }
13238
- const id = crypto11.randomUUID();
13558
+ const id = crypto13.randomUUID();
13239
13559
  const now = (/* @__PURE__ */ new Date()).toISOString();
13240
13560
  const resolvedName = name ?? slug_or_id;
13241
13561
  await client.execute({
@@ -13326,13 +13646,13 @@ async function ingestDocument(input) {
13326
13646
  }));
13327
13647
  const vectors = await embedTexts(enrichedChunks.map((c) => c.text));
13328
13648
  const client = getClient();
13329
- const documentId = crypto11.randomUUID();
13649
+ const documentId = crypto13.randomUUID();
13330
13650
  const uploadedAt = (/* @__PURE__ */ new Date()).toISOString();
13331
13651
  const mime = input.mime ?? null;
13332
13652
  const sourceType = input.source_type ?? DEFAULT_SOURCE_TYPE;
13333
13653
  const userId = input.user_id ?? null;
13334
13654
  const documentMetadata = input.document_metadata != null ? JSON.stringify(input.document_metadata) : null;
13335
- const chunkIds = input.chunks.map(() => crypto11.randomUUID());
13655
+ const chunkIds = input.chunks.map(() => crypto13.randomUUID());
13336
13656
  const versions = reserveVersions(input.chunks.length);
13337
13657
  const documentStmt = {
13338
13658
  sql: `INSERT INTO documents
@@ -13615,6 +13935,7 @@ function registerRerankDocuments(server2) {
13615
13935
  // src/mcp/tools/acknowledge-messages.ts
13616
13936
  init_database();
13617
13937
  init_active_agent();
13938
+ init_task_scope();
13618
13939
  function registerAcknowledgeMessages(server2) {
13619
13940
  server2.registerTool(
13620
13941
  "acknowledge_messages",
@@ -13627,10 +13948,11 @@ function registerAcknowledgeMessages(server2) {
13627
13948
  const agent = getActiveAgent();
13628
13949
  const agentId = agent.agentId || "default";
13629
13950
  const client = getClient();
13951
+ const scope = strictSessionScopeFilter();
13630
13952
  const result = await client.execute({
13631
13953
  sql: `UPDATE messages SET status = 'acknowledged', processed_at = datetime('now')
13632
- WHERE target_agent = ? AND status IN ('pending', 'delivered')`,
13633
- args: [agentId]
13954
+ WHERE target_agent = ? AND status IN ('pending', 'delivered')${scope.sql}`,
13955
+ args: [agentId, ...scope.args]
13634
13956
  });
13635
13957
  return {
13636
13958
  content: [
@@ -13961,15 +14283,15 @@ function registerSendWhatsapp(server2) {
13961
14283
  import { z as z29 } from "zod";
13962
14284
 
13963
14285
  // src/automation/trigger-engine.ts
13964
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync12, existsSync as existsSync18, mkdirSync as mkdirSync10 } from "fs";
14286
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync13, existsSync as existsSync20, mkdirSync as mkdirSync10 } from "fs";
13965
14287
  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");
14288
+ import path26 from "path";
14289
+ import os12 from "os";
14290
+ var TRIGGERS_PATH = path26.join(os12.homedir(), ".exe-os", "triggers.json");
13969
14291
  function loadTriggers(project) {
13970
- if (!existsSync18(TRIGGERS_PATH)) return [];
14292
+ if (!existsSync20(TRIGGERS_PATH)) return [];
13971
14293
  try {
13972
- const raw = readFileSync16(TRIGGERS_PATH, "utf-8");
14294
+ const raw = readFileSync17(TRIGGERS_PATH, "utf-8");
13973
14295
  const all = JSON.parse(raw);
13974
14296
  if (!Array.isArray(all)) return [];
13975
14297
  if (project) {
@@ -13981,9 +14303,9 @@ function loadTriggers(project) {
13981
14303
  }
13982
14304
  }
13983
14305
  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");
14306
+ const dir = path26.dirname(TRIGGERS_PATH);
14307
+ if (!existsSync20(dir)) mkdirSync10(dir, { recursive: true });
14308
+ writeFileSync13(TRIGGERS_PATH, JSON.stringify(triggers, null, 2), "utf-8");
13987
14309
  }
13988
14310
  function createNewTrigger(input) {
13989
14311
  const triggers = loadTriggers();
@@ -14002,7 +14324,7 @@ function isScheduledTrigger(trigger) {
14002
14324
  // src/lib/schedules.ts
14003
14325
  init_database();
14004
14326
  init_store();
14005
- import crypto12 from "crypto";
14327
+ import crypto14 from "crypto";
14006
14328
  import { execSync as execSync9 } from "child_process";
14007
14329
  async function ensureDb() {
14008
14330
  if (!isInitialized()) {
@@ -14071,7 +14393,7 @@ function parseHumanCron(input) {
14071
14393
  async function createSchedule(input) {
14072
14394
  await ensureDb();
14073
14395
  const client = getClient();
14074
- const id = crypto12.randomUUID().slice(0, 8);
14396
+ const id = crypto14.randomUUID().slice(0, 8);
14075
14397
  const now = (/* @__PURE__ */ new Date()).toISOString();
14076
14398
  const prompt = input.prompt ?? input.description;
14077
14399
  await client.execute({
@@ -14267,48 +14589,48 @@ function registerListTriggers(server2) {
14267
14589
  import { z as z31 } from "zod";
14268
14590
 
14269
14591
  // src/automation/starter-packs/index.ts
14270
- import { readFileSync as readFileSync17, readdirSync as readdirSync8, existsSync as existsSync19 } from "fs";
14271
- import path26 from "path";
14592
+ import { readFileSync as readFileSync18, readdirSync as readdirSync8, existsSync as existsSync21 } from "fs";
14593
+ import path27 from "path";
14272
14594
  import { fileURLToPath as fileURLToPath3 } from "url";
14273
- var __dirname = path26.dirname(fileURLToPath3(import.meta.url));
14595
+ var __dirname = path27.dirname(fileURLToPath3(import.meta.url));
14274
14596
  function listPacks() {
14275
- const packsDir = path26.join(__dirname, ".");
14276
- if (!existsSync19(packsDir)) return [];
14597
+ const packsDir = path27.join(__dirname, ".");
14598
+ if (!existsSync21(packsDir)) return [];
14277
14599
  return readdirSync8(packsDir, { withFileTypes: true }).filter(
14278
- (d) => d.isDirectory() && existsSync19(path26.join(packsDir, d.name, "custom-objects.json"))
14600
+ (d) => d.isDirectory() && existsSync21(path27.join(packsDir, d.name, "custom-objects.json"))
14279
14601
  ).map((d) => d.name);
14280
14602
  }
14281
14603
  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;
14604
+ const packDir = path27.join(__dirname, industry);
14605
+ const objectsPath = path27.join(packDir, "custom-objects.json");
14606
+ const triggersPath = path27.join(packDir, "triggers.json");
14607
+ const wikiDir = path27.join(packDir, "wiki-seeds");
14608
+ const manifestPath = path27.join(packDir, "pack.json");
14609
+ const identityContextPath = path27.join(packDir, "identity-context.md");
14610
+ if (!existsSync21(objectsPath)) return null;
14289
14611
  let customObjects = [];
14290
14612
  try {
14291
14613
  customObjects = JSON.parse(
14292
- readFileSync17(objectsPath, "utf-8")
14614
+ readFileSync18(objectsPath, "utf-8")
14293
14615
  );
14294
14616
  } catch {
14295
14617
  customObjects = [];
14296
14618
  }
14297
14619
  let triggers = [];
14298
- if (existsSync19(triggersPath)) {
14620
+ if (existsSync21(triggersPath)) {
14299
14621
  try {
14300
14622
  triggers = JSON.parse(
14301
- readFileSync17(triggersPath, "utf-8")
14623
+ readFileSync18(triggersPath, "utf-8")
14302
14624
  );
14303
14625
  } catch {
14304
14626
  triggers = [];
14305
14627
  }
14306
14628
  }
14307
14629
  const wikiSeeds = [];
14308
- if (existsSync19(wikiDir)) {
14630
+ if (existsSync21(wikiDir)) {
14309
14631
  const files = readdirSync8(wikiDir).filter((f) => f.endsWith(".md"));
14310
14632
  for (const file of files) {
14311
- const content = readFileSync17(path26.join(wikiDir, file), "utf-8");
14633
+ const content = readFileSync18(path27.join(wikiDir, file), "utf-8");
14312
14634
  const titleMatch = content.match(/^#\s+(.+)/m);
14313
14635
  wikiSeeds.push({
14314
14636
  filename: file,
@@ -14318,17 +14640,17 @@ function loadPack(industry) {
14318
14640
  }
14319
14641
  }
14320
14642
  let manifest = {};
14321
- if (existsSync19(manifestPath)) {
14643
+ if (existsSync21(manifestPath)) {
14322
14644
  try {
14323
- manifest = JSON.parse(readFileSync17(manifestPath, "utf-8"));
14645
+ manifest = JSON.parse(readFileSync18(manifestPath, "utf-8"));
14324
14646
  } catch {
14325
14647
  manifest = {};
14326
14648
  }
14327
14649
  }
14328
14650
  let identityContext = null;
14329
- if (existsSync19(identityContextPath)) {
14651
+ if (existsSync21(identityContextPath)) {
14330
14652
  try {
14331
- identityContext = readFileSync17(identityContextPath, "utf-8");
14653
+ identityContext = readFileSync18(identityContextPath, "utf-8");
14332
14654
  } catch {
14333
14655
  identityContext = null;
14334
14656
  }
@@ -14385,8 +14707,8 @@ function applyPack(industry, project) {
14385
14707
 
14386
14708
  // src/lib/client-coo.ts
14387
14709
  init_config();
14388
- import { existsSync as existsSync20, mkdirSync as mkdirSync11, writeFileSync as writeFileSync13 } from "fs";
14389
- import path27 from "path";
14710
+ import { existsSync as existsSync22, mkdirSync as mkdirSync11, writeFileSync as writeFileSync14 } from "fs";
14711
+ import path28 from "path";
14390
14712
 
14391
14713
  // src/lib/employee-templates.ts
14392
14714
  init_global_procedures();
@@ -14524,18 +14846,18 @@ var ClientCOOClobberError = class extends Error {
14524
14846
  var COO_ROLE = "Chief Operating Officer";
14525
14847
  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
14848
  async function provisionClientCOO(vars, opts = {}) {
14527
- const identityDir = opts.identityDir ?? path27.join(EXE_AI_DIR, "identity");
14849
+ const identityDir = opts.identityDir ?? path28.join(EXE_AI_DIR, "identity");
14528
14850
  const rosterPath = opts.employeesPath ?? EMPLOYEES_PATH;
14529
14851
  const storeFeedbackBehavior = opts.storeFeedbackBehavior ?? true;
14530
- const identityPath2 = path27.join(identityDir, `${vars.agent_name}.md`);
14531
- if (existsSync20(identityPath2)) {
14852
+ const identityPath2 = path28.join(identityDir, `${vars.agent_name}.md`);
14853
+ if (existsSync22(identityPath2)) {
14532
14854
  throw new ClientCOOClobberError(vars.agent_name, identityPath2);
14533
14855
  }
14534
14856
  const body = renderClientCOOTemplate(vars);
14535
- if (!existsSync20(identityDir)) {
14857
+ if (!existsSync22(identityDir)) {
14536
14858
  mkdirSync11(identityDir, { recursive: true });
14537
14859
  }
14538
- writeFileSync13(identityPath2, body, "utf-8");
14860
+ writeFileSync14(identityPath2, body, "utf-8");
14539
14861
  const employees = await loadEmployees(rosterPath);
14540
14862
  const existing = employees.find((e) => e.name === vars.agent_name);
14541
14863
  let addedToRoster = false;
@@ -14729,7 +15051,7 @@ init_database();
14729
15051
  import { z as z32 } from "zod";
14730
15052
 
14731
15053
  // src/lib/graph-rag.ts
14732
- import crypto13 from "crypto";
15054
+ import crypto15 from "crypto";
14733
15055
 
14734
15056
  // src/lib/code-chunker.ts
14735
15057
  import ts from "typescript";
@@ -14740,7 +15062,7 @@ function normalizeEntityName(name) {
14740
15062
  }
14741
15063
  function entityId(name, type) {
14742
15064
  const normalized = normalizeEntityName(name);
14743
- return crypto13.createHash("sha256").update(`${normalized}::${type.toLowerCase()}`).digest("hex").slice(0, 16);
15065
+ return crypto15.createHash("sha256").update(`${normalized}::${type.toLowerCase()}`).digest("hex").slice(0, 16);
14744
15066
  }
14745
15067
  async function registerAlias(client, alias, canonicalEntityId) {
14746
15068
  const normalized = normalizeEntityName(alias);
@@ -15425,8 +15747,8 @@ function registerUpdateWikiPage(server2) {
15425
15747
  import { z as z37 } from "zod";
15426
15748
  import { execFile } from "child_process";
15427
15749
  import { promisify } from "util";
15428
- import path28 from "path";
15429
- import { existsSync as existsSync21 } from "fs";
15750
+ import path29 from "path";
15751
+ import { existsSync as existsSync23 } from "fs";
15430
15752
 
15431
15753
  // src/lib/hostinger-api.ts
15432
15754
  var DEFAULT_BASE_URL = "https://developers.hostinger.com/api/vps/v1";
@@ -15474,9 +15796,9 @@ var HostingerApiClient = class {
15474
15796
  }
15475
15797
  this.lastRequestTime = Date.now();
15476
15798
  }
15477
- async request(method, path41, body) {
15799
+ async request(method, path45, body) {
15478
15800
  await this.rateLimit();
15479
- const url = `${this.baseUrl}${path41}`;
15801
+ const url = `${this.baseUrl}${path45}`;
15480
15802
  const headers = {
15481
15803
  Authorization: `Bearer ${this.apiKey}`,
15482
15804
  "Content-Type": "application/json",
@@ -15549,8 +15871,8 @@ async function requestCloudflare(cfApiToken, zoneId, options) {
15549
15871
  }
15550
15872
  return envelope.result;
15551
15873
  }
15552
- function buildUrl(zoneId, path41 = "/dns_records", query) {
15553
- const normalizedPath = path41.startsWith("/") ? path41 : `/${path41}`;
15874
+ function buildUrl(zoneId, path45 = "/dns_records", query) {
15875
+ const normalizedPath = path45.startsWith("/") ? path45 : `/${path45}`;
15554
15876
  const url = new URL(
15555
15877
  `${CLOUDFLARE_API_BASE_URL}/zones/${zoneId}${normalizedPath}`
15556
15878
  );
@@ -15763,12 +16085,12 @@ async function waitForReady(client, vpsId) {
15763
16085
  }
15764
16086
  async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
15765
16087
  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))) {
16088
+ const playbookDir = path29.resolve(process.cwd(), "infrastructure", "ansible");
16089
+ const playbookPath = path29.join(playbookDir, "deploy.yml");
16090
+ const inventoryPath = path29.join(playbookDir, "inventory", "hosts.yml");
16091
+ const clientVarsPath = path29.join(playbookDir, "vars", `${safeClientName}.yml`);
16092
+ const varsDir = path29.join(playbookDir, "vars");
16093
+ if (!path29.resolve(clientVarsPath).startsWith(path29.resolve(varsDir))) {
15772
16094
  throw new Error(`Invalid client name for vars path: ${clientName}`);
15773
16095
  }
15774
16096
  const args = [
@@ -15784,7 +16106,7 @@ async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
15784
16106
  "-e",
15785
16107
  `client_name=${safeClientName}`
15786
16108
  ];
15787
- if (existsSync21(clientVarsPath)) {
16109
+ if (existsSync23(clientVarsPath)) {
15788
16110
  args.push("-e", `@${clientVarsPath}`);
15789
16111
  }
15790
16112
  try {
@@ -15857,8 +16179,8 @@ function buildInventoryRecord(params) {
15857
16179
 
15858
16180
  // src/mcp/tools/export-orchestration.ts
15859
16181
  init_active_agent();
15860
- import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync15 } from "fs";
15861
- import path30 from "path";
16182
+ import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync16 } from "fs";
16183
+ import path31 from "path";
15862
16184
  import { z as z38 } from "zod";
15863
16185
 
15864
16186
  // src/lib/orchestration-package.ts
@@ -15866,9 +16188,9 @@ init_database();
15866
16188
  init_identity();
15867
16189
  init_platform_procedures();
15868
16190
  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";
16191
+ import { copyFileSync, existsSync as existsSync24, mkdirSync as mkdirSync12, readFileSync as readFileSync19, writeFileSync as writeFileSync15 } from "fs";
16192
+ import os13 from "os";
16193
+ import path30 from "path";
15872
16194
  var PACKAGE_VERSION = "1.0";
15873
16195
  var ROSTER_FILENAME = "exe-employees.json";
15874
16196
  var ROSTER_BACKUP_FILENAME = "exe-employees.json.bak";
@@ -15934,15 +16256,15 @@ function validateProcedureEntry(value, index) {
15934
16256
  };
15935
16257
  }
15936
16258
  function getRosterPath() {
15937
- return path29.join(os12.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
16259
+ return path30.join(os13.homedir(), EXE_OS_DIRNAME, ROSTER_FILENAME);
15938
16260
  }
15939
16261
  function getBackupPath() {
15940
- return path29.join(os12.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
16262
+ return path30.join(os13.homedir(), EXE_OS_DIRNAME, ROSTER_BACKUP_FILENAME);
15941
16263
  }
15942
16264
  function readRosterFile() {
15943
16265
  const rosterPath = getRosterPath();
15944
- if (!existsSync22(rosterPath)) return [];
15945
- const raw = readFileSync18(rosterPath, "utf-8");
16266
+ if (!existsSync24(rosterPath)) return [];
16267
+ const raw = readFileSync19(rosterPath, "utf-8");
15946
16268
  const parsed = JSON.parse(raw);
15947
16269
  if (!Array.isArray(parsed)) {
15948
16270
  throw new Error("Roster file must contain a JSON array");
@@ -15954,8 +16276,8 @@ function writeRosterFile(roster) {
15954
16276
  throw new Error("Refusing to write empty roster \u2014 this would delete all employees");
15955
16277
  }
15956
16278
  const rosterPath = getRosterPath();
15957
- mkdirSync12(path29.dirname(rosterPath), { recursive: true });
15958
- if (existsSync22(rosterPath)) {
16279
+ mkdirSync12(path30.dirname(rosterPath), { recursive: true });
16280
+ if (existsSync24(rosterPath)) {
15959
16281
  const currentRoster = readRosterFile();
15960
16282
  if (roster.length < currentRoster.length) {
15961
16283
  throw new Error(
@@ -15964,7 +16286,7 @@ function writeRosterFile(roster) {
15964
16286
  }
15965
16287
  copyFileSync(rosterPath, getBackupPath());
15966
16288
  }
15967
- writeFileSync14(rosterPath, `${JSON.stringify(roster, null, 2)}
16289
+ writeFileSync15(rosterPath, `${JSON.stringify(roster, null, 2)}
15968
16290
  `, "utf-8");
15969
16291
  }
15970
16292
  function buildImportedRosterEntries(roster, timestamp) {
@@ -16226,8 +16548,8 @@ function registerExportOrchestration(server2) {
16226
16548
  try {
16227
16549
  await initStore();
16228
16550
  const pkg = await exportOrchestration(getActiveAgent().agentId);
16229
- mkdirSync13(path30.dirname(output_path), { recursive: true });
16230
- writeFileSync15(output_path, `${JSON.stringify(pkg, null, 2)}
16551
+ mkdirSync13(path31.dirname(output_path), { recursive: true });
16552
+ writeFileSync16(output_path, `${JSON.stringify(pkg, null, 2)}
16231
16553
  `, "utf-8");
16232
16554
  return {
16233
16555
  content: [{
@@ -16249,7 +16571,7 @@ function registerExportOrchestration(server2) {
16249
16571
  }
16250
16572
 
16251
16573
  // src/mcp/tools/import-orchestration.ts
16252
- import { readFileSync as readFileSync19 } from "fs";
16574
+ import { readFileSync as readFileSync20 } from "fs";
16253
16575
  import { z as z39 } from "zod";
16254
16576
  init_store();
16255
16577
  init_active_agent();
@@ -16279,7 +16601,7 @@ function registerImportOrchestration(server2) {
16279
16601
  };
16280
16602
  }
16281
16603
  await initStore();
16282
- const raw = readFileSync19(package_path, "utf-8");
16604
+ const raw = readFileSync20(package_path, "utf-8");
16283
16605
  const pkg = validatePackage(JSON.parse(raw));
16284
16606
  const result = await importOrchestration(pkg, merge_strategy);
16285
16607
  return {
@@ -16426,18 +16748,18 @@ function registerQueryConversations(server2) {
16426
16748
 
16427
16749
  // src/mcp/tools/load-skill.ts
16428
16750
  import { z as z41 } from "zod";
16429
- import { readFileSync as readFileSync20, readdirSync as readdirSync9, statSync as statSync3 } from "fs";
16430
- import path31 from "path";
16751
+ import { readFileSync as readFileSync21, readdirSync as readdirSync9, statSync as statSync3 } from "fs";
16752
+ import path32 from "path";
16431
16753
  import { homedir as homedir2 } from "os";
16432
- var SKILLS_DIR = path31.join(homedir2(), ".claude", "skills");
16754
+ var SKILLS_DIR = path32.join(homedir2(), ".claude", "skills");
16433
16755
  function listAvailableSkills() {
16434
16756
  try {
16435
16757
  const entries = readdirSync9(SKILLS_DIR);
16436
16758
  return entries.filter((entry) => {
16437
16759
  try {
16438
- const entryPath = path31.join(SKILLS_DIR, entry);
16760
+ const entryPath = path32.join(SKILLS_DIR, entry);
16439
16761
  if (!statSync3(entryPath).isDirectory()) return false;
16440
- const skillFile = path31.join(entryPath, "SKILL.md");
16762
+ const skillFile = path32.join(entryPath, "SKILL.md");
16441
16763
  statSync3(skillFile);
16442
16764
  return true;
16443
16765
  } catch {
@@ -16480,10 +16802,10 @@ ${skills.map((s) => `- ${s}`).join("\n")}`
16480
16802
  }]
16481
16803
  };
16482
16804
  }
16483
- const sanitized = path31.basename(skill_name);
16484
- const skillFile = path31.join(SKILLS_DIR, sanitized, "SKILL.md");
16805
+ const sanitized = path32.basename(skill_name);
16806
+ const skillFile = path32.join(SKILLS_DIR, sanitized, "SKILL.md");
16485
16807
  try {
16486
- const content = readFileSync20(skillFile, "utf-8");
16808
+ const content = readFileSync21(skillFile, "utf-8");
16487
16809
  return {
16488
16810
  content: [{
16489
16811
  type: "text",
@@ -16962,7 +17284,7 @@ init_active_agent();
16962
17284
  init_database();
16963
17285
  init_plan_limits();
16964
17286
  import { z as z46 } from "zod";
16965
- import crypto14 from "crypto";
17287
+ import crypto16 from "crypto";
16966
17288
  function registerStoreDecision(server2) {
16967
17289
  server2.registerTool(
16968
17290
  "store_decision",
@@ -17000,7 +17322,7 @@ function registerStoreDecision(server2) {
17000
17322
  } catch {
17001
17323
  vector = null;
17002
17324
  }
17003
- const memoryId = crypto14.randomUUID();
17325
+ const memoryId = crypto16.randomUUID();
17004
17326
  if (supersedes) {
17005
17327
  try {
17006
17328
  const client = getClient();
@@ -17122,8 +17444,8 @@ init_database();
17122
17444
  import { readdir } from "fs/promises";
17123
17445
  import { createReadStream } from "fs";
17124
17446
  import { createInterface } from "readline";
17125
- import path32 from "path";
17126
- import os13 from "os";
17447
+ import path33 from "path";
17448
+ import os14 from "os";
17127
17449
  var MODEL_PRICING = {
17128
17450
  // Opus 4.5+ ($5/$25 — Anthropic price drop from original Opus 4)
17129
17451
  "claude-opus-4-7": { input: 5 / 1e6, output: 25 / 1e6, cacheRead: 0.5 / 1e6, cacheWrite: 6.25 / 1e6 },
@@ -17149,6 +17471,8 @@ var MODEL_PRICING = {
17149
17471
  "claude-3-haiku": { input: 0.25 / 1e6, output: 1.25 / 1e6, cacheRead: 0.03 / 1e6, cacheWrite: 0.3 / 1e6 }
17150
17472
  };
17151
17473
  var DEFAULT_PRICING = MODEL_PRICING["claude-sonnet-4"];
17474
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
17475
+ var _spendCache = /* @__PURE__ */ new Map();
17152
17476
  function getPricing(model) {
17153
17477
  if (MODEL_PRICING[model]) return MODEL_PRICING[model];
17154
17478
  const stripped = model.replace(/-\d{8}$/, "");
@@ -17160,29 +17484,33 @@ function getPricing(model) {
17160
17484
  return DEFAULT_PRICING;
17161
17485
  }
17162
17486
  async function getAgentSpend(period = "7d") {
17487
+ const cached = _spendCache.get(period);
17488
+ if (cached && Date.now() < cached.expires) {
17489
+ return cached.result;
17490
+ }
17163
17491
  const cutoff = periodToCutoff(period);
17164
17492
  const client = getClient();
17165
- const result = await client.execute({
17493
+ const dbResult = await client.execute({
17166
17494
  sql: `SELECT session_uuid, agent_id FROM session_agent_map WHERE started_at >= ?`,
17167
17495
  args: [cutoff]
17168
17496
  });
17169
- if (result.rows.length === 0) return [];
17497
+ if (dbResult.rows.length === 0) return [];
17170
17498
  const sessionAgent = /* @__PURE__ */ new Map();
17171
- for (const row of result.rows) {
17499
+ for (const row of dbResult.rows) {
17172
17500
  sessionAgent.set(row.session_uuid, row.agent_id);
17173
17501
  }
17174
- const claudeDir = path32.join(os13.homedir(), ".claude", "projects");
17502
+ const claudeDir = path33.join(os14.homedir(), ".claude", "projects");
17175
17503
  let projectDirs = [];
17176
17504
  try {
17177
17505
  const entries = await readdir(claudeDir);
17178
- projectDirs = entries.map((e) => path32.join(claudeDir, e));
17506
+ projectDirs = entries.map((e) => path33.join(claudeDir, e));
17179
17507
  } catch {
17180
17508
  return [];
17181
17509
  }
17182
17510
  const agentTotals = /* @__PURE__ */ new Map();
17183
17511
  for (const [sessionUuid, agentId] of sessionAgent) {
17184
17512
  for (const dir of projectDirs) {
17185
- const jsonlPath = path32.join(dir, `${sessionUuid}.jsonl`);
17513
+ const jsonlPath = path33.join(dir, `${sessionUuid}.jsonl`);
17186
17514
  try {
17187
17515
  const usage = await extractSessionUsage(jsonlPath);
17188
17516
  if (usage.input === 0 && usage.output === 0) continue;
@@ -17206,7 +17534,7 @@ async function getAgentSpend(period = "7d") {
17206
17534
  }
17207
17535
  }
17208
17536
  }
17209
- return Array.from(agentTotals.entries()).map(([agentId, t]) => ({
17537
+ const result = Array.from(agentTotals.entries()).map(([agentId, t]) => ({
17210
17538
  agentId,
17211
17539
  inputTokens: t.input,
17212
17540
  outputTokens: t.output,
@@ -17216,6 +17544,8 @@ async function getAgentSpend(period = "7d") {
17216
17544
  sessions: t.sessions.size,
17217
17545
  period
17218
17546
  })).sort((a, b) => b.costUSD - a.costUSD);
17547
+ _spendCache.set(period, { result, expires: Date.now() + CACHE_TTL_MS });
17548
+ return result;
17219
17549
  }
17220
17550
  async function extractSessionUsage(jsonlPath) {
17221
17551
  let input = 0;
@@ -17760,12 +18090,12 @@ function registerExportGraph(server2) {
17760
18090
  }
17761
18091
  const html = await exportGraphHTML(client);
17762
18092
  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");
18093
+ const path45 = await import("path");
18094
+ const os19 = await import("os");
18095
+ const outDir = path45.join(os19.homedir(), ".exe-os", "exports");
17766
18096
  fs.mkdirSync(outDir, { recursive: true });
17767
18097
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
17768
- const filePath = path41.join(outDir, `graph-${timestamp}.html`);
18098
+ const filePath = path45.join(outDir, `graph-${timestamp}.html`);
17769
18099
  fs.writeFileSync(filePath, html, "utf-8");
17770
18100
  return {
17771
18101
  content: [
@@ -17986,10 +18316,10 @@ function registerListAgentSessions(server2) {
17986
18316
 
17987
18317
  // src/mcp/tools/get-daemon-health.ts
17988
18318
  import { z as z56 } from "zod";
17989
- import { existsSync as existsSync23, readFileSync as readFileSync21 } from "fs";
17990
- import path33 from "path";
18319
+ import { existsSync as existsSync25, readFileSync as readFileSync22 } from "fs";
18320
+ import path34 from "path";
17991
18321
  import { homedir as homedir3 } from "os";
17992
- var PID_PATH2 = path33.join(homedir3(), ".exe-os", "exed.pid");
18322
+ var PID_PATH2 = path34.join(homedir3(), ".exe-os", "exed.pid");
17993
18323
  function formatUptime(seconds) {
17994
18324
  const h = Math.floor(seconds / 3600);
17995
18325
  const m = Math.floor(seconds % 3600 / 60);
@@ -18000,8 +18330,8 @@ function formatUptime(seconds) {
18000
18330
  }
18001
18331
  function isDaemonAlive() {
18002
18332
  try {
18003
- if (!existsSync23(PID_PATH2)) return { alive: false, pid: null };
18004
- const pid = parseInt(readFileSync21(PID_PATH2, "utf8").trim(), 10);
18333
+ if (!existsSync25(PID_PATH2)) return { alive: false, pid: null };
18334
+ const pid = parseInt(readFileSync22(PID_PATH2, "utf8").trim(), 10);
18005
18335
  if (isNaN(pid) || pid <= 0) return { alive: false, pid: null };
18006
18336
  process.kill(pid, 0);
18007
18337
  return { alive: true, pid };
@@ -18075,7 +18405,7 @@ init_tmux_routing();
18075
18405
  init_task_scope();
18076
18406
  init_employees();
18077
18407
  import { execSync as execSync10 } from "child_process";
18078
- import { existsSync as existsSync24, readFileSync as readFileSync22, writeFileSync as writeFileSync16 } from "fs";
18408
+ import { existsSync as existsSync26, readFileSync as readFileSync23, writeFileSync as writeFileSync17 } from "fs";
18079
18409
  import { homedir as homedir4 } from "os";
18080
18410
  import { join as join2 } from "path";
18081
18411
  var IDLE_NUDGE_DEDUP_MS = Number(process.env.EXE_NUDGE_INTERVAL_MS) || 6e4;
@@ -18178,15 +18508,15 @@ function registerGetAutoWakeStatus(server2) {
18178
18508
  init_worker_gate();
18179
18509
  init_config();
18180
18510
  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");
18511
+ import { readdirSync as readdirSync11, existsSync as existsSync28 } from "fs";
18512
+ import path36 from "path";
18513
+ var WORKER_PID_DIR2 = path36.join(EXE_AI_DIR, "worker-pids");
18184
18514
  function countAliveWorkers() {
18185
18515
  let alive = 0;
18186
18516
  let stale = 0;
18187
18517
  let reservations = 0;
18188
18518
  try {
18189
- if (!existsSync26(WORKER_PID_DIR2)) return { alive: 0, stale: 0, reservations: 0 };
18519
+ if (!existsSync28(WORKER_PID_DIR2)) return { alive: 0, stale: 0, reservations: 0 };
18190
18520
  const files = readdirSync11(WORKER_PID_DIR2);
18191
18521
  for (const f of files) {
18192
18522
  if (!f.endsWith(".pid")) continue;
@@ -18256,7 +18586,7 @@ import { z as z59 } from "zod";
18256
18586
  // src/bin/exe-doctor.ts
18257
18587
  init_store();
18258
18588
  init_database();
18259
- import os14 from "os";
18589
+ import os15 from "os";
18260
18590
 
18261
18591
  // src/lib/is-main.ts
18262
18592
  import { realpathSync } from "fs";
@@ -18274,9 +18604,9 @@ function isMainModule(importMetaUrl) {
18274
18604
  }
18275
18605
 
18276
18606
  // src/bin/exe-doctor.ts
18277
- import { existsSync as existsSync27, readFileSync as readFileSync23 } from "fs";
18607
+ import { existsSync as existsSync29, readFileSync as readFileSync24 } from "fs";
18278
18608
  import { spawn as spawn2 } from "child_process";
18279
- import path36 from "path";
18609
+ import path37 from "path";
18280
18610
  import { randomUUID as randomUUID7 } from "crypto";
18281
18611
 
18282
18612
  // src/lib/conflict-detector.ts
@@ -18679,7 +19009,7 @@ async function auditOrphanedProjects(client) {
18679
19009
  for (const row of result.rows) {
18680
19010
  const name = row.project_name;
18681
19011
  const count = Number(row.cnt);
18682
- const exists = existsSync27(path36.join(home, name)) || existsSync27(path36.join(home, "..", name)) || existsSync27(path36.join(process.cwd(), "..", name));
19012
+ const exists = existsSync29(path37.join(home, name)) || existsSync29(path37.join(home, "..", name)) || existsSync29(path37.join(process.cwd(), "..", name));
18683
19013
  if (!exists) {
18684
19014
  orphans.push({ project_name: name, count });
18685
19015
  }
@@ -18687,18 +19017,18 @@ async function auditOrphanedProjects(client) {
18687
19017
  return orphans;
18688
19018
  }
18689
19019
  function auditHookHealth() {
18690
- const logPath = path36.join(
19020
+ const logPath = path37.join(
18691
19021
  process.env.HOME ?? process.env.USERPROFILE ?? "",
18692
19022
  ".exe-os",
18693
19023
  "logs",
18694
19024
  "hooks.log"
18695
19025
  );
18696
- if (!existsSync27(logPath)) {
19026
+ if (!existsSync29(logPath)) {
18697
19027
  return { logExists: false, totalLines: 0, errorsLastHour: 0, topPatterns: [] };
18698
19028
  }
18699
19029
  let content;
18700
19030
  try {
18701
- content = readFileSync23(logPath, "utf-8");
19031
+ content = readFileSync24(logPath, "utf-8");
18702
19032
  } catch {
18703
19033
  return { logExists: false, totalLines: 0, errorsLastHour: 0, topPatterns: [] };
18704
19034
  }
@@ -18773,7 +19103,7 @@ function formatReport(report, flags) {
18773
19103
  }
18774
19104
  lines.push("");
18775
19105
  }
18776
- const totalMemGB = os14.totalmem() / (1024 * 1024 * 1024);
19106
+ const totalMemGB = os15.totalmem() / (1024 * 1024 * 1024);
18777
19107
  const isLowMemSystem = totalMemGB <= 8;
18778
19108
  if (isLowMemSystem && report.nullVectors > 0) {
18779
19109
  lines.push(`\u{1F7E2} Null vectors: ${fmtNum(report.nullVectors)} / ${fmtNum(s.total)} (expected \u2014 8GB system, keyword search mode)`);
@@ -18895,7 +19225,7 @@ async function fixNullVectors() {
18895
19225
  }
18896
19226
  }
18897
19227
  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");
19228
+ const backfillPath = path37.join(npmRoot, "exe-os", "dist", "bin", "backfill-vectors.js");
18899
19229
  return new Promise((resolve, reject) => {
18900
19230
  const child = spawn2("node", [backfillPath], { stdio: "inherit" });
18901
19231
  if (child.pid) registerWorkerPid2(child.pid);
@@ -19093,13 +19423,13 @@ import { z as z60 } from "zod";
19093
19423
 
19094
19424
  // src/lib/cloud-sync.ts
19095
19425
  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";
19426
+ 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";
19427
+ import crypto18 from "crypto";
19428
+ import path39 from "path";
19099
19429
  import { homedir as homedir6 } from "os";
19100
19430
 
19101
19431
  // src/lib/crypto.ts
19102
- import crypto15 from "crypto";
19432
+ import crypto17 from "crypto";
19103
19433
  var ALGORITHM = "aes-256-gcm";
19104
19434
  var IV_LENGTH = 12;
19105
19435
  var TAG_LENGTH = 16;
@@ -19110,7 +19440,7 @@ function initSyncCrypto(masterKey) {
19110
19440
  throw new Error(`Master key must be 32 bytes, got ${masterKey.length}`);
19111
19441
  }
19112
19442
  _syncKey = Buffer.from(
19113
- crypto15.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
19443
+ crypto17.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
19114
19444
  );
19115
19445
  }
19116
19446
  function isSyncCryptoInitialized() {
@@ -19124,8 +19454,8 @@ function requireSyncKey() {
19124
19454
  }
19125
19455
  function encryptSyncBlob(data) {
19126
19456
  const key = requireSyncKey();
19127
- const iv = crypto15.randomBytes(IV_LENGTH);
19128
- const cipher = crypto15.createCipheriv(ALGORITHM, key, iv);
19457
+ const iv = crypto17.randomBytes(IV_LENGTH);
19458
+ const cipher = crypto17.createCipheriv(ALGORITHM, key, iv);
19129
19459
  const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
19130
19460
  const tag = cipher.getAuthTag();
19131
19461
  return Buffer.concat([iv, encrypted, tag]).toString("base64");
@@ -19139,7 +19469,7 @@ function decryptSyncBlob(ciphertext) {
19139
19469
  const iv = combined.subarray(0, IV_LENGTH);
19140
19470
  const tag = combined.subarray(combined.length - TAG_LENGTH);
19141
19471
  const encrypted = combined.subarray(IV_LENGTH, combined.length - TAG_LENGTH);
19142
- const decipher = crypto15.createDecipheriv(ALGORITHM, key, iv);
19472
+ const decipher = crypto17.createDecipheriv(ALGORITHM, key, iv);
19143
19473
  decipher.setAuthTag(tag);
19144
19474
  return Buffer.concat([decipher.update(encrypted), decipher.final()]);
19145
19475
  }
@@ -19164,12 +19494,13 @@ init_license();
19164
19494
  init_config();
19165
19495
  init_crdt_sync();
19166
19496
  init_employees();
19497
+ init_secure_files();
19167
19498
  function sqlSafe(v) {
19168
19499
  return v === void 0 ? null : v;
19169
19500
  }
19170
19501
  function logError(msg) {
19171
19502
  try {
19172
- const logPath = path38.join(homedir6(), ".exe-os", "workers.log");
19503
+ const logPath = path39.join(homedir6(), ".exe-os", "workers.log");
19173
19504
  appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
19174
19505
  `);
19175
19506
  } catch {
@@ -19178,24 +19509,93 @@ function logError(msg) {
19178
19509
  var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
19179
19510
  var FETCH_TIMEOUT_MS4 = 3e4;
19180
19511
  var PUSH_BATCH_SIZE = 5e3;
19181
- var ROSTER_LOCK_PATH = path38.join(EXE_AI_DIR, "roster-merge.lock");
19512
+ var ROSTER_LOCK_PATH = path39.join(EXE_AI_DIR, "roster-merge.lock");
19182
19513
  var LOCK_STALE_MS = 3e4;
19514
+ var _pgPromise = null;
19515
+ var _pgFailed = false;
19516
+ function loadPgClient() {
19517
+ if (_pgFailed) return null;
19518
+ const postgresUrl = process.env.DATABASE_URL;
19519
+ const configPath = path39.join(EXE_AI_DIR, "config.json");
19520
+ let cloudPostgresUrl;
19521
+ try {
19522
+ if (existsSync31(configPath)) {
19523
+ const cfg = JSON.parse(readFileSync26(configPath, "utf8"));
19524
+ cloudPostgresUrl = cfg.cloud?.postgresUrl;
19525
+ if (cfg.cloud?.syncToPostgres === false) {
19526
+ _pgFailed = true;
19527
+ return null;
19528
+ }
19529
+ }
19530
+ } catch {
19531
+ }
19532
+ const url = postgresUrl || cloudPostgresUrl;
19533
+ if (!url) {
19534
+ _pgFailed = true;
19535
+ return null;
19536
+ }
19537
+ if (!_pgPromise) {
19538
+ _pgPromise = (async () => {
19539
+ const { createRequire: createRequire6 } = await import("module");
19540
+ const { pathToFileURL: pathToFileURL6 } = await import("url");
19541
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path39.join(homedir6(), "exe-db");
19542
+ const req = createRequire6(path39.join(exeDbRoot, "package.json"));
19543
+ const entry = req.resolve("@prisma/client");
19544
+ const mod = await import(pathToFileURL6(entry).href);
19545
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
19546
+ if (!Ctor) throw new Error("No PrismaClient");
19547
+ return new Ctor();
19548
+ })().catch(() => {
19549
+ _pgFailed = true;
19550
+ _pgPromise = null;
19551
+ throw new Error("pg_unavailable");
19552
+ });
19553
+ }
19554
+ return _pgPromise;
19555
+ }
19556
+ async function pushToPostgres(records) {
19557
+ const loader = loadPgClient();
19558
+ if (!loader) return 0;
19559
+ let prisma;
19560
+ try {
19561
+ prisma = await loader;
19562
+ } catch {
19563
+ return 0;
19564
+ }
19565
+ let inserted = 0;
19566
+ for (const rec of records) {
19567
+ try {
19568
+ await prisma.$executeRawUnsafe(
19569
+ `INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
19570
+ VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
19571
+ ON CONFLICT (source, source_id, event_type) DO NOTHING`,
19572
+ String(rec.id ?? ""),
19573
+ JSON.stringify(rec),
19574
+ JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
19575
+ rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
19576
+ );
19577
+ inserted++;
19578
+ } catch {
19579
+ }
19580
+ }
19581
+ return inserted;
19582
+ }
19183
19583
  async function withRosterLock(fn) {
19184
19584
  try {
19185
19585
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
19186
19586
  closeSync2(fd);
19187
- writeFileSync19(ROSTER_LOCK_PATH, String(Date.now()));
19587
+ writeFileSync20(ROSTER_LOCK_PATH, String(Date.now()));
19188
19588
  } catch (err) {
19189
19589
  if (err.code === "EEXIST") {
19190
19590
  try {
19191
- const ts2 = parseInt(readFileSync25(ROSTER_LOCK_PATH, "utf-8"), 10);
19591
+ const ts2 = parseInt(readFileSync26(ROSTER_LOCK_PATH, "utf-8"), 10);
19192
19592
  if (Date.now() - ts2 < LOCK_STALE_MS) {
19193
19593
  throw new Error("Roster merge already in progress \u2014 another sync is running");
19194
19594
  }
19195
19595
  unlinkSync10(ROSTER_LOCK_PATH);
19196
19596
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
19197
19597
  closeSync2(fd);
19198
- writeFileSync19(ROSTER_LOCK_PATH, String(Date.now()));
19598
+ writeFileSync20(ROSTER_LOCK_PATH, String(Date.now()));
19199
19599
  } catch (retryErr) {
19200
19600
  if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
19201
19601
  throw new Error("Roster merge already in progress \u2014 another sync is running");
@@ -19465,6 +19865,10 @@ async function cloudSync(config2) {
19465
19865
  const maxVersion = Number(records[records.length - 1].version);
19466
19866
  const pushOk = await cloudPush(records, maxVersion, config2);
19467
19867
  if (!pushOk) break;
19868
+ try {
19869
+ await pushToPostgres(records);
19870
+ } catch {
19871
+ }
19468
19872
  await client.execute({
19469
19873
  sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
19470
19874
  args: [String(maxVersion)]
@@ -19569,8 +19973,8 @@ async function cloudSync(config2) {
19569
19973
  try {
19570
19974
  const employees = await loadEmployees();
19571
19975
  rosterResult.employees = employees.length;
19572
- const idDir = path38.join(EXE_AI_DIR, "identity");
19573
- if (existsSync29(idDir)) {
19976
+ const idDir = path39.join(EXE_AI_DIR, "identity");
19977
+ if (existsSync31(idDir)) {
19574
19978
  rosterResult.identities = readdirSync12(idDir).filter((f) => f.endsWith(".md")).length;
19575
19979
  }
19576
19980
  } catch {
@@ -19588,55 +19992,55 @@ async function cloudSync(config2) {
19588
19992
  roster: rosterResult
19589
19993
  };
19590
19994
  }
19591
- var ROSTER_DELETIONS_PATH = path38.join(EXE_AI_DIR, "roster-deletions.json");
19995
+ var ROSTER_DELETIONS_PATH = path39.join(EXE_AI_DIR, "roster-deletions.json");
19592
19996
  function consumeRosterDeletions() {
19593
19997
  try {
19594
- if (!existsSync29(ROSTER_DELETIONS_PATH)) return [];
19595
- const deletions = JSON.parse(readFileSync25(ROSTER_DELETIONS_PATH, "utf-8"));
19596
- writeFileSync19(ROSTER_DELETIONS_PATH, "[]");
19998
+ if (!existsSync31(ROSTER_DELETIONS_PATH)) return [];
19999
+ const deletions = JSON.parse(readFileSync26(ROSTER_DELETIONS_PATH, "utf-8"));
20000
+ writeFileSync20(ROSTER_DELETIONS_PATH, "[]");
19597
20001
  return deletions;
19598
20002
  } catch {
19599
20003
  return [];
19600
20004
  }
19601
20005
  }
19602
20006
  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");
20007
+ const rosterPath = paths?.rosterPath ?? path39.join(EXE_AI_DIR, "exe-employees.json");
20008
+ const identityDir = paths?.identityDir ?? path39.join(EXE_AI_DIR, "identity");
20009
+ const configPath = paths?.configPath ?? path39.join(EXE_AI_DIR, "config.json");
19606
20010
  let roster = [];
19607
- if (existsSync29(rosterPath)) {
20011
+ if (existsSync31(rosterPath)) {
19608
20012
  try {
19609
- roster = JSON.parse(readFileSync25(rosterPath, "utf-8"));
20013
+ roster = JSON.parse(readFileSync26(rosterPath, "utf-8"));
19610
20014
  } catch {
19611
20015
  }
19612
20016
  }
19613
20017
  const identities = {};
19614
- if (existsSync29(identityDir)) {
20018
+ if (existsSync31(identityDir)) {
19615
20019
  for (const file of readdirSync12(identityDir).filter((f) => f.endsWith(".md"))) {
19616
20020
  try {
19617
- identities[file] = readFileSync25(path38.join(identityDir, file), "utf-8");
20021
+ identities[file] = readFileSync26(path39.join(identityDir, file), "utf-8");
19618
20022
  } catch {
19619
20023
  }
19620
20024
  }
19621
20025
  }
19622
20026
  let config2;
19623
- if (existsSync29(configPath)) {
20027
+ if (existsSync31(configPath)) {
19624
20028
  try {
19625
- config2 = JSON.parse(readFileSync25(configPath, "utf-8"));
20029
+ config2 = JSON.parse(readFileSync26(configPath, "utf-8"));
19626
20030
  } catch {
19627
20031
  }
19628
20032
  }
19629
20033
  let agentConfig;
19630
- const agentConfigPath = path38.join(EXE_AI_DIR, "agent-config.json");
19631
- if (existsSync29(agentConfigPath)) {
20034
+ const agentConfigPath = path39.join(EXE_AI_DIR, "agent-config.json");
20035
+ if (existsSync31(agentConfigPath)) {
19632
20036
  try {
19633
- agentConfig = JSON.parse(readFileSync25(agentConfigPath, "utf-8"));
20037
+ agentConfig = JSON.parse(readFileSync26(agentConfigPath, "utf-8"));
19634
20038
  } catch {
19635
20039
  }
19636
20040
  }
19637
20041
  const deletedNames = consumeRosterDeletions();
19638
20042
  const content = JSON.stringify({ roster, identities, config: config2, agentConfig, deletedNames });
19639
- const hash = crypto16.createHash("sha256").update(content).digest("hex").slice(0, 16);
20043
+ const hash = crypto18.createHash("sha256").update(content).digest("hex").slice(0, 16);
19640
20044
  return { roster, identities, config: config2, agentConfig, deletedNames, version: hash };
19641
20045
  }
19642
20046
  async function cloudPushRoster(config2) {
@@ -19706,23 +20110,24 @@ async function cloudPullRoster(config2) {
19706
20110
  }
19707
20111
  }
19708
20112
  function mergeConfig(remoteConfig, configPath) {
19709
- const cfgPath = configPath ?? path38.join(EXE_AI_DIR, "config.json");
20113
+ const cfgPath = configPath ?? path39.join(EXE_AI_DIR, "config.json");
19710
20114
  let local = {};
19711
- if (existsSync29(cfgPath)) {
20115
+ if (existsSync31(cfgPath)) {
19712
20116
  try {
19713
- local = JSON.parse(readFileSync25(cfgPath, "utf-8"));
20117
+ local = JSON.parse(readFileSync26(cfgPath, "utf-8"));
19714
20118
  } catch {
19715
20119
  }
19716
20120
  }
19717
20121
  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");
20122
+ const dir = path39.dirname(cfgPath);
20123
+ ensurePrivateDirSync(dir);
20124
+ writeFileSync20(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
20125
+ enforcePrivateFileSync(cfgPath);
19721
20126
  }
19722
20127
  async function mergeRosterFromRemote(remote, paths) {
19723
20128
  return withRosterLock(async () => {
19724
20129
  const rosterPath = paths?.rosterPath ?? void 0;
19725
- const identityDir = paths?.identityDir ?? path38.join(EXE_AI_DIR, "identity");
20130
+ const identityDir = paths?.identityDir ?? path39.join(EXE_AI_DIR, "identity");
19726
20131
  const localEmployees = await loadEmployees(rosterPath);
19727
20132
  const localNames = new Set(localEmployees.map((e) => e.name));
19728
20133
  let added = 0;
@@ -19743,15 +20148,15 @@ async function mergeRosterFromRemote(remote, paths) {
19743
20148
  ) ?? lookupKey;
19744
20149
  const remoteIdentity = remote.identities[matchedKey];
19745
20150
  if (remoteIdentity) {
19746
- if (!existsSync29(identityDir)) mkdirSync16(identityDir, { recursive: true });
19747
- const idPath = path38.join(identityDir, `${remoteEmp.name}.md`);
20151
+ if (!existsSync31(identityDir)) mkdirSync16(identityDir, { recursive: true });
20152
+ const idPath = path39.join(identityDir, `${remoteEmp.name}.md`);
19748
20153
  let localIdentity = null;
19749
20154
  try {
19750
- localIdentity = existsSync29(idPath) ? readFileSync25(idPath, "utf-8") : null;
20155
+ localIdentity = existsSync31(idPath) ? readFileSync26(idPath, "utf-8") : null;
19751
20156
  } catch {
19752
20157
  }
19753
20158
  if (localIdentity !== remoteIdentity) {
19754
- writeFileSync19(idPath, remoteIdentity, "utf-8");
20159
+ writeFileSync20(idPath, remoteIdentity, "utf-8");
19755
20160
  identitiesUpdated++;
19756
20161
  }
19757
20162
  }
@@ -19777,16 +20182,18 @@ async function mergeRosterFromRemote(remote, paths) {
19777
20182
  }
19778
20183
  if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
19779
20184
  try {
19780
- const agentConfigPath = path38.join(EXE_AI_DIR, "agent-config.json");
20185
+ const agentConfigPath = path39.join(EXE_AI_DIR, "agent-config.json");
19781
20186
  let local = {};
19782
- if (existsSync29(agentConfigPath)) {
20187
+ if (existsSync31(agentConfigPath)) {
19783
20188
  try {
19784
- local = JSON.parse(readFileSync25(agentConfigPath, "utf-8"));
20189
+ local = JSON.parse(readFileSync26(agentConfigPath, "utf-8"));
19785
20190
  } catch {
19786
20191
  }
19787
20192
  }
19788
20193
  const merged = { ...remote.agentConfig, ...local };
19789
- writeFileSync19(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
20194
+ ensurePrivateDirSync(path39.dirname(agentConfigPath));
20195
+ writeFileSync20(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
20196
+ enforcePrivateFileSync(agentConfigPath);
19790
20197
  } catch {
19791
20198
  }
19792
20199
  }
@@ -21012,11 +21419,11 @@ import { z as z65 } from "zod";
21012
21419
  // src/lib/people.ts
21013
21420
  init_config();
21014
21421
  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");
21422
+ import { existsSync as existsSync32, readFileSync as readFileSync27 } from "fs";
21423
+ import path40 from "path";
21424
+ var PEOPLE_PATH = path40.join(EXE_AI_DIR, "people.json");
21018
21425
  async function loadPeople() {
21019
- if (!existsSync30(PEOPLE_PATH)) return [];
21426
+ if (!existsSync32(PEOPLE_PATH)) return [];
21020
21427
  try {
21021
21428
  const raw = await readFile5(PEOPLE_PATH, "utf-8");
21022
21429
  return JSON.parse(raw);
@@ -21025,7 +21432,7 @@ async function loadPeople() {
21025
21432
  }
21026
21433
  }
21027
21434
  async function savePeople(people) {
21028
- await mkdir5(path39.dirname(PEOPLE_PATH), { recursive: true });
21435
+ await mkdir5(path40.dirname(PEOPLE_PATH), { recursive: true });
21029
21436
  await writeFile6(PEOPLE_PATH, JSON.stringify(people, null, 2) + "\n", "utf-8");
21030
21437
  }
21031
21438
  async function addPerson(person) {
@@ -21306,6 +21713,331 @@ function registerListEmployees(server2) {
21306
21713
  );
21307
21714
  }
21308
21715
 
21716
+ // src/mcp/tools/ingest-raw.ts
21717
+ import os16 from "os";
21718
+ import path41 from "path";
21719
+ import { createRequire as createRequire3 } from "module";
21720
+ import { pathToFileURL as pathToFileURL3 } from "url";
21721
+ import { z as z68 } from "zod";
21722
+ var prismaPromise = null;
21723
+ function loadPrisma() {
21724
+ if (!prismaPromise) {
21725
+ prismaPromise = (async () => {
21726
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
21727
+ if (explicitPath) {
21728
+ const mod2 = await import(pathToFileURL3(explicitPath).href);
21729
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
21730
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
21731
+ return new Ctor2();
21732
+ }
21733
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path41.join(os16.homedir(), "exe-db");
21734
+ const req = createRequire3(path41.join(exeDbRoot, "package.json"));
21735
+ const entry = req.resolve("@prisma/client");
21736
+ const mod = await import(pathToFileURL3(entry).href);
21737
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
21738
+ if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
21739
+ return new Ctor();
21740
+ })();
21741
+ }
21742
+ return prismaPromise;
21743
+ }
21744
+ function registerIngestRaw(server2) {
21745
+ server2.registerTool(
21746
+ "ingest_raw",
21747
+ {
21748
+ title: "Ingest Raw Event",
21749
+ 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.",
21750
+ inputSchema: {
21751
+ source: z68.string().describe(
21752
+ 'Event source identifier: "whatsapp", "shopify", "asana", "email", "external_agent", etc.'
21753
+ ),
21754
+ event_type: z68.string().describe(
21755
+ 'Event type: "message", "order", "task_update", "webhook", etc.'
21756
+ ),
21757
+ payload: z68.record(z68.string(), z68.unknown()).describe(
21758
+ "Exact raw JSON payload \u2014 stored verbatim, never modified."
21759
+ ),
21760
+ source_id: z68.string().optional().describe(
21761
+ "External reference ID for deduplication (e.g., WhatsApp message ID, Shopify order ID)."
21762
+ ),
21763
+ metadata: z68.record(z68.string(), z68.unknown()).optional().describe(
21764
+ "Source-specific metadata (account info, adapter version, etc.)."
21765
+ )
21766
+ }
21767
+ },
21768
+ async ({ source, event_type, payload, source_id, metadata }) => {
21769
+ try {
21770
+ const prisma = await loadPrisma();
21771
+ const id = crypto.randomUUID();
21772
+ await prisma.$executeRawUnsafe(
21773
+ `INSERT INTO "raw"."raw_events" ("id", "source", "source_id", "event_type", "payload", "metadata", "timestamp")
21774
+ VALUES ($1, $2, $3, $4, $5::jsonb, $6::jsonb, NOW())
21775
+ ON CONFLICT ("source", "source_id", "event_type") DO NOTHING`,
21776
+ id,
21777
+ source,
21778
+ source_id ?? null,
21779
+ event_type,
21780
+ JSON.stringify(payload),
21781
+ metadata ? JSON.stringify(metadata) : null
21782
+ );
21783
+ return {
21784
+ content: [{
21785
+ type: "text",
21786
+ text: JSON.stringify({
21787
+ event_id: id,
21788
+ source,
21789
+ event_type,
21790
+ source_id: source_id ?? null,
21791
+ status: "ingested"
21792
+ })
21793
+ }]
21794
+ };
21795
+ } catch (err) {
21796
+ const message = err instanceof Error ? err.message : String(err);
21797
+ return {
21798
+ content: [{
21799
+ type: "text",
21800
+ text: `Error ingesting raw event: ${message}`
21801
+ }],
21802
+ isError: true
21803
+ };
21804
+ }
21805
+ }
21806
+ );
21807
+ }
21808
+
21809
+ // src/mcp/tools/create-license.ts
21810
+ init_license();
21811
+ import os17 from "os";
21812
+ import path42 from "path";
21813
+ import { randomBytes as randomBytes2, randomUUID as randomUUID8 } from "crypto";
21814
+ import { createRequire as createRequire4 } from "module";
21815
+ import { pathToFileURL as pathToFileURL4 } from "url";
21816
+ import { z as z69 } from "zod";
21817
+ var prismaPromise2 = null;
21818
+ function loadPrisma2() {
21819
+ if (!prismaPromise2) {
21820
+ prismaPromise2 = (async () => {
21821
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
21822
+ if (explicitPath) {
21823
+ const mod2 = await import(pathToFileURL4(explicitPath).href);
21824
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
21825
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
21826
+ return new Ctor2();
21827
+ }
21828
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path42.join(os17.homedir(), "exe-db");
21829
+ const req = createRequire4(path42.join(exeDbRoot, "package.json"));
21830
+ const entry = req.resolve("@prisma/client");
21831
+ const mod = await import(pathToFileURL4(entry).href);
21832
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
21833
+ if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
21834
+ return new Ctor();
21835
+ })();
21836
+ }
21837
+ return prismaPromise2;
21838
+ }
21839
+ function generateKey() {
21840
+ return `exe_sk_${randomBytes2(16).toString("hex")}`;
21841
+ }
21842
+ function registerCreateLicense(server2) {
21843
+ server2.registerTool(
21844
+ "create_license",
21845
+ {
21846
+ title: "Create License",
21847
+ description: "Generate an exe_sk_* license key for a user. Stores in billing.licenses. Returns the key to give to the customer.",
21848
+ inputSchema: {
21849
+ email: z69.string().email().describe("Customer email address"),
21850
+ name: z69.string().optional().describe("Customer name"),
21851
+ plan: z69.enum(["free", "pro", "team", "agency", "enterprise"]).default("pro").describe("License plan tier"),
21852
+ expires_in_days: z69.number().int().positive().default(365).describe("Days until expiration (default 365)")
21853
+ }
21854
+ },
21855
+ async ({ email, name, plan, expires_in_days }) => {
21856
+ try {
21857
+ const prisma = await loadPrisma2();
21858
+ const id = randomUUID8();
21859
+ const key = generateKey();
21860
+ const limits = PLAN_LIMITS[plan];
21861
+ const expiresAt = new Date(
21862
+ Date.now() + (expires_in_days ?? 365) * 24 * 60 * 60 * 1e3
21863
+ );
21864
+ await prisma.$executeRawUnsafe(
21865
+ `INSERT INTO billing.licenses (id, key, email, name, plan, status, device_limit, employee_limit, memory_limit, expires_at, created_at, created_by)
21866
+ VALUES ($1, $2, $3, $4, $5, 'active', $6, $7, $8, $9, NOW(), $10)`,
21867
+ id,
21868
+ key,
21869
+ email,
21870
+ name ?? null,
21871
+ plan,
21872
+ limits.devices,
21873
+ limits.employees,
21874
+ limits.memories,
21875
+ expiresAt,
21876
+ "exe"
21877
+ );
21878
+ const lines = [];
21879
+ lines.push("## License Created\n");
21880
+ lines.push(`- **Key:** \`${key}\``);
21881
+ lines.push(`- **Email:** ${email}`);
21882
+ if (name) lines.push(`- **Name:** ${name}`);
21883
+ lines.push(`- **Plan:** ${plan}`);
21884
+ lines.push(`- **Expires:** ${expiresAt.toISOString().split("T")[0]}`);
21885
+ lines.push(`- **Devices:** ${limits.devices === -1 ? "unlimited" : limits.devices}`);
21886
+ lines.push(`- **Employees:** ${limits.employees === -1 ? "unlimited" : limits.employees}`);
21887
+ lines.push(`- **Memories:** ${limits.memories === -1 ? "unlimited" : limits.memories.toLocaleString()}`);
21888
+ lines.push(`
21889
+ Give this key to the customer. They paste it during \`exe-os setup\`.`);
21890
+ return { content: [{ type: "text", text: lines.join("\n") }] };
21891
+ } catch (err) {
21892
+ const msg = err instanceof Error ? err.message : String(err);
21893
+ return {
21894
+ content: [{ type: "text", text: `Error creating license: ${msg}` }],
21895
+ isError: true
21896
+ };
21897
+ }
21898
+ }
21899
+ );
21900
+ }
21901
+
21902
+ // src/mcp/tools/list-licenses.ts
21903
+ import os18 from "os";
21904
+ import path43 from "path";
21905
+ import { createRequire as createRequire5 } from "module";
21906
+ import { pathToFileURL as pathToFileURL5 } from "url";
21907
+ import { z as z70 } from "zod";
21908
+ var prismaPromise3 = null;
21909
+ function loadPrisma3() {
21910
+ if (!prismaPromise3) {
21911
+ prismaPromise3 = (async () => {
21912
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
21913
+ if (explicitPath) {
21914
+ const mod2 = await import(pathToFileURL5(explicitPath).href);
21915
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
21916
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
21917
+ return new Ctor2();
21918
+ }
21919
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path43.join(os18.homedir(), "exe-db");
21920
+ const req = createRequire5(path43.join(exeDbRoot, "package.json"));
21921
+ const entry = req.resolve("@prisma/client");
21922
+ const mod = await import(pathToFileURL5(entry).href);
21923
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
21924
+ if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
21925
+ return new Ctor();
21926
+ })();
21927
+ }
21928
+ return prismaPromise3;
21929
+ }
21930
+ function registerListLicenses(server2) {
21931
+ server2.registerTool(
21932
+ "list_licenses",
21933
+ {
21934
+ title: "List Licenses",
21935
+ description: "List all issued licenses with status (active/expired/revoked). Optionally filter by plan.",
21936
+ inputSchema: {
21937
+ plan: z70.enum(["free", "pro", "team", "agency", "enterprise"]).optional().describe("Filter by plan tier (omit for all)")
21938
+ }
21939
+ },
21940
+ async ({ plan }) => {
21941
+ try {
21942
+ const prisma = await loadPrisma3();
21943
+ let rows;
21944
+ if (plan) {
21945
+ rows = await prisma.$queryRawUnsafe(
21946
+ `SELECT id, key, email, name, plan, status, device_limit, employee_limit, memory_limit, expires_at, created_at, created_by
21947
+ FROM billing.licenses WHERE plan = $1 ORDER BY created_at DESC`,
21948
+ plan
21949
+ );
21950
+ } else {
21951
+ rows = await prisma.$queryRawUnsafe(
21952
+ `SELECT id, key, email, name, plan, status, device_limit, employee_limit, memory_limit, expires_at, created_at, created_by
21953
+ FROM billing.licenses ORDER BY created_at DESC`
21954
+ );
21955
+ }
21956
+ if (!rows || rows.length === 0) {
21957
+ return {
21958
+ content: [{ type: "text", text: "No licenses found." }]
21959
+ };
21960
+ }
21961
+ const lines = [];
21962
+ lines.push(`## Licenses (${rows.length} total)
21963
+ `);
21964
+ lines.push("| Email | Plan | Status | Key | Expires |");
21965
+ lines.push("|-------|------|--------|-----|---------|");
21966
+ for (const row of rows) {
21967
+ const keyShort = `${row.key.slice(0, 11)}...${row.key.slice(-4)}`;
21968
+ const expires = row.expires_at ? new Date(row.expires_at).toISOString().split("T")[0] : "never";
21969
+ const effectiveStatus = row.status === "active" && row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date() ? "expired" : row.status;
21970
+ lines.push(
21971
+ `| ${row.email} | ${row.plan} | ${effectiveStatus} | \`${keyShort}\` | ${expires} |`
21972
+ );
21973
+ }
21974
+ return { content: [{ type: "text", text: lines.join("\n") }] };
21975
+ } catch (err) {
21976
+ const msg = err instanceof Error ? err.message : String(err);
21977
+ return {
21978
+ content: [{ type: "text", text: `Error listing licenses: ${msg}` }],
21979
+ isError: true
21980
+ };
21981
+ }
21982
+ }
21983
+ );
21984
+ }
21985
+
21986
+ // src/mcp/tools/activate-license.ts
21987
+ init_license();
21988
+ import { z as z71 } from "zod";
21989
+ function registerActivateLicense(server2) {
21990
+ server2.registerTool(
21991
+ "activate_license",
21992
+ {
21993
+ title: "Activate License",
21994
+ description: "Activate an exe_sk_* license key on this device. Writes to ~/.exe-os/license.key, validates against Postgres, and caches the result.",
21995
+ inputSchema: {
21996
+ key: z71.string().startsWith("exe_sk_").describe("License key (exe_sk_*)")
21997
+ }
21998
+ },
21999
+ async ({ key }) => {
22000
+ try {
22001
+ saveLicense(key);
22002
+ const deviceId = loadDeviceId();
22003
+ const license = await validateLicense(key, deviceId);
22004
+ const lines = [];
22005
+ if (license.valid) {
22006
+ lines.push("## License Activated\n");
22007
+ lines.push(`- **Plan:** ${license.plan}`);
22008
+ lines.push(`- **Email:** ${license.email || "N/A"}`);
22009
+ lines.push(
22010
+ `- **Expires:** ${license.expiresAt ? license.expiresAt.split("T")[0] : "never"}`
22011
+ );
22012
+ lines.push(
22013
+ `- **Devices:** ${license.deviceLimit === -1 ? "unlimited" : license.deviceLimit}`
22014
+ );
22015
+ lines.push(
22016
+ `- **Employees:** ${license.employeeLimit === -1 ? "unlimited" : license.employeeLimit}`
22017
+ );
22018
+ lines.push(
22019
+ `- **Memories:** ${license.memoryLimit === -1 ? "unlimited" : license.memoryLimit.toLocaleString()}`
22020
+ );
22021
+ lines.push(`
22022
+ Key saved to ~/.exe-os/license.key. Device ID: ${deviceId}`);
22023
+ } else {
22024
+ lines.push("## Activation Failed\n");
22025
+ lines.push("Key was saved but could not be validated.");
22026
+ lines.push("Check that the key is correct and try again later.");
22027
+ lines.push(`Plan fell back to: ${license.plan}`);
22028
+ }
22029
+ return { content: [{ type: "text", text: lines.join("\n") }] };
22030
+ } catch (err) {
22031
+ const msg = err instanceof Error ? err.message : String(err);
22032
+ return {
22033
+ content: [{ type: "text", text: `Error activating license: ${msg}` }],
22034
+ isError: true
22035
+ };
22036
+ }
22037
+ }
22038
+ );
22039
+ }
22040
+
21309
22041
  // src/lib/telemetry.ts
21310
22042
  var ENABLED = process.env.EXE_TELEMETRY === "1";
21311
22043
  var initialized = false;
@@ -21442,6 +22174,10 @@ registerListPeople(server);
21442
22174
  registerGetPerson(server);
21443
22175
  registerSetAgentConfig(server);
21444
22176
  registerListEmployees(server);
22177
+ registerIngestRaw(server);
22178
+ registerCreateLicense(server);
22179
+ registerListLicenses(server);
22180
+ registerActivateLicense(server);
21445
22181
  try {
21446
22182
  await initStore();
21447
22183
  process.stderr.write("[exe-os] MCP server starting...\n");
@@ -21510,14 +22246,14 @@ try {
21510
22246
  `
21511
22247
  );
21512
22248
  const thisFile = fileURLToPath5(import.meta.url);
21513
- const backfillPath = path40.resolve(
21514
- path40.dirname(thisFile),
22249
+ const backfillPath = path44.resolve(
22250
+ path44.dirname(thisFile),
21515
22251
  "../bin/backfill-vectors.js"
21516
22252
  );
21517
- if (existsSync31(backfillPath)) {
22253
+ if (existsSync33(backfillPath)) {
21518
22254
  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 });
22255
+ const logPath = path44.join(exeDir, "workers.log");
22256
+ mkdirSync17(path44.dirname(logPath), { recursive: true });
21521
22257
  let logFd = "ignore";
21522
22258
  try {
21523
22259
  logFd = openSync3(logPath, "a");