@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
@@ -25,9 +25,47 @@ var __copyProps = (to, from, except, desc) => {
25
25
  };
26
26
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
27
 
28
+ // src/lib/secure-files.ts
29
+ import { chmodSync, existsSync, mkdirSync } from "fs";
30
+ import { chmod, mkdir } from "fs/promises";
31
+ async function ensurePrivateDir(dirPath) {
32
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
33
+ try {
34
+ await chmod(dirPath, PRIVATE_DIR_MODE);
35
+ } catch {
36
+ }
37
+ }
38
+ function ensurePrivateDirSync(dirPath) {
39
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
40
+ try {
41
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
42
+ } catch {
43
+ }
44
+ }
45
+ async function enforcePrivateFile(filePath) {
46
+ try {
47
+ await chmod(filePath, PRIVATE_FILE_MODE);
48
+ } catch {
49
+ }
50
+ }
51
+ function enforcePrivateFileSync(filePath) {
52
+ try {
53
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
54
+ } catch {
55
+ }
56
+ }
57
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
58
+ var init_secure_files = __esm({
59
+ "src/lib/secure-files.ts"() {
60
+ "use strict";
61
+ PRIVATE_DIR_MODE = 448;
62
+ PRIVATE_FILE_MODE = 384;
63
+ }
64
+ });
65
+
28
66
  // src/lib/config.ts
29
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
30
- import { readFileSync, existsSync, renameSync } from "fs";
67
+ import { readFile, writeFile } from "fs/promises";
68
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
31
69
  import path from "path";
32
70
  import os from "os";
33
71
  function resolveDataDir() {
@@ -35,7 +73,7 @@ function resolveDataDir() {
35
73
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
36
74
  const newDir = path.join(os.homedir(), ".exe-os");
37
75
  const legacyDir = path.join(os.homedir(), ".exe-mem");
38
- if (!existsSync(newDir) && existsSync(legacyDir)) {
76
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
39
77
  try {
40
78
  renameSync(legacyDir, newDir);
41
79
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -98,9 +136,9 @@ function normalizeAutoUpdate(raw) {
98
136
  }
99
137
  async function loadConfig() {
100
138
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
101
- await mkdir(dir, { recursive: true });
139
+ await ensurePrivateDir(dir);
102
140
  const configPath = path.join(dir, "config.json");
103
- if (!existsSync(configPath)) {
141
+ if (!existsSync2(configPath)) {
104
142
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
105
143
  }
106
144
  const raw = await readFile(configPath, "utf-8");
@@ -113,6 +151,7 @@ async function loadConfig() {
113
151
  `);
114
152
  try {
115
153
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
154
+ await enforcePrivateFile(configPath);
116
155
  } catch {
117
156
  }
118
157
  }
@@ -132,6 +171,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
132
171
  var init_config = __esm({
133
172
  "src/lib/config.ts"() {
134
173
  "use strict";
174
+ init_secure_files();
135
175
  EXE_AI_DIR = resolveDataDir();
136
176
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
137
177
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -278,7 +318,7 @@ var init_session_key = __esm({
278
318
 
279
319
  // src/lib/employees.ts
280
320
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
281
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
321
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
282
322
  import { execSync as execSync2 } from "child_process";
283
323
  import path2 from "path";
284
324
  import os2 from "os";
@@ -299,7 +339,7 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
299
339
  return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
300
340
  }
301
341
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
302
- if (!existsSync2(employeesPath)) return [];
342
+ if (!existsSync3(employeesPath)) return [];
303
343
  try {
304
344
  return JSON.parse(readFileSync2(employeesPath, "utf-8"));
305
345
  } catch {
@@ -337,13 +377,13 @@ var init_employees = __esm({
337
377
  });
338
378
 
339
379
  // src/lib/session-registry.ts
340
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "fs";
380
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync4 } from "fs";
341
381
  import path4 from "path";
342
382
  import os3 from "os";
343
383
  function registerSession(entry) {
344
384
  const dir = path4.dirname(REGISTRY_PATH);
345
- if (!existsSync3(dir)) {
346
- mkdirSync2(dir, { recursive: true });
385
+ if (!existsSync4(dir)) {
386
+ mkdirSync3(dir, { recursive: true });
347
387
  }
348
388
  const sessions = listSessions();
349
389
  const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
@@ -578,10 +618,10 @@ var init_runtime_table = __esm({
578
618
  });
579
619
 
580
620
  // src/lib/agent-config.ts
581
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
621
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync5 } from "fs";
582
622
  import path5 from "path";
583
623
  function loadAgentConfig() {
584
- if (!existsSync4(AGENT_CONFIG_PATH)) return {};
624
+ if (!existsSync5(AGENT_CONFIG_PATH)) return {};
585
625
  try {
586
626
  return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
587
627
  } catch {
@@ -602,6 +642,7 @@ var init_agent_config = __esm({
602
642
  "use strict";
603
643
  init_config();
604
644
  init_runtime_table();
645
+ init_secure_files();
605
646
  AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
606
647
  DEFAULT_MODELS = {
607
648
  claude: "claude-opus-4",
@@ -620,16 +661,16 @@ __export(intercom_queue_exports, {
620
661
  queueIntercom: () => queueIntercom,
621
662
  readQueue: () => readQueue
622
663
  });
623
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync5, mkdirSync as mkdirSync4 } from "fs";
664
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
624
665
  import path6 from "path";
625
666
  import os4 from "os";
626
667
  function ensureDir() {
627
668
  const dir = path6.dirname(QUEUE_PATH);
628
- if (!existsSync5(dir)) mkdirSync4(dir, { recursive: true });
669
+ if (!existsSync6(dir)) mkdirSync4(dir, { recursive: true });
629
670
  }
630
671
  function readQueue() {
631
672
  try {
632
- if (!existsSync5(QUEUE_PATH)) return [];
673
+ if (!existsSync6(QUEUE_PATH)) return [];
633
674
  return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
634
675
  } catch {
635
676
  return [];
@@ -1376,13 +1417,50 @@ var init_database_adapter = __esm({
1376
1417
  }
1377
1418
  });
1378
1419
 
1420
+ // src/lib/daemon-auth.ts
1421
+ import crypto from "crypto";
1422
+ import path8 from "path";
1423
+ import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
1424
+ function normalizeToken(token) {
1425
+ if (!token) return null;
1426
+ const trimmed = token.trim();
1427
+ return trimmed.length > 0 ? trimmed : null;
1428
+ }
1429
+ function readDaemonToken() {
1430
+ try {
1431
+ if (!existsSync7(DAEMON_TOKEN_PATH)) return null;
1432
+ return normalizeToken(readFileSync7(DAEMON_TOKEN_PATH, "utf8"));
1433
+ } catch {
1434
+ return null;
1435
+ }
1436
+ }
1437
+ function ensureDaemonToken(seed) {
1438
+ const existing = readDaemonToken();
1439
+ if (existing) return existing;
1440
+ const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
1441
+ ensurePrivateDirSync(EXE_AI_DIR);
1442
+ writeFileSync6(DAEMON_TOKEN_PATH, `${token}
1443
+ `, "utf8");
1444
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
1445
+ return token;
1446
+ }
1447
+ var DAEMON_TOKEN_PATH;
1448
+ var init_daemon_auth = __esm({
1449
+ "src/lib/daemon-auth.ts"() {
1450
+ "use strict";
1451
+ init_config();
1452
+ init_secure_files();
1453
+ DAEMON_TOKEN_PATH = path8.join(EXE_AI_DIR, "exed.token");
1454
+ }
1455
+ });
1456
+
1379
1457
  // src/lib/exe-daemon-client.ts
1380
1458
  import net from "net";
1381
1459
  import os6 from "os";
1382
1460
  import { spawn } from "child_process";
1383
1461
  import { randomUUID } from "crypto";
1384
- import { existsSync as existsSync6, unlinkSync as unlinkSync3, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
1385
- import path8 from "path";
1462
+ import { existsSync as existsSync8, unlinkSync as unlinkSync3, readFileSync as readFileSync8, openSync, closeSync, statSync } from "fs";
1463
+ import path9 from "path";
1386
1464
  import { fileURLToPath } from "url";
1387
1465
  function handleData(chunk) {
1388
1466
  _buffer += chunk.toString();
@@ -1410,9 +1488,9 @@ function handleData(chunk) {
1410
1488
  }
1411
1489
  }
1412
1490
  function cleanupStaleFiles() {
1413
- if (existsSync6(PID_PATH)) {
1491
+ if (existsSync8(PID_PATH)) {
1414
1492
  try {
1415
- const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
1493
+ const pid = parseInt(readFileSync8(PID_PATH, "utf8").trim(), 10);
1416
1494
  if (pid > 0) {
1417
1495
  try {
1418
1496
  process.kill(pid, 0);
@@ -1433,11 +1511,11 @@ function cleanupStaleFiles() {
1433
1511
  }
1434
1512
  }
1435
1513
  function findPackageRoot() {
1436
- let dir = path8.dirname(fileURLToPath(import.meta.url));
1437
- const { root } = path8.parse(dir);
1514
+ let dir = path9.dirname(fileURLToPath(import.meta.url));
1515
+ const { root } = path9.parse(dir);
1438
1516
  while (dir !== root) {
1439
- if (existsSync6(path8.join(dir, "package.json"))) return dir;
1440
- dir = path8.dirname(dir);
1517
+ if (existsSync8(path9.join(dir, "package.json"))) return dir;
1518
+ dir = path9.dirname(dir);
1441
1519
  }
1442
1520
  return null;
1443
1521
  }
@@ -1463,16 +1541,17 @@ function spawnDaemon() {
1463
1541
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
1464
1542
  return;
1465
1543
  }
1466
- const daemonPath = path8.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1467
- if (!existsSync6(daemonPath)) {
1544
+ const daemonPath = path9.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1545
+ if (!existsSync8(daemonPath)) {
1468
1546
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1469
1547
  `);
1470
1548
  return;
1471
1549
  }
1472
1550
  const resolvedPath = daemonPath;
1551
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
1473
1552
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1474
1553
  `);
1475
- const logPath = path8.join(path8.dirname(SOCKET_PATH), "exed.log");
1554
+ const logPath = path9.join(path9.dirname(SOCKET_PATH), "exed.log");
1476
1555
  let stderrFd = "ignore";
1477
1556
  try {
1478
1557
  stderrFd = openSync(logPath, "a");
@@ -1490,7 +1569,8 @@ function spawnDaemon() {
1490
1569
  TMUX_PANE: void 0,
1491
1570
  // Prevents resolveExeSession() from scoping to one session
1492
1571
  EXE_DAEMON_SOCK: SOCKET_PATH,
1493
- EXE_DAEMON_PID: PID_PATH
1572
+ EXE_DAEMON_PID: PID_PATH,
1573
+ [DAEMON_TOKEN_ENV]: daemonToken
1494
1574
  }
1495
1575
  });
1496
1576
  child.unref();
@@ -1597,13 +1677,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
1597
1677
  return;
1598
1678
  }
1599
1679
  const id = randomUUID();
1680
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
1600
1681
  const timer = setTimeout(() => {
1601
1682
  _pending.delete(id);
1602
1683
  resolve({ error: "Request timeout" });
1603
1684
  }, timeoutMs);
1604
1685
  _pending.set(id, { resolve, timer });
1605
1686
  try {
1606
- _socket.write(JSON.stringify({ id, ...payload }) + "\n");
1687
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
1607
1688
  } catch {
1608
1689
  clearTimeout(timer);
1609
1690
  _pending.delete(id);
@@ -1614,17 +1695,19 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
1614
1695
  function isClientConnected() {
1615
1696
  return _connected;
1616
1697
  }
1617
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
1698
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _pending, MAX_BUFFER;
1618
1699
  var init_exe_daemon_client = __esm({
1619
1700
  "src/lib/exe-daemon-client.ts"() {
1620
1701
  "use strict";
1621
1702
  init_config();
1622
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path8.join(EXE_AI_DIR, "exed.sock");
1623
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path8.join(EXE_AI_DIR, "exed.pid");
1624
- SPAWN_LOCK_PATH = path8.join(EXE_AI_DIR, "exed-spawn.lock");
1703
+ init_daemon_auth();
1704
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path9.join(EXE_AI_DIR, "exed.sock");
1705
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path9.join(EXE_AI_DIR, "exed.pid");
1706
+ SPAWN_LOCK_PATH = path9.join(EXE_AI_DIR, "exed-spawn.lock");
1625
1707
  SPAWN_LOCK_STALE_MS = 3e4;
1626
1708
  CONNECT_TIMEOUT_MS = 15e3;
1627
1709
  REQUEST_TIMEOUT_MS = 3e4;
1710
+ DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
1628
1711
  _socket = null;
1629
1712
  _connected = false;
1630
1713
  _buffer = "";
@@ -2203,6 +2286,7 @@ async function ensureSchema() {
2203
2286
  project TEXT NOT NULL,
2204
2287
  summary TEXT NOT NULL,
2205
2288
  task_file TEXT,
2289
+ session_scope TEXT,
2206
2290
  read INTEGER NOT NULL DEFAULT 0,
2207
2291
  created_at TEXT NOT NULL
2208
2292
  );
@@ -2211,7 +2295,7 @@ async function ensureSchema() {
2211
2295
  ON notifications(read);
2212
2296
 
2213
2297
  CREATE INDEX IF NOT EXISTS idx_notifications_agent
2214
- ON notifications(agent_id);
2298
+ ON notifications(agent_id, session_scope);
2215
2299
 
2216
2300
  CREATE INDEX IF NOT EXISTS idx_notifications_task_file
2217
2301
  ON notifications(task_file);
@@ -2249,6 +2333,7 @@ async function ensureSchema() {
2249
2333
  target_agent TEXT NOT NULL,
2250
2334
  target_project TEXT,
2251
2335
  target_device TEXT NOT NULL DEFAULT 'local',
2336
+ session_scope TEXT,
2252
2337
  content TEXT NOT NULL,
2253
2338
  priority TEXT DEFAULT 'normal',
2254
2339
  status TEXT DEFAULT 'pending',
@@ -2262,10 +2347,31 @@ async function ensureSchema() {
2262
2347
  );
2263
2348
 
2264
2349
  CREATE INDEX IF NOT EXISTS idx_messages_target
2265
- ON messages(target_agent, status);
2350
+ ON messages(target_agent, session_scope, status);
2266
2351
 
2267
2352
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
2268
- ON messages(target_agent, from_agent, server_seq);
2353
+ ON messages(target_agent, session_scope, from_agent, server_seq);
2354
+ `);
2355
+ try {
2356
+ await client.execute({
2357
+ sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
2358
+ args: []
2359
+ });
2360
+ } catch {
2361
+ }
2362
+ try {
2363
+ await client.execute({
2364
+ sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
2365
+ args: []
2366
+ });
2367
+ } catch {
2368
+ }
2369
+ await client.executeMultiple(`
2370
+ CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
2371
+ ON notifications(agent_id, session_scope, read, created_at);
2372
+
2373
+ CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
2374
+ ON messages(target_agent, session_scope, status, created_at);
2269
2375
  `);
2270
2376
  try {
2271
2377
  await client.execute({
@@ -2849,6 +2955,13 @@ async function ensureSchema() {
2849
2955
  } catch {
2850
2956
  }
2851
2957
  }
2958
+ try {
2959
+ await client.execute({
2960
+ sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
2961
+ args: []
2962
+ });
2963
+ } catch {
2964
+ }
2852
2965
  }
2853
2966
  async function disposeDatabase() {
2854
2967
  if (_walCheckpointTimer) {
@@ -2887,18 +3000,21 @@ var init_database = __esm({
2887
3000
  });
2888
3001
 
2889
3002
  // src/lib/license.ts
2890
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync7, mkdirSync as mkdirSync5 } from "fs";
3003
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
2891
3004
  import { randomUUID as randomUUID2 } from "crypto";
2892
- import path9 from "path";
3005
+ import { createRequire as createRequire2 } from "module";
3006
+ import { pathToFileURL as pathToFileURL2 } from "url";
3007
+ import os7 from "os";
3008
+ import path10 from "path";
2893
3009
  import { jwtVerify, importSPKI } from "jose";
2894
3010
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
2895
3011
  var init_license = __esm({
2896
3012
  "src/lib/license.ts"() {
2897
3013
  "use strict";
2898
3014
  init_config();
2899
- LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
2900
- CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
2901
- DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
3015
+ LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
3016
+ CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
3017
+ DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
2902
3018
  PLAN_LIMITS = {
2903
3019
  free: { devices: 1, employees: 1, memories: 5e3 },
2904
3020
  pro: { devices: 3, employees: 5, memories: 1e5 },
@@ -2910,12 +3026,12 @@ var init_license = __esm({
2910
3026
  });
2911
3027
 
2912
3028
  // src/lib/plan-limits.ts
2913
- import { readFileSync as readFileSync9, existsSync as existsSync8 } from "fs";
2914
- import path10 from "path";
3029
+ import { readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
3030
+ import path11 from "path";
2915
3031
  function getLicenseSync() {
2916
3032
  try {
2917
- if (!existsSync8(CACHE_PATH2)) return freeLicense();
2918
- const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
3033
+ if (!existsSync10(CACHE_PATH2)) return freeLicense();
3034
+ const raw = JSON.parse(readFileSync10(CACHE_PATH2, "utf8"));
2919
3035
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
2920
3036
  const parts = raw.token.split(".");
2921
3037
  if (parts.length !== 3) return freeLicense();
@@ -2953,8 +3069,8 @@ function assertEmployeeLimitSync(rosterPath) {
2953
3069
  const filePath = rosterPath ?? EMPLOYEES_PATH;
2954
3070
  let count = 0;
2955
3071
  try {
2956
- if (existsSync8(filePath)) {
2957
- const raw = readFileSync9(filePath, "utf8");
3072
+ if (existsSync10(filePath)) {
3073
+ const raw = readFileSync10(filePath, "utf8");
2958
3074
  const employees = JSON.parse(raw);
2959
3075
  count = Array.isArray(employees) ? employees.length : 0;
2960
3076
  }
@@ -2983,29 +3099,30 @@ var init_plan_limits = __esm({
2983
3099
  this.name = "PlanLimitError";
2984
3100
  }
2985
3101
  };
2986
- CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
3102
+ CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
2987
3103
  }
2988
3104
  });
2989
3105
 
2990
3106
  // src/lib/notifications.ts
2991
- import crypto from "crypto";
2992
- import path11 from "path";
2993
- import os7 from "os";
3107
+ import crypto2 from "crypto";
3108
+ import path12 from "path";
3109
+ import os8 from "os";
2994
3110
  import {
2995
- readFileSync as readFileSync10,
3111
+ readFileSync as readFileSync11,
2996
3112
  readdirSync as readdirSync2,
2997
3113
  unlinkSync as unlinkSync4,
2998
- existsSync as existsSync9,
3114
+ existsSync as existsSync11,
2999
3115
  rmdirSync
3000
3116
  } from "fs";
3001
3117
  async function writeNotification(notification) {
3002
3118
  try {
3003
3119
  const client = getClient();
3004
- const id = crypto.randomUUID();
3120
+ const id = crypto2.randomUUID();
3005
3121
  const now = (/* @__PURE__ */ new Date()).toISOString();
3122
+ const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
3006
3123
  await client.execute({
3007
- sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
3008
- VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
3124
+ sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
3125
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`,
3009
3126
  args: [
3010
3127
  id,
3011
3128
  notification.agentId,
@@ -3014,6 +3131,7 @@ async function writeNotification(notification) {
3014
3131
  notification.project,
3015
3132
  notification.summary,
3016
3133
  notification.taskFile ?? null,
3134
+ sessionScope,
3017
3135
  now
3018
3136
  ]
3019
3137
  });
@@ -3022,12 +3140,14 @@ async function writeNotification(notification) {
3022
3140
  `);
3023
3141
  }
3024
3142
  }
3025
- async function markAsReadByTaskFile(taskFile) {
3143
+ async function markAsReadByTaskFile(taskFile, sessionScope) {
3026
3144
  try {
3027
3145
  const client = getClient();
3146
+ const scope = strictSessionScopeFilter(sessionScope);
3028
3147
  await client.execute({
3029
- sql: "UPDATE notifications SET read = 1 WHERE task_file = ? AND read = 0",
3030
- args: [taskFile]
3148
+ sql: `UPDATE notifications SET read = 1
3149
+ WHERE task_file = ? AND read = 0${scope.sql}`,
3150
+ args: [taskFile, ...scope.args]
3031
3151
  });
3032
3152
  } catch {
3033
3153
  }
@@ -3036,11 +3156,12 @@ var init_notifications = __esm({
3036
3156
  "src/lib/notifications.ts"() {
3037
3157
  "use strict";
3038
3158
  init_database();
3159
+ init_task_scope();
3039
3160
  }
3040
3161
  });
3041
3162
 
3042
3163
  // src/lib/session-kill-telemetry.ts
3043
- import crypto2 from "crypto";
3164
+ import crypto3 from "crypto";
3044
3165
  async function recordSessionKill(input2) {
3045
3166
  try {
3046
3167
  const client = getClient();
@@ -3050,7 +3171,7 @@ async function recordSessionKill(input2) {
3050
3171
  ticks_idle, estimated_tokens_saved)
3051
3172
  VALUES (?, ?, ?, ?, ?, ?, ?)`,
3052
3173
  args: [
3053
- crypto2.randomUUID(),
3174
+ crypto3.randomUUID(),
3054
3175
  input2.sessionName,
3055
3176
  input2.agentId,
3056
3177
  (/* @__PURE__ */ new Date()).toISOString(),
@@ -3128,13 +3249,117 @@ var init_state_bus = __esm({
3128
3249
  }
3129
3250
  });
3130
3251
 
3131
- // src/lib/tasks-crud.ts
3132
- import crypto3 from "crypto";
3133
- import path12 from "path";
3134
- import os8 from "os";
3252
+ // src/lib/project-name.ts
3135
3253
  import { execSync as execSync5 } from "child_process";
3254
+ import path13 from "path";
3255
+ function getProjectName(cwd) {
3256
+ const dir = cwd ?? process.cwd();
3257
+ if (_cached2 && _cachedCwd === dir) return _cached2;
3258
+ try {
3259
+ let repoRoot;
3260
+ try {
3261
+ const gitCommonDir = execSync5("git rev-parse --path-format=absolute --git-common-dir", {
3262
+ cwd: dir,
3263
+ encoding: "utf8",
3264
+ timeout: 2e3,
3265
+ stdio: ["pipe", "pipe", "pipe"]
3266
+ }).trim();
3267
+ repoRoot = path13.dirname(gitCommonDir);
3268
+ } catch {
3269
+ repoRoot = execSync5("git rev-parse --show-toplevel", {
3270
+ cwd: dir,
3271
+ encoding: "utf8",
3272
+ timeout: 2e3,
3273
+ stdio: ["pipe", "pipe", "pipe"]
3274
+ }).trim();
3275
+ }
3276
+ _cached2 = path13.basename(repoRoot);
3277
+ _cachedCwd = dir;
3278
+ return _cached2;
3279
+ } catch {
3280
+ _cached2 = path13.basename(dir);
3281
+ _cachedCwd = dir;
3282
+ return _cached2;
3283
+ }
3284
+ }
3285
+ var _cached2, _cachedCwd;
3286
+ var init_project_name = __esm({
3287
+ "src/lib/project-name.ts"() {
3288
+ "use strict";
3289
+ _cached2 = null;
3290
+ _cachedCwd = null;
3291
+ }
3292
+ });
3293
+
3294
+ // src/lib/session-scope.ts
3295
+ var session_scope_exports = {};
3296
+ __export(session_scope_exports, {
3297
+ assertSessionScope: () => assertSessionScope,
3298
+ findSessionForProject: () => findSessionForProject,
3299
+ getSessionProject: () => getSessionProject
3300
+ });
3301
+ function getSessionProject(sessionName) {
3302
+ const sessions = listSessions();
3303
+ const entry = sessions.find((s) => s.windowName === sessionName);
3304
+ if (!entry) return null;
3305
+ const parts = entry.projectDir.split("/").filter(Boolean);
3306
+ return parts[parts.length - 1] ?? null;
3307
+ }
3308
+ function findSessionForProject(projectName) {
3309
+ const sessions = listSessions();
3310
+ for (const s of sessions) {
3311
+ const proj = s.projectDir.split("/").filter(Boolean).pop();
3312
+ if (proj === projectName && isCoordinatorName(s.agentId)) return s;
3313
+ }
3314
+ return null;
3315
+ }
3316
+ function assertSessionScope(actionType, targetProject) {
3317
+ try {
3318
+ const currentProject = getProjectName();
3319
+ const exeSession = resolveExeSession();
3320
+ if (!exeSession) {
3321
+ return { allowed: true, reason: "no_session" };
3322
+ }
3323
+ if (currentProject === targetProject) {
3324
+ return {
3325
+ allowed: true,
3326
+ reason: "same_session",
3327
+ currentProject,
3328
+ targetProject
3329
+ };
3330
+ }
3331
+ process.stderr.write(
3332
+ `[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
3333
+ `
3334
+ );
3335
+ return {
3336
+ allowed: false,
3337
+ reason: "cross_session_denied",
3338
+ currentProject,
3339
+ targetProject,
3340
+ targetSession: findSessionForProject(targetProject)?.windowName
3341
+ };
3342
+ } catch {
3343
+ return { allowed: true, reason: "no_session" };
3344
+ }
3345
+ }
3346
+ var init_session_scope = __esm({
3347
+ "src/lib/session-scope.ts"() {
3348
+ "use strict";
3349
+ init_session_registry();
3350
+ init_project_name();
3351
+ init_tmux_routing();
3352
+ init_employees();
3353
+ }
3354
+ });
3355
+
3356
+ // src/lib/tasks-crud.ts
3357
+ import crypto4 from "crypto";
3358
+ import path14 from "path";
3359
+ import os9 from "os";
3360
+ import { execSync as execSync6 } from "child_process";
3136
3361
  import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
3137
- import { existsSync as existsSync10, readFileSync as readFileSync11 } from "fs";
3362
+ import { existsSync as existsSync12, readFileSync as readFileSync12 } from "fs";
3138
3363
  async function writeCheckpoint(input2) {
3139
3364
  const client = getClient();
3140
3365
  const row = await resolveTask(client, input2.taskId);
@@ -3250,13 +3475,28 @@ async function resolveTask(client, identifier, scopeSession) {
3250
3475
  }
3251
3476
  async function createTaskCore(input2) {
3252
3477
  const client = getClient();
3253
- const id = crypto3.randomUUID();
3478
+ const id = crypto4.randomUUID();
3254
3479
  const now = (/* @__PURE__ */ new Date()).toISOString();
3255
3480
  const slug = slugify(input2.title);
3256
3481
  let earlySessionScope = null;
3482
+ let scopeMismatchWarning;
3257
3483
  try {
3258
3484
  const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
3259
- earlySessionScope = resolveExeSession2();
3485
+ const resolved = resolveExeSession2();
3486
+ if (resolved && input2.projectName) {
3487
+ const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
3488
+ const sessionProject = getSessionProject2(resolved);
3489
+ if (sessionProject && sessionProject !== input2.projectName) {
3490
+ scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input2.projectName}". Routed to default scope.`;
3491
+ process.stderr.write(`[create_task] ${scopeMismatchWarning}
3492
+ `);
3493
+ earlySessionScope = null;
3494
+ } else {
3495
+ earlySessionScope = resolved;
3496
+ }
3497
+ } else {
3498
+ earlySessionScope = resolved;
3499
+ }
3260
3500
  } catch {
3261
3501
  }
3262
3502
  const scope = earlySessionScope ?? "default";
@@ -3307,10 +3547,14 @@ async function createTaskCore(input2) {
3307
3547
  ${laneWarning}` : laneWarning;
3308
3548
  }
3309
3549
  }
3550
+ if (scopeMismatchWarning) {
3551
+ warning = warning ? `${warning}
3552
+ ${scopeMismatchWarning}` : scopeMismatchWarning;
3553
+ }
3310
3554
  if (input2.baseDir) {
3311
3555
  try {
3312
- await mkdir3(path12.join(input2.baseDir, "exe", "output"), { recursive: true });
3313
- await mkdir3(path12.join(input2.baseDir, "exe", "research"), { recursive: true });
3556
+ await mkdir3(path14.join(input2.baseDir, "exe", "output"), { recursive: true });
3557
+ await mkdir3(path14.join(input2.baseDir, "exe", "research"), { recursive: true });
3314
3558
  await ensureArchitectureDoc(input2.baseDir, input2.projectName);
3315
3559
  await ensureGitignoreExe(input2.baseDir);
3316
3560
  } catch {
@@ -3346,13 +3590,19 @@ ${laneWarning}` : laneWarning;
3346
3590
  });
3347
3591
  if (input2.baseDir) {
3348
3592
  try {
3349
- const EXE_OS_DIR = path12.join(os8.homedir(), ".exe-os");
3350
- const mdPath = path12.join(EXE_OS_DIR, taskFile);
3351
- const mdDir = path12.dirname(mdPath);
3352
- if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
3593
+ const EXE_OS_DIR = path14.join(os9.homedir(), ".exe-os");
3594
+ const mdPath = path14.join(EXE_OS_DIR, taskFile);
3595
+ const mdDir = path14.dirname(mdPath);
3596
+ if (!existsSync12(mdDir)) await mkdir3(mdDir, { recursive: true });
3353
3597
  const reviewer = input2.reviewer ?? input2.assignedBy;
3354
3598
  const mdContent = `# ${input2.title}
3355
3599
 
3600
+ ## MANDATORY: When done
3601
+
3602
+ You MUST call update_task with status "done" and a result summary when finished.
3603
+ If you skip this, your reviewer will not know you're done and your work won't be reviewed.
3604
+ Do NOT let a failed commit or any error prevent you from calling update_task(done).
3605
+
3356
3606
  **ID:** ${id}
3357
3607
  **Status:** ${initialStatus}
3358
3608
  **Priority:** ${input2.priority}
@@ -3366,12 +3616,6 @@ ${laneWarning}` : laneWarning;
3366
3616
  ## Context
3367
3617
 
3368
3618
  ${input2.context}
3369
-
3370
- ## MANDATORY: When done
3371
-
3372
- You MUST call update_task with status "done" and a result summary when finished.
3373
- If you skip this, your reviewer will not know you're done and your work won't be reviewed.
3374
- Do NOT let a failed commit or any error prevent you from calling update_task(done).
3375
3619
  `;
3376
3620
  await writeFile3(mdPath, mdContent, "utf-8");
3377
3621
  } catch (err) {
@@ -3453,14 +3697,14 @@ function isTmuxSessionAlive(identifier) {
3453
3697
  if (!identifier || identifier === "unknown") return true;
3454
3698
  try {
3455
3699
  if (identifier.startsWith("%")) {
3456
- const output = execSync5("tmux list-panes -a -F '#{pane_id}'", {
3700
+ const output = execSync6("tmux list-panes -a -F '#{pane_id}'", {
3457
3701
  timeout: 2e3,
3458
3702
  encoding: "utf8",
3459
3703
  stdio: ["pipe", "pipe", "pipe"]
3460
3704
  });
3461
3705
  return output.split("\n").some((l) => l.trim() === identifier);
3462
3706
  } else {
3463
- execSync5(`tmux has-session -t ${JSON.stringify(identifier)}`, {
3707
+ execSync6(`tmux has-session -t ${JSON.stringify(identifier)}`, {
3464
3708
  timeout: 2e3,
3465
3709
  stdio: ["pipe", "pipe", "pipe"]
3466
3710
  });
@@ -3469,7 +3713,7 @@ function isTmuxSessionAlive(identifier) {
3469
3713
  } catch {
3470
3714
  if (identifier.startsWith("%")) return true;
3471
3715
  try {
3472
- execSync5("tmux list-sessions", {
3716
+ execSync6("tmux list-sessions", {
3473
3717
  timeout: 2e3,
3474
3718
  stdio: ["pipe", "pipe", "pipe"]
3475
3719
  });
@@ -3484,12 +3728,12 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
3484
3728
  if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
3485
3729
  try {
3486
3730
  const since = new Date(taskCreatedAt).toISOString();
3487
- const branch = execSync5(
3731
+ const branch = execSync6(
3488
3732
  "git rev-parse --abbrev-ref HEAD 2>/dev/null",
3489
3733
  { encoding: "utf8", timeout: 3e3 }
3490
3734
  ).trim();
3491
3735
  const branchArg = branch && branch !== "HEAD" ? branch : "";
3492
- const commitCount = execSync5(
3736
+ const commitCount = execSync6(
3493
3737
  `git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
3494
3738
  { encoding: "utf8", timeout: 5e3 }
3495
3739
  ).trim();
@@ -3620,7 +3864,7 @@ ${input2.result}` : `\u26A0\uFE0F ${warning}`;
3620
3864
  await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
3621
3865
  } catch {
3622
3866
  }
3623
- if (input2.status === "done" || input2.status === "cancelled") {
3867
+ if (input2.status === "done" || input2.status === "cancelled" || input2.status === "closed") {
3624
3868
  try {
3625
3869
  const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
3626
3870
  clearQueueForAgent2(String(row.assigned_to));
@@ -3649,9 +3893,9 @@ async function deleteTaskCore(taskId, _baseDir) {
3649
3893
  return { taskFile, assignedTo, assignedBy, taskSlug };
3650
3894
  }
3651
3895
  async function ensureArchitectureDoc(baseDir, projectName) {
3652
- const archPath = path12.join(baseDir, "exe", "ARCHITECTURE.md");
3896
+ const archPath = path14.join(baseDir, "exe", "ARCHITECTURE.md");
3653
3897
  try {
3654
- if (existsSync10(archPath)) return;
3898
+ if (existsSync12(archPath)) return;
3655
3899
  const template = [
3656
3900
  `# ${projectName} \u2014 System Architecture`,
3657
3901
  "",
@@ -3684,10 +3928,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
3684
3928
  }
3685
3929
  }
3686
3930
  async function ensureGitignoreExe(baseDir) {
3687
- const gitignorePath = path12.join(baseDir, ".gitignore");
3931
+ const gitignorePath = path14.join(baseDir, ".gitignore");
3688
3932
  try {
3689
- if (existsSync10(gitignorePath)) {
3690
- const content = readFileSync11(gitignorePath, "utf-8");
3933
+ if (existsSync12(gitignorePath)) {
3934
+ const content = readFileSync12(gitignorePath, "utf-8");
3691
3935
  if (/^\/?exe\/?$/m.test(content)) return;
3692
3936
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
3693
3937
  } else {
@@ -3718,58 +3962,42 @@ var init_tasks_crud = __esm({
3718
3962
  });
3719
3963
 
3720
3964
  // src/lib/tasks-review.ts
3721
- import path13 from "path";
3722
- import { existsSync as existsSync11, readdirSync as readdirSync3, unlinkSync as unlinkSync5 } from "fs";
3965
+ import path15 from "path";
3966
+ import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync5 } from "fs";
3723
3967
  async function countPendingReviews(sessionScope) {
3724
3968
  const client = getClient();
3725
- if (sessionScope) {
3726
- const result2 = await client.execute({
3727
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review' AND session_scope = ?",
3728
- args: [sessionScope]
3729
- });
3730
- return Number(result2.rows[0]?.cnt) || 0;
3731
- }
3969
+ const scope = strictSessionScopeFilter(
3970
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
3971
+ );
3732
3972
  const result = await client.execute({
3733
- sql: "SELECT COUNT(*) as cnt FROM tasks WHERE status = 'needs_review'",
3734
- args: []
3973
+ sql: `SELECT COUNT(*) as cnt FROM tasks
3974
+ WHERE status = 'needs_review'${scope.sql}`,
3975
+ args: [...scope.args]
3735
3976
  });
3736
3977
  return Number(result.rows[0]?.cnt) || 0;
3737
3978
  }
3738
3979
  async function countNewPendingReviewsSince(sinceIso, sessionScope) {
3739
3980
  const client = getClient();
3740
- if (sessionScope) {
3741
- const result2 = await client.execute({
3742
- sql: `SELECT COUNT(*) as cnt FROM tasks
3743
- WHERE status = 'needs_review' AND updated_at > ?
3744
- AND session_scope = ?`,
3745
- args: [sinceIso, sessionScope]
3746
- });
3747
- return Number(result2.rows[0]?.cnt) || 0;
3748
- }
3981
+ const scope = strictSessionScopeFilter(
3982
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
3983
+ );
3749
3984
  const result = await client.execute({
3750
3985
  sql: `SELECT COUNT(*) as cnt FROM tasks
3751
- WHERE status = 'needs_review' AND updated_at > ?`,
3752
- args: [sinceIso]
3986
+ WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
3987
+ args: [sinceIso, ...scope.args]
3753
3988
  });
3754
3989
  return Number(result.rows[0]?.cnt) || 0;
3755
3990
  }
3756
3991
  async function listPendingReviews(limit, sessionScope) {
3757
3992
  const client = getClient();
3758
- if (sessionScope) {
3759
- const result2 = await client.execute({
3760
- sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
3761
- WHERE status = 'needs_review'
3762
- AND session_scope = ?
3763
- ORDER BY updated_at ASC LIMIT ?`,
3764
- args: [sessionScope, limit]
3765
- });
3766
- return result2.rows;
3767
- }
3993
+ const scope = strictSessionScopeFilter(
3994
+ sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
3995
+ );
3768
3996
  const result = await client.execute({
3769
3997
  sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
3770
- WHERE status = 'needs_review'
3998
+ WHERE status = 'needs_review'${scope.sql}
3771
3999
  ORDER BY updated_at ASC LIMIT ?`,
3772
- args: [limit]
4000
+ args: [...scope.args, limit]
3773
4001
  });
3774
4002
  return result.rows;
3775
4003
  }
@@ -3781,7 +4009,7 @@ async function cleanupOrphanedReviews() {
3781
4009
  WHERE status IN ('open', 'needs_review', 'in_progress')
3782
4010
  AND assigned_by = 'system'
3783
4011
  AND title LIKE 'Review:%'
3784
- AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
4012
+ AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
3785
4013
  args: [now]
3786
4014
  });
3787
4015
  const r1b = await client.execute({
@@ -3900,11 +4128,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
3900
4128
  );
3901
4129
  }
3902
4130
  try {
3903
- const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
3904
- if (existsSync11(cacheDir)) {
4131
+ const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
4132
+ if (existsSync13(cacheDir)) {
3905
4133
  for (const f of readdirSync3(cacheDir)) {
3906
4134
  if (f.startsWith("review-notified-")) {
3907
- unlinkSync5(path13.join(cacheDir, f));
4135
+ unlinkSync5(path15.join(cacheDir, f));
3908
4136
  }
3909
4137
  }
3910
4138
  }
@@ -3921,11 +4149,12 @@ var init_tasks_review = __esm({
3921
4149
  init_tmux_routing();
3922
4150
  init_session_key();
3923
4151
  init_state_bus();
4152
+ init_task_scope();
3924
4153
  }
3925
4154
  });
3926
4155
 
3927
4156
  // src/lib/tasks-chain.ts
3928
- import path14 from "path";
4157
+ import path16 from "path";
3929
4158
  import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
3930
4159
  async function cascadeUnblock(taskId, baseDir, now) {
3931
4160
  const client = getClient();
@@ -3942,7 +4171,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
3942
4171
  });
3943
4172
  for (const ur of unblockedRows.rows) {
3944
4173
  try {
3945
- const ubFile = path14.join(baseDir, String(ur.task_file));
4174
+ const ubFile = path16.join(baseDir, String(ur.task_file));
3946
4175
  let ubContent = await readFile3(ubFile, "utf-8");
3947
4176
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
3948
4177
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -3977,7 +4206,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
3977
4206
  const scScope = sessionScopeFilter();
3978
4207
  const remaining = await client.execute({
3979
4208
  sql: `SELECT COUNT(*) as cnt FROM tasks
3980
- WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
4209
+ WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
3981
4210
  args: [parentTaskId, ...scScope.args]
3982
4211
  });
3983
4212
  const cnt = Number(remaining.rows[0]?.cnt ?? 1);
@@ -4009,110 +4238,6 @@ var init_tasks_chain = __esm({
4009
4238
  }
4010
4239
  });
4011
4240
 
4012
- // src/lib/project-name.ts
4013
- import { execSync as execSync6 } from "child_process";
4014
- import path15 from "path";
4015
- function getProjectName(cwd) {
4016
- const dir = cwd ?? process.cwd();
4017
- if (_cached2 && _cachedCwd === dir) return _cached2;
4018
- try {
4019
- let repoRoot;
4020
- try {
4021
- const gitCommonDir = execSync6("git rev-parse --path-format=absolute --git-common-dir", {
4022
- cwd: dir,
4023
- encoding: "utf8",
4024
- timeout: 2e3,
4025
- stdio: ["pipe", "pipe", "pipe"]
4026
- }).trim();
4027
- repoRoot = path15.dirname(gitCommonDir);
4028
- } catch {
4029
- repoRoot = execSync6("git rev-parse --show-toplevel", {
4030
- cwd: dir,
4031
- encoding: "utf8",
4032
- timeout: 2e3,
4033
- stdio: ["pipe", "pipe", "pipe"]
4034
- }).trim();
4035
- }
4036
- _cached2 = path15.basename(repoRoot);
4037
- _cachedCwd = dir;
4038
- return _cached2;
4039
- } catch {
4040
- _cached2 = path15.basename(dir);
4041
- _cachedCwd = dir;
4042
- return _cached2;
4043
- }
4044
- }
4045
- var _cached2, _cachedCwd;
4046
- var init_project_name = __esm({
4047
- "src/lib/project-name.ts"() {
4048
- "use strict";
4049
- _cached2 = null;
4050
- _cachedCwd = null;
4051
- }
4052
- });
4053
-
4054
- // src/lib/session-scope.ts
4055
- var session_scope_exports = {};
4056
- __export(session_scope_exports, {
4057
- assertSessionScope: () => assertSessionScope,
4058
- findSessionForProject: () => findSessionForProject,
4059
- getSessionProject: () => getSessionProject
4060
- });
4061
- function getSessionProject(sessionName) {
4062
- const sessions = listSessions();
4063
- const entry = sessions.find((s) => s.windowName === sessionName);
4064
- if (!entry) return null;
4065
- const parts = entry.projectDir.split("/").filter(Boolean);
4066
- return parts[parts.length - 1] ?? null;
4067
- }
4068
- function findSessionForProject(projectName) {
4069
- const sessions = listSessions();
4070
- for (const s of sessions) {
4071
- const proj = s.projectDir.split("/").filter(Boolean).pop();
4072
- if (proj === projectName && isCoordinatorName(s.agentId)) return s;
4073
- }
4074
- return null;
4075
- }
4076
- function assertSessionScope(actionType, targetProject) {
4077
- try {
4078
- const currentProject = getProjectName();
4079
- const exeSession = resolveExeSession();
4080
- if (!exeSession) {
4081
- return { allowed: true, reason: "no_session" };
4082
- }
4083
- if (currentProject === targetProject) {
4084
- return {
4085
- allowed: true,
4086
- reason: "same_session",
4087
- currentProject,
4088
- targetProject
4089
- };
4090
- }
4091
- process.stderr.write(
4092
- `[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
4093
- `
4094
- );
4095
- return {
4096
- allowed: false,
4097
- reason: "cross_session_denied",
4098
- currentProject,
4099
- targetProject,
4100
- targetSession: findSessionForProject(targetProject)?.windowName
4101
- };
4102
- } catch {
4103
- return { allowed: true, reason: "no_session" };
4104
- }
4105
- }
4106
- var init_session_scope = __esm({
4107
- "src/lib/session-scope.ts"() {
4108
- "use strict";
4109
- init_session_registry();
4110
- init_project_name();
4111
- init_tmux_routing();
4112
- init_employees();
4113
- }
4114
- });
4115
-
4116
4241
  // src/lib/tasks-notify.ts
4117
4242
  async function dispatchTaskToEmployee(input2) {
4118
4243
  if (isCoordinatorName(input2.assignedTo)) return { dispatched: "skipped" };
@@ -4180,10 +4305,10 @@ var init_tasks_notify = __esm({
4180
4305
  });
4181
4306
 
4182
4307
  // src/lib/behaviors.ts
4183
- import crypto4 from "crypto";
4308
+ import crypto5 from "crypto";
4184
4309
  async function storeBehavior(opts) {
4185
4310
  const client = getClient();
4186
- const id = crypto4.randomUUID();
4311
+ const id = crypto5.randomUUID();
4187
4312
  const now = (/* @__PURE__ */ new Date()).toISOString();
4188
4313
  await client.execute({
4189
4314
  sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
@@ -4212,7 +4337,7 @@ __export(skill_learning_exports, {
4212
4337
  storeTrajectory: () => storeTrajectory,
4213
4338
  sweepTrajectories: () => sweepTrajectories
4214
4339
  });
4215
- import crypto5 from "crypto";
4340
+ import crypto6 from "crypto";
4216
4341
  async function extractTrajectory(taskId, agentId) {
4217
4342
  const client = getClient();
4218
4343
  const result = await client.execute({
@@ -4241,11 +4366,11 @@ async function extractTrajectory(taskId, agentId) {
4241
4366
  return signature;
4242
4367
  }
4243
4368
  function hashSignature(signature) {
4244
- return crypto5.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
4369
+ return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
4245
4370
  }
4246
4371
  async function storeTrajectory(opts) {
4247
4372
  const client = getClient();
4248
- const id = crypto5.randomUUID();
4373
+ const id = crypto6.randomUUID();
4249
4374
  const now = (/* @__PURE__ */ new Date()).toISOString();
4250
4375
  const signatureHash = hashSignature(opts.signature);
4251
4376
  await client.execute({
@@ -4510,8 +4635,8 @@ __export(tasks_exports, {
4510
4635
  updateTaskStatus: () => updateTaskStatus,
4511
4636
  writeCheckpoint: () => writeCheckpoint
4512
4637
  });
4513
- import path16 from "path";
4514
- import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, unlinkSync as unlinkSync6 } from "fs";
4638
+ import path17 from "path";
4639
+ import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, unlinkSync as unlinkSync6 } from "fs";
4515
4640
  async function createTask(input2) {
4516
4641
  const result = await createTaskCore(input2);
4517
4642
  if (!input2.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -4530,12 +4655,12 @@ async function updateTask(input2) {
4530
4655
  const { row, taskFile, now, taskId } = await updateTaskStatus(input2);
4531
4656
  try {
4532
4657
  const agent = String(row.assigned_to);
4533
- const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
4534
- const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
4658
+ const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
4659
+ const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
4535
4660
  if (input2.status === "in_progress") {
4536
4661
  mkdirSync6(cacheDir, { recursive: true });
4537
- writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
4538
- } else if (input2.status === "done" || input2.status === "blocked" || input2.status === "cancelled") {
4662
+ writeFileSync8(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
4663
+ } else if (input2.status === "done" || input2.status === "blocked" || input2.status === "cancelled" || input2.status === "closed") {
4539
4664
  try {
4540
4665
  unlinkSync6(cachePath);
4541
4666
  } catch {
@@ -4543,10 +4668,10 @@ async function updateTask(input2) {
4543
4668
  }
4544
4669
  } catch {
4545
4670
  }
4546
- if (input2.status === "done") {
4671
+ if (input2.status === "done" || input2.status === "closed") {
4547
4672
  await cleanupReviewFile(row, taskFile, input2.baseDir);
4548
4673
  }
4549
- if (input2.status === "done" || input2.status === "cancelled") {
4674
+ if (input2.status === "done" || input2.status === "cancelled" || input2.status === "closed") {
4550
4675
  try {
4551
4676
  const client = getClient();
4552
4677
  const taskTitle = String(row.title);
@@ -4562,7 +4687,7 @@ async function updateTask(input2) {
4562
4687
  if (!isCoordinatorName(assignedAgent)) {
4563
4688
  try {
4564
4689
  const draftClient = getClient();
4565
- if (input2.status === "done") {
4690
+ if (input2.status === "done" || input2.status === "closed") {
4566
4691
  await draftClient.execute({
4567
4692
  sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
4568
4693
  args: [assignedAgent]
@@ -4579,7 +4704,7 @@ async function updateTask(input2) {
4579
4704
  try {
4580
4705
  const client = getClient();
4581
4706
  const cascaded = await client.execute({
4582
- sql: `UPDATE tasks SET status = 'done', updated_at = ?
4707
+ sql: `UPDATE tasks SET status = 'closed', updated_at = ?
4583
4708
  WHERE parent_task_id = ? AND status = 'needs_review'`,
4584
4709
  args: [now, taskId]
4585
4710
  });
@@ -4592,14 +4717,14 @@ async function updateTask(input2) {
4592
4717
  } catch {
4593
4718
  }
4594
4719
  }
4595
- const isTerminal = input2.status === "done" || input2.status === "needs_review";
4720
+ const isTerminal = input2.status === "done" || input2.status === "needs_review" || input2.status === "closed";
4596
4721
  if (isTerminal) {
4597
4722
  const isCoordinator = isCoordinatorName(String(row.assigned_to));
4598
4723
  if (!isCoordinator) {
4599
4724
  notifyTaskDone();
4600
4725
  }
4601
4726
  await markTaskNotificationsRead(taskFile);
4602
- if (input2.status === "done") {
4727
+ if (input2.status === "done" || input2.status === "closed") {
4603
4728
  try {
4604
4729
  await cascadeUnblock(taskId, input2.baseDir, now);
4605
4730
  } catch {
@@ -4619,7 +4744,7 @@ async function updateTask(input2) {
4619
4744
  }
4620
4745
  }
4621
4746
  }
4622
- if (input2.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
4747
+ if ((input2.status === "done" || input2.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
4623
4748
  Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
4624
4749
  ({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
4625
4750
  taskId,
@@ -4991,6 +5116,7 @@ __export(tmux_routing_exports, {
4991
5116
  isEmployeeAlive: () => isEmployeeAlive,
4992
5117
  isExeSession: () => isExeSession,
4993
5118
  isSessionBusy: () => isSessionBusy,
5119
+ notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
4994
5120
  notifyParentExe: () => notifyParentExe,
4995
5121
  parseParentExe: () => parseParentExe,
4996
5122
  registerParentExe: () => registerParentExe,
@@ -5001,13 +5127,13 @@ __export(tmux_routing_exports, {
5001
5127
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
5002
5128
  });
5003
5129
  import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
5004
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync4 } from "fs";
5005
- import path17 from "path";
5006
- import os9 from "os";
5130
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, mkdirSync as mkdirSync7, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync4 } from "fs";
5131
+ import path18 from "path";
5132
+ import os10 from "os";
5007
5133
  import { fileURLToPath as fileURLToPath2 } from "url";
5008
5134
  import { unlinkSync as unlinkSync7 } from "fs";
5009
5135
  function spawnLockPath(sessionName) {
5010
- return path17.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
5136
+ return path18.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
5011
5137
  }
5012
5138
  function isProcessAlive(pid) {
5013
5139
  try {
@@ -5018,13 +5144,13 @@ function isProcessAlive(pid) {
5018
5144
  }
5019
5145
  }
5020
5146
  function acquireSpawnLock2(sessionName) {
5021
- if (!existsSync12(SPAWN_LOCK_DIR)) {
5147
+ if (!existsSync14(SPAWN_LOCK_DIR)) {
5022
5148
  mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
5023
5149
  }
5024
5150
  const lockFile = spawnLockPath(sessionName);
5025
- if (existsSync12(lockFile)) {
5151
+ if (existsSync14(lockFile)) {
5026
5152
  try {
5027
- const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
5153
+ const lock = JSON.parse(readFileSync13(lockFile, "utf8"));
5028
5154
  const age = Date.now() - lock.timestamp;
5029
5155
  if (isProcessAlive(lock.pid) && age < 6e4) {
5030
5156
  return false;
@@ -5032,7 +5158,7 @@ function acquireSpawnLock2(sessionName) {
5032
5158
  } catch {
5033
5159
  }
5034
5160
  }
5035
- writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
5161
+ writeFileSync9(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
5036
5162
  return true;
5037
5163
  }
5038
5164
  function releaseSpawnLock2(sessionName) {
@@ -5044,13 +5170,13 @@ function releaseSpawnLock2(sessionName) {
5044
5170
  function resolveBehaviorsExporterScript() {
5045
5171
  try {
5046
5172
  const thisFile = fileURLToPath2(import.meta.url);
5047
- const scriptPath = path17.join(
5048
- path17.dirname(thisFile),
5173
+ const scriptPath = path18.join(
5174
+ path18.dirname(thisFile),
5049
5175
  "..",
5050
5176
  "bin",
5051
5177
  "exe-export-behaviors.js"
5052
5178
  );
5053
- return existsSync12(scriptPath) ? scriptPath : null;
5179
+ return existsSync14(scriptPath) ? scriptPath : null;
5054
5180
  } catch {
5055
5181
  return null;
5056
5182
  }
@@ -5116,12 +5242,12 @@ function extractRootExe(name) {
5116
5242
  return parts.length > 0 ? parts[parts.length - 1] : null;
5117
5243
  }
5118
5244
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
5119
- if (!existsSync12(SESSION_CACHE)) {
5245
+ if (!existsSync14(SESSION_CACHE)) {
5120
5246
  mkdirSync7(SESSION_CACHE, { recursive: true });
5121
5247
  }
5122
5248
  const rootExe = extractRootExe(parentExe) ?? parentExe;
5123
- const filePath = path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
5124
- writeFileSync8(filePath, JSON.stringify({
5249
+ const filePath = path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
5250
+ writeFileSync9(filePath, JSON.stringify({
5125
5251
  parentExe: rootExe,
5126
5252
  dispatchedBy: dispatchedBy || rootExe,
5127
5253
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -5129,7 +5255,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
5129
5255
  }
5130
5256
  function getParentExe(sessionKey) {
5131
5257
  try {
5132
- const data = JSON.parse(readFileSync12(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
5258
+ const data = JSON.parse(readFileSync13(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
5133
5259
  return data.parentExe || null;
5134
5260
  } catch {
5135
5261
  return null;
@@ -5137,8 +5263,8 @@ function getParentExe(sessionKey) {
5137
5263
  }
5138
5264
  function getDispatchedBy(sessionKey) {
5139
5265
  try {
5140
- const data = JSON.parse(readFileSync12(
5141
- path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
5266
+ const data = JSON.parse(readFileSync13(
5267
+ path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
5142
5268
  "utf8"
5143
5269
  ));
5144
5270
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -5208,8 +5334,8 @@ async function verifyPaneAtCapacity(sessionName) {
5208
5334
  }
5209
5335
  function readDebounceState() {
5210
5336
  try {
5211
- if (!existsSync12(DEBOUNCE_FILE)) return {};
5212
- const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
5337
+ if (!existsSync14(DEBOUNCE_FILE)) return {};
5338
+ const raw = JSON.parse(readFileSync13(DEBOUNCE_FILE, "utf8"));
5213
5339
  const state = {};
5214
5340
  for (const [key, val] of Object.entries(raw)) {
5215
5341
  if (typeof val === "number") {
@@ -5225,8 +5351,8 @@ function readDebounceState() {
5225
5351
  }
5226
5352
  function writeDebounceState(state) {
5227
5353
  try {
5228
- if (!existsSync12(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
5229
- writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
5354
+ if (!existsSync14(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
5355
+ writeFileSync9(DEBOUNCE_FILE, JSON.stringify(state));
5230
5356
  } catch {
5231
5357
  }
5232
5358
  }
@@ -5324,8 +5450,8 @@ function sendIntercom(targetSession) {
5324
5450
  try {
5325
5451
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
5326
5452
  const agent = baseAgentName(rawAgent);
5327
- const markerPath = path17.join(SESSION_CACHE, `current-task-${agent}.json`);
5328
- if (existsSync12(markerPath)) {
5453
+ const markerPath = path18.join(SESSION_CACHE, `current-task-${agent}.json`);
5454
+ if (existsSync14(markerPath)) {
5329
5455
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
5330
5456
  return "debounced";
5331
5457
  }
@@ -5334,8 +5460,8 @@ function sendIntercom(targetSession) {
5334
5460
  try {
5335
5461
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
5336
5462
  const agent = baseAgentName(rawAgent);
5337
- const taskDir = path17.join(process.cwd(), "exe", agent);
5338
- if (existsSync12(taskDir)) {
5463
+ const taskDir = path18.join(process.cwd(), "exe", agent);
5464
+ if (existsSync14(taskDir)) {
5339
5465
  const files = readdirSync4(taskDir).filter(
5340
5466
  (f) => f.endsWith(".md") && f !== "DONE.txt"
5341
5467
  );
@@ -5395,6 +5521,21 @@ function notifyParentExe(sessionKey) {
5395
5521
  }
5396
5522
  return true;
5397
5523
  }
5524
+ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitle) {
5525
+ const transport = getTransport();
5526
+ try {
5527
+ const sessions = transport.listSessions();
5528
+ if (!sessions.includes(coordinatorSession)) return false;
5529
+ execSync7(
5530
+ `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
5531
+ { timeout: 3e3 }
5532
+ );
5533
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
5534
+ return true;
5535
+ } catch {
5536
+ return false;
5537
+ }
5538
+ }
5398
5539
  function ensureEmployee(employeeName, exeSession, projectDir, opts) {
5399
5540
  if (isCoordinatorName(employeeName)) {
5400
5541
  return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
@@ -5468,26 +5609,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5468
5609
  const transport = getTransport();
5469
5610
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
5470
5611
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
5471
- const logDir = path17.join(os9.homedir(), ".exe-os", "session-logs");
5472
- const logFile = path17.join(logDir, `${instanceLabel}-${Date.now()}.log`);
5473
- if (!existsSync12(logDir)) {
5612
+ const logDir = path18.join(os10.homedir(), ".exe-os", "session-logs");
5613
+ const logFile = path18.join(logDir, `${instanceLabel}-${Date.now()}.log`);
5614
+ if (!existsSync14(logDir)) {
5474
5615
  mkdirSync7(logDir, { recursive: true });
5475
5616
  }
5476
5617
  transport.kill(sessionName);
5477
5618
  let cleanupSuffix = "";
5478
5619
  try {
5479
5620
  const thisFile = fileURLToPath2(import.meta.url);
5480
- const cleanupScript = path17.join(path17.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
5481
- if (existsSync12(cleanupScript)) {
5621
+ const cleanupScript = path18.join(path18.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
5622
+ if (existsSync14(cleanupScript)) {
5482
5623
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
5483
5624
  }
5484
5625
  } catch {
5485
5626
  }
5486
5627
  try {
5487
- const claudeJsonPath = path17.join(os9.homedir(), ".claude.json");
5628
+ const claudeJsonPath = path18.join(os10.homedir(), ".claude.json");
5488
5629
  let claudeJson = {};
5489
5630
  try {
5490
- claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
5631
+ claudeJson = JSON.parse(readFileSync13(claudeJsonPath, "utf8"));
5491
5632
  } catch {
5492
5633
  }
5493
5634
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -5495,17 +5636,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5495
5636
  const trustDir = opts?.cwd ?? projectDir;
5496
5637
  if (!projects[trustDir]) projects[trustDir] = {};
5497
5638
  projects[trustDir].hasTrustDialogAccepted = true;
5498
- writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
5639
+ writeFileSync9(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
5499
5640
  } catch {
5500
5641
  }
5501
5642
  try {
5502
- const settingsDir = path17.join(os9.homedir(), ".claude", "projects");
5643
+ const settingsDir = path18.join(os10.homedir(), ".claude", "projects");
5503
5644
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
5504
- const projSettingsDir = path17.join(settingsDir, normalizedKey);
5505
- const settingsPath = path17.join(projSettingsDir, "settings.json");
5645
+ const projSettingsDir = path18.join(settingsDir, normalizedKey);
5646
+ const settingsPath = path18.join(projSettingsDir, "settings.json");
5506
5647
  let settings = {};
5507
5648
  try {
5508
- settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
5649
+ settings = JSON.parse(readFileSync13(settingsPath, "utf8"));
5509
5650
  } catch {
5510
5651
  }
5511
5652
  const perms = settings.permissions ?? {};
@@ -5534,7 +5675,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5534
5675
  perms.allow = allow;
5535
5676
  settings.permissions = perms;
5536
5677
  mkdirSync7(projSettingsDir, { recursive: true });
5537
- writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
5678
+ writeFileSync9(settingsPath, JSON.stringify(settings, null, 2) + "\n");
5538
5679
  }
5539
5680
  } catch {
5540
5681
  }
@@ -5549,8 +5690,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5549
5690
  let behaviorsFlag = "";
5550
5691
  let legacyFallbackWarned = false;
5551
5692
  if (!useExeAgent && !useBinSymlink) {
5552
- const identityPath = path17.join(
5553
- os9.homedir(),
5693
+ const identityPath = path18.join(
5694
+ os10.homedir(),
5554
5695
  ".exe-os",
5555
5696
  "identity",
5556
5697
  `${employeeName}.md`
@@ -5559,13 +5700,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5559
5700
  const hasAgentFlag = claudeSupportsAgentFlag();
5560
5701
  if (hasAgentFlag) {
5561
5702
  identityFlag = ` --agent ${employeeName}`;
5562
- } else if (existsSync12(identityPath)) {
5703
+ } else if (existsSync14(identityPath)) {
5563
5704
  identityFlag = ` --append-system-prompt-file ${identityPath}`;
5564
5705
  legacyFallbackWarned = true;
5565
5706
  }
5566
5707
  const behaviorsFile = exportBehaviorsSync(
5567
5708
  employeeName,
5568
- path17.basename(spawnCwd),
5709
+ path18.basename(spawnCwd),
5569
5710
  sessionName
5570
5711
  );
5571
5712
  if (behaviorsFile) {
@@ -5580,16 +5721,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5580
5721
  }
5581
5722
  let sessionContextFlag = "";
5582
5723
  try {
5583
- const ctxDir = path17.join(os9.homedir(), ".exe-os", "session-cache");
5724
+ const ctxDir = path18.join(os10.homedir(), ".exe-os", "session-cache");
5584
5725
  mkdirSync7(ctxDir, { recursive: true });
5585
- const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
5726
+ const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
5586
5727
  const ctxContent = [
5587
5728
  `## Session Context`,
5588
5729
  `You are running in tmux session: ${sessionName}.`,
5589
5730
  `Your parent coordinator session is ${exeSession}.`,
5590
5731
  `Your employees (if any) use the -${exeSession} suffix.`
5591
5732
  ].join("\n");
5592
- writeFileSync8(ctxFile, ctxContent);
5733
+ writeFileSync9(ctxFile, ctxContent);
5593
5734
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
5594
5735
  } catch {
5595
5736
  }
@@ -5666,8 +5807,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5666
5807
  transport.pipeLog(sessionName, logFile);
5667
5808
  try {
5668
5809
  const mySession = getMySession();
5669
- const dispatchInfo = path17.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5670
- writeFileSync8(dispatchInfo, JSON.stringify({
5810
+ const dispatchInfo = path18.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5811
+ writeFileSync9(dispatchInfo, JSON.stringify({
5671
5812
  dispatchedBy: mySession,
5672
5813
  rootExe: exeSession,
5673
5814
  provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
@@ -5741,15 +5882,15 @@ var init_tmux_routing = __esm({
5741
5882
  init_intercom_queue();
5742
5883
  init_plan_limits();
5743
5884
  init_employees();
5744
- SPAWN_LOCK_DIR = path17.join(os9.homedir(), ".exe-os", "spawn-locks");
5745
- SESSION_CACHE = path17.join(os9.homedir(), ".exe-os", "session-cache");
5885
+ SPAWN_LOCK_DIR = path18.join(os10.homedir(), ".exe-os", "spawn-locks");
5886
+ SESSION_CACHE = path18.join(os10.homedir(), ".exe-os", "session-cache");
5746
5887
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
5747
5888
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
5748
5889
  VERIFY_PANE_LINES = 200;
5749
5890
  INTERCOM_DEBOUNCE_MS = 3e4;
5750
5891
  CODEX_DEBOUNCE_MS = 12e4;
5751
- INTERCOM_LOG2 = path17.join(os9.homedir(), ".exe-os", "intercom.log");
5752
- DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
5892
+ INTERCOM_LOG2 = path18.join(os10.homedir(), ".exe-os", "intercom.log");
5893
+ DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
5753
5894
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
5754
5895
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
5755
5896
  }
@@ -5772,6 +5913,15 @@ function sessionScopeFilter(sessionScope, tableAlias) {
5772
5913
  args: [scope]
5773
5914
  };
5774
5915
  }
5916
+ function strictSessionScopeFilter(sessionScope, tableAlias) {
5917
+ const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
5918
+ if (!scope) return { sql: "", args: [] };
5919
+ const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
5920
+ return {
5921
+ sql: ` AND ${col} = ?`,
5922
+ args: [scope]
5923
+ };
5924
+ }
5775
5925
  var init_task_scope = __esm({
5776
5926
  "src/lib/task-scope.ts"() {
5777
5927
  "use strict";
@@ -5790,14 +5940,14 @@ var init_memory = __esm({
5790
5940
 
5791
5941
  // src/lib/keychain.ts
5792
5942
  import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
5793
- import { existsSync as existsSync13 } from "fs";
5794
- import path18 from "path";
5795
- import os10 from "os";
5943
+ import { existsSync as existsSync15 } from "fs";
5944
+ import path19 from "path";
5945
+ import os11 from "os";
5796
5946
  function getKeyDir() {
5797
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path18.join(os10.homedir(), ".exe-os");
5947
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path19.join(os11.homedir(), ".exe-os");
5798
5948
  }
5799
5949
  function getKeyPath() {
5800
- return path18.join(getKeyDir(), "master.key");
5950
+ return path19.join(getKeyDir(), "master.key");
5801
5951
  }
5802
5952
  async function tryKeytar() {
5803
5953
  try {
@@ -5818,9 +5968,9 @@ async function getMasterKey() {
5818
5968
  }
5819
5969
  }
5820
5970
  const keyPath = getKeyPath();
5821
- if (!existsSync13(keyPath)) {
5971
+ if (!existsSync15(keyPath)) {
5822
5972
  process.stderr.write(
5823
- `[keychain] Key not found at ${keyPath} (HOME=${os10.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
5973
+ `[keychain] Key not found at ${keyPath} (HOME=${os11.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
5824
5974
  `
5825
5975
  );
5826
5976
  return null;
@@ -5850,6 +6000,7 @@ var shard_manager_exports = {};
5850
6000
  __export(shard_manager_exports, {
5851
6001
  disposeShards: () => disposeShards,
5852
6002
  ensureShardSchema: () => ensureShardSchema,
6003
+ getOpenShardCount: () => getOpenShardCount,
5853
6004
  getReadyShardClient: () => getReadyShardClient,
5854
6005
  getShardClient: () => getShardClient,
5855
6006
  getShardsDir: () => getShardsDir,
@@ -5858,15 +6009,18 @@ __export(shard_manager_exports, {
5858
6009
  listShards: () => listShards,
5859
6010
  shardExists: () => shardExists
5860
6011
  });
5861
- import path19 from "path";
5862
- import { existsSync as existsSync14, mkdirSync as mkdirSync8, readdirSync as readdirSync5 } from "fs";
6012
+ import path20 from "path";
6013
+ import { existsSync as existsSync16, mkdirSync as mkdirSync8, readdirSync as readdirSync5 } from "fs";
5863
6014
  import { createClient as createClient2 } from "@libsql/client";
5864
6015
  function initShardManager(encryptionKey) {
5865
6016
  _encryptionKey = encryptionKey;
5866
- if (!existsSync14(SHARDS_DIR)) {
6017
+ if (!existsSync16(SHARDS_DIR)) {
5867
6018
  mkdirSync8(SHARDS_DIR, { recursive: true });
5868
6019
  }
5869
6020
  _shardingEnabled = true;
6021
+ if (_evictionTimer) clearInterval(_evictionTimer);
6022
+ _evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
6023
+ _evictionTimer.unref();
5870
6024
  }
5871
6025
  function isShardingEnabled() {
5872
6026
  return _shardingEnabled;
@@ -5883,21 +6037,28 @@ function getShardClient(projectName) {
5883
6037
  throw new Error(`Invalid project name for shard: "${projectName}"`);
5884
6038
  }
5885
6039
  const cached = _shards.get(safeName);
5886
- if (cached) return cached;
5887
- const dbPath = path19.join(SHARDS_DIR, `${safeName}.db`);
6040
+ if (cached) {
6041
+ _shardLastAccess.set(safeName, Date.now());
6042
+ return cached;
6043
+ }
6044
+ while (_shards.size >= MAX_OPEN_SHARDS) {
6045
+ evictLRU();
6046
+ }
6047
+ const dbPath = path20.join(SHARDS_DIR, `${safeName}.db`);
5888
6048
  const client = createClient2({
5889
6049
  url: `file:${dbPath}`,
5890
6050
  encryptionKey: _encryptionKey
5891
6051
  });
5892
6052
  _shards.set(safeName, client);
6053
+ _shardLastAccess.set(safeName, Date.now());
5893
6054
  return client;
5894
6055
  }
5895
6056
  function shardExists(projectName) {
5896
6057
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
5897
- return existsSync14(path19.join(SHARDS_DIR, `${safeName}.db`));
6058
+ return existsSync16(path20.join(SHARDS_DIR, `${safeName}.db`));
5898
6059
  }
5899
6060
  function listShards() {
5900
- if (!existsSync14(SHARDS_DIR)) return [];
6061
+ if (!existsSync16(SHARDS_DIR)) return [];
5901
6062
  return readdirSync5(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
5902
6063
  }
5903
6064
  async function ensureShardSchema(client) {
@@ -5949,6 +6110,8 @@ async function ensureShardSchema(client) {
5949
6110
  for (const col of [
5950
6111
  "ALTER TABLE memories ADD COLUMN task_id TEXT",
5951
6112
  "ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
6113
+ "ALTER TABLE memories ADD COLUMN author_device_id TEXT",
6114
+ "ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
5952
6115
  "ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
5953
6116
  "ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
5954
6117
  "ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
@@ -6086,21 +6249,69 @@ async function getReadyShardClient(projectName) {
6086
6249
  await ensureShardSchema(client);
6087
6250
  return client;
6088
6251
  }
6252
+ function evictLRU() {
6253
+ let oldest = null;
6254
+ let oldestTime = Infinity;
6255
+ for (const [name, time] of _shardLastAccess) {
6256
+ if (time < oldestTime) {
6257
+ oldestTime = time;
6258
+ oldest = name;
6259
+ }
6260
+ }
6261
+ if (oldest) {
6262
+ const client = _shards.get(oldest);
6263
+ if (client) {
6264
+ client.close();
6265
+ }
6266
+ _shards.delete(oldest);
6267
+ _shardLastAccess.delete(oldest);
6268
+ }
6269
+ }
6270
+ function evictIdleShards() {
6271
+ const now = Date.now();
6272
+ const toEvict = [];
6273
+ for (const [name, lastAccess] of _shardLastAccess) {
6274
+ if (now - lastAccess > SHARD_IDLE_MS) {
6275
+ toEvict.push(name);
6276
+ }
6277
+ }
6278
+ for (const name of toEvict) {
6279
+ const client = _shards.get(name);
6280
+ if (client) {
6281
+ client.close();
6282
+ }
6283
+ _shards.delete(name);
6284
+ _shardLastAccess.delete(name);
6285
+ }
6286
+ }
6287
+ function getOpenShardCount() {
6288
+ return _shards.size;
6289
+ }
6089
6290
  function disposeShards() {
6291
+ if (_evictionTimer) {
6292
+ clearInterval(_evictionTimer);
6293
+ _evictionTimer = null;
6294
+ }
6090
6295
  for (const [, client] of _shards) {
6091
6296
  client.close();
6092
6297
  }
6093
6298
  _shards.clear();
6299
+ _shardLastAccess.clear();
6094
6300
  _shardingEnabled = false;
6095
6301
  _encryptionKey = null;
6096
6302
  }
6097
- var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
6303
+ var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
6098
6304
  var init_shard_manager = __esm({
6099
6305
  "src/lib/shard-manager.ts"() {
6100
6306
  "use strict";
6101
6307
  init_config();
6102
- SHARDS_DIR = path19.join(EXE_AI_DIR, "shards");
6308
+ SHARDS_DIR = path20.join(EXE_AI_DIR, "shards");
6309
+ SHARD_IDLE_MS = 5 * 60 * 1e3;
6310
+ MAX_OPEN_SHARDS = 10;
6311
+ EVICTION_INTERVAL_MS = 60 * 1e3;
6103
6312
  _shards = /* @__PURE__ */ new Map();
6313
+ _shardLastAccess = /* @__PURE__ */ new Map();
6314
+ _evictionTimer = null;
6104
6315
  _encryptionKey = null;
6105
6316
  _shardingEnabled = false;
6106
6317
  }
@@ -6864,13 +7075,13 @@ var init_store = __esm({
6864
7075
  });
6865
7076
 
6866
7077
  // src/adapters/claude/hooks/pre-compact.ts
6867
- import crypto6 from "crypto";
7078
+ import crypto7 from "crypto";
6868
7079
 
6869
7080
  // src/lib/active-agent.ts
6870
7081
  init_config();
6871
7082
  init_session_key();
6872
7083
  init_employees();
6873
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync, unlinkSync as unlinkSync2, readdirSync } from "fs";
7084
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync } from "fs";
6874
7085
  import { execSync as execSync3 } from "child_process";
6875
7086
  import path3 from "path";
6876
7087
  var CACHE_DIR = path3.join(EXE_AI_DIR, "session-cache");
@@ -7037,7 +7248,7 @@ ${taskLines}`);
7037
7248
  recoveryLines.push(`Files: ${lastCheckpoint.files_touched.join(", ")}`);
7038
7249
  }
7039
7250
  await writeMemory2({
7040
- id: crypto6.randomUUID(),
7251
+ id: crypto7.randomUUID(),
7041
7252
  agent_id: agent.agentId,
7042
7253
  agent_role: agent.agentRole,
7043
7254
  session_id: payload.session_id,