@askexenow/exe-os 0.9.8 → 0.9.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/bin/backfill-conversations.js +222 -49
  2. package/dist/bin/backfill-responses.js +221 -48
  3. package/dist/bin/backfill-vectors.js +225 -52
  4. package/dist/bin/cleanup-stale-review-tasks.js +150 -28
  5. package/dist/bin/cli.js +1295 -856
  6. package/dist/bin/exe-agent-config.js +36 -8
  7. package/dist/bin/exe-agent.js +14 -4
  8. package/dist/bin/exe-assign.js +221 -48
  9. package/dist/bin/exe-boot.js +778 -427
  10. package/dist/bin/exe-call.js +41 -13
  11. package/dist/bin/exe-cloud.js +163 -58
  12. package/dist/bin/exe-dispatch.js +276 -139
  13. package/dist/bin/exe-doctor.js +145 -27
  14. package/dist/bin/exe-export-behaviors.js +141 -23
  15. package/dist/bin/exe-forget.js +137 -19
  16. package/dist/bin/exe-gateway.js +677 -388
  17. package/dist/bin/exe-heartbeat.js +227 -108
  18. package/dist/bin/exe-kill.js +138 -20
  19. package/dist/bin/exe-launch-agent.js +172 -39
  20. package/dist/bin/exe-link.js +291 -100
  21. package/dist/bin/exe-new-employee.js +214 -106
  22. package/dist/bin/exe-pending-messages.js +395 -33
  23. package/dist/bin/exe-pending-notifications.js +684 -99
  24. package/dist/bin/exe-pending-reviews.js +420 -74
  25. package/dist/bin/exe-rename.js +147 -49
  26. package/dist/bin/exe-review.js +138 -20
  27. package/dist/bin/exe-search.js +240 -69
  28. package/dist/bin/exe-session-cleanup.js +440 -250
  29. package/dist/bin/exe-settings.js +61 -17
  30. package/dist/bin/exe-start-codex.js +158 -39
  31. package/dist/bin/exe-start-opencode.js +157 -38
  32. package/dist/bin/exe-status.js +151 -29
  33. package/dist/bin/exe-team.js +138 -20
  34. package/dist/bin/git-sweep.js +404 -212
  35. package/dist/bin/graph-backfill.js +137 -19
  36. package/dist/bin/graph-export.js +140 -22
  37. package/dist/bin/install.js +90 -61
  38. package/dist/bin/scan-tasks.js +412 -220
  39. package/dist/bin/setup.js +564 -293
  40. package/dist/bin/shard-migrate.js +139 -21
  41. package/dist/bin/update.js +138 -49
  42. package/dist/bin/wiki-sync.js +137 -19
  43. package/dist/gateway/index.js +533 -320
  44. package/dist/hooks/bug-report-worker.js +344 -193
  45. package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
  46. package/dist/hooks/commit-complete.js +402 -210
  47. package/dist/hooks/error-recall.js +245 -74
  48. package/dist/hooks/exe-heartbeat-hook.js +16 -6
  49. package/dist/hooks/ingest-worker.js +3423 -3157
  50. package/dist/hooks/ingest.js +832 -97
  51. package/dist/hooks/instructions-loaded.js +227 -54
  52. package/dist/hooks/notification.js +216 -43
  53. package/dist/hooks/post-compact.js +239 -62
  54. package/dist/hooks/pre-compact.js +408 -216
  55. package/dist/hooks/pre-tool-use.js +268 -90
  56. package/dist/hooks/prompt-ingest-worker.js +352 -102
  57. package/dist/hooks/prompt-submit.js +541 -328
  58. package/dist/hooks/response-ingest-worker.js +372 -122
  59. package/dist/hooks/session-end.js +443 -240
  60. package/dist/hooks/session-start.js +313 -127
  61. package/dist/hooks/stop.js +293 -98
  62. package/dist/hooks/subagent-stop.js +239 -62
  63. package/dist/hooks/summary-worker.js +568 -236
  64. package/dist/index.js +538 -324
  65. package/dist/lib/agent-config.js +28 -6
  66. package/dist/lib/cloud-sync.js +284 -105
  67. package/dist/lib/config.js +30 -10
  68. package/dist/lib/consolidation.js +16 -6
  69. package/dist/lib/database.js +123 -25
  70. package/dist/lib/db-daemon-client.js +73 -19
  71. package/dist/lib/db.js +123 -25
  72. package/dist/lib/device-registry.js +133 -35
  73. package/dist/lib/embedder.js +107 -32
  74. package/dist/lib/employee-templates.js +14 -4
  75. package/dist/lib/employees.js +41 -13
  76. package/dist/lib/exe-daemon-client.js +88 -22
  77. package/dist/lib/exe-daemon.js +935 -587
  78. package/dist/lib/hybrid-search.js +240 -69
  79. package/dist/lib/identity.js +18 -8
  80. package/dist/lib/license.js +133 -48
  81. package/dist/lib/messaging.js +116 -56
  82. package/dist/lib/reminders.js +14 -4
  83. package/dist/lib/schedules.js +137 -19
  84. package/dist/lib/skill-learning.js +33 -6
  85. package/dist/lib/store.js +137 -19
  86. package/dist/lib/task-router.js +14 -4
  87. package/dist/lib/tasks.js +280 -234
  88. package/dist/lib/tmux-routing.js +172 -125
  89. package/dist/lib/token-spend.js +26 -8
  90. package/dist/mcp/server.js +1326 -609
  91. package/dist/mcp/tools/complete-reminder.js +14 -4
  92. package/dist/mcp/tools/create-reminder.js +14 -4
  93. package/dist/mcp/tools/create-task.js +306 -248
  94. package/dist/mcp/tools/deactivate-behavior.js +16 -6
  95. package/dist/mcp/tools/list-reminders.js +14 -4
  96. package/dist/mcp/tools/list-tasks.js +123 -107
  97. package/dist/mcp/tools/send-message.js +75 -29
  98. package/dist/mcp/tools/update-task.js +1848 -199
  99. package/dist/runtime/index.js +441 -248
  100. package/dist/tui/App.js +761 -424
  101. package/package.json +1 -1
@@ -19,6 +19,44 @@ var __copyProps = (to, from, except, desc) => {
19
19
  };
20
20
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
21
 
22
+ // src/lib/secure-files.ts
23
+ import { chmodSync, existsSync, mkdirSync } from "fs";
24
+ import { chmod, mkdir } from "fs/promises";
25
+ async function ensurePrivateDir(dirPath) {
26
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
27
+ try {
28
+ await chmod(dirPath, PRIVATE_DIR_MODE);
29
+ } catch {
30
+ }
31
+ }
32
+ function ensurePrivateDirSync(dirPath) {
33
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
34
+ try {
35
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
36
+ } catch {
37
+ }
38
+ }
39
+ async function enforcePrivateFile(filePath) {
40
+ try {
41
+ await chmod(filePath, PRIVATE_FILE_MODE);
42
+ } catch {
43
+ }
44
+ }
45
+ function enforcePrivateFileSync(filePath) {
46
+ try {
47
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
48
+ } catch {
49
+ }
50
+ }
51
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
52
+ var init_secure_files = __esm({
53
+ "src/lib/secure-files.ts"() {
54
+ "use strict";
55
+ PRIVATE_DIR_MODE = 448;
56
+ PRIVATE_FILE_MODE = 384;
57
+ }
58
+ });
59
+
22
60
  // src/lib/config.ts
23
61
  var config_exports = {};
24
62
  __export(config_exports, {
@@ -35,8 +73,8 @@ __export(config_exports, {
35
73
  migrateConfig: () => migrateConfig,
36
74
  saveConfig: () => saveConfig
37
75
  });
38
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
39
- import { readFileSync, existsSync, renameSync } from "fs";
76
+ import { readFile, writeFile } from "fs/promises";
77
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
40
78
  import path from "path";
41
79
  import os from "os";
42
80
  function resolveDataDir() {
@@ -44,7 +82,7 @@ function resolveDataDir() {
44
82
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
45
83
  const newDir = path.join(os.homedir(), ".exe-os");
46
84
  const legacyDir = path.join(os.homedir(), ".exe-mem");
47
- if (!existsSync(newDir) && existsSync(legacyDir)) {
85
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
48
86
  try {
49
87
  renameSync(legacyDir, newDir);
50
88
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -107,9 +145,9 @@ function normalizeAutoUpdate(raw) {
107
145
  }
108
146
  async function loadConfig() {
109
147
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
110
- await mkdir(dir, { recursive: true });
148
+ await ensurePrivateDir(dir);
111
149
  const configPath = path.join(dir, "config.json");
112
- if (!existsSync(configPath)) {
150
+ if (!existsSync2(configPath)) {
113
151
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
114
152
  }
115
153
  const raw = await readFile(configPath, "utf-8");
@@ -122,6 +160,7 @@ async function loadConfig() {
122
160
  `);
123
161
  try {
124
162
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
163
+ await enforcePrivateFile(configPath);
125
164
  } catch {
126
165
  }
127
166
  }
@@ -140,7 +179,7 @@ async function loadConfig() {
140
179
  function loadConfigSync() {
141
180
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
142
181
  const configPath = path.join(dir, "config.json");
143
- if (!existsSync(configPath)) {
182
+ if (!existsSync2(configPath)) {
144
183
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
145
184
  }
146
185
  try {
@@ -158,12 +197,10 @@ function loadConfigSync() {
158
197
  }
159
198
  async function saveConfig(config) {
160
199
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
161
- await mkdir(dir, { recursive: true });
200
+ await ensurePrivateDir(dir);
162
201
  const configPath = path.join(dir, "config.json");
163
202
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
164
- if (config.cloud?.apiKey) {
165
- await chmod(configPath, 384);
166
- }
203
+ await enforcePrivateFile(configPath);
167
204
  }
168
205
  async function loadConfigFrom(configPath) {
169
206
  const raw = await readFile(configPath, "utf-8");
@@ -183,6 +220,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
183
220
  var init_config = __esm({
184
221
  "src/lib/config.ts"() {
185
222
  "use strict";
223
+ init_secure_files();
186
224
  EXE_AI_DIR = resolveDataDir();
187
225
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
188
226
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -363,10 +401,10 @@ __export(agent_config_exports, {
363
401
  saveAgentConfig: () => saveAgentConfig,
364
402
  setAgentRuntime: () => setAgentRuntime
365
403
  });
366
- import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
404
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
367
405
  import path2 from "path";
368
406
  function loadAgentConfig() {
369
- if (!existsSync2(AGENT_CONFIG_PATH)) return {};
407
+ if (!existsSync3(AGENT_CONFIG_PATH)) return {};
370
408
  try {
371
409
  return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
372
410
  } catch {
@@ -375,8 +413,9 @@ function loadAgentConfig() {
375
413
  }
376
414
  function saveAgentConfig(config) {
377
415
  const dir = path2.dirname(AGENT_CONFIG_PATH);
378
- if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
416
+ ensurePrivateDirSync(dir);
379
417
  writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
418
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
380
419
  }
381
420
  function getAgentRuntime(agentId) {
382
421
  const config = loadAgentConfig();
@@ -416,6 +455,7 @@ var init_agent_config = __esm({
416
455
  "use strict";
417
456
  init_config();
418
457
  init_runtime_table();
458
+ init_secure_files();
419
459
  AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
420
460
  KNOWN_RUNTIMES = {
421
461
  claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
@@ -463,7 +503,7 @@ __export(employees_exports, {
463
503
  validateEmployeeName: () => validateEmployeeName
464
504
  });
465
505
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
466
- import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
506
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
467
507
  import { execSync } from "child_process";
468
508
  import path3 from "path";
469
509
  import os2 from "os";
@@ -502,7 +542,7 @@ function validateEmployeeName(name) {
502
542
  return { valid: true };
503
543
  }
504
544
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
505
- if (!existsSync3(employeesPath)) {
545
+ if (!existsSync4(employeesPath)) {
506
546
  return [];
507
547
  }
508
548
  const raw = await readFile2(employeesPath, "utf-8");
@@ -517,7 +557,7 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
517
557
  await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
518
558
  }
519
559
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
520
- if (!existsSync3(employeesPath)) return [];
560
+ if (!existsSync4(employeesPath)) return [];
521
561
  try {
522
562
  return JSON.parse(readFileSync3(employeesPath, "utf-8"));
523
563
  } catch {
@@ -565,7 +605,7 @@ function appendToCoordinatorTeam(employee) {
565
605
  const coordinator = getCoordinatorEmployee(loadEmployeesSync());
566
606
  if (!coordinator) return;
567
607
  const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
568
- if (!existsSync3(idPath)) return;
608
+ if (!existsSync4(idPath)) return;
569
609
  const content = readFileSync3(idPath, "utf-8");
570
610
  if (content.includes(`**${capitalize(employee.name)}`)) return;
571
611
  const teamMatch = content.match(TEAM_SECTION_RE);
@@ -619,9 +659,9 @@ async function normalizeRosterCase(rosterPath) {
619
659
  const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
620
660
  const oldPath = path3.join(identityDir, `${oldName}.md`);
621
661
  const newPath = path3.join(identityDir, `${emp.name}.md`);
622
- if (existsSync3(oldPath) && !existsSync3(newPath)) {
662
+ if (existsSync4(oldPath) && !existsSync4(newPath)) {
623
663
  renameSync2(oldPath, newPath);
624
- } else if (existsSync3(oldPath) && oldPath !== newPath) {
664
+ } else if (existsSync4(oldPath) && oldPath !== newPath) {
625
665
  const content = readFileSync3(oldPath, "utf-8");
626
666
  writeFileSync2(newPath, content, "utf-8");
627
667
  if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
@@ -664,7 +704,7 @@ function registerBinSymlinks(name) {
664
704
  for (const suffix of ["", "-opencode"]) {
665
705
  const linkName = `${name}${suffix}`;
666
706
  const linkPath = path3.join(binDir, linkName);
667
- if (existsSync3(linkPath)) {
707
+ if (existsSync4(linkPath)) {
668
708
  skipped.push(linkName);
669
709
  continue;
670
710
  }
@@ -1275,13 +1315,50 @@ var init_database_adapter = __esm({
1275
1315
  }
1276
1316
  });
1277
1317
 
1318
+ // src/lib/daemon-auth.ts
1319
+ import crypto from "crypto";
1320
+ import path5 from "path";
1321
+ import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
1322
+ function normalizeToken(token) {
1323
+ if (!token) return null;
1324
+ const trimmed = token.trim();
1325
+ return trimmed.length > 0 ? trimmed : null;
1326
+ }
1327
+ function readDaemonToken() {
1328
+ try {
1329
+ if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
1330
+ return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
1331
+ } catch {
1332
+ return null;
1333
+ }
1334
+ }
1335
+ function ensureDaemonToken(seed) {
1336
+ const existing = readDaemonToken();
1337
+ if (existing) return existing;
1338
+ const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
1339
+ ensurePrivateDirSync(EXE_AI_DIR);
1340
+ writeFileSync3(DAEMON_TOKEN_PATH, `${token}
1341
+ `, "utf8");
1342
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
1343
+ return token;
1344
+ }
1345
+ var DAEMON_TOKEN_PATH;
1346
+ var init_daemon_auth = __esm({
1347
+ "src/lib/daemon-auth.ts"() {
1348
+ "use strict";
1349
+ init_config();
1350
+ init_secure_files();
1351
+ DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
1352
+ }
1353
+ });
1354
+
1278
1355
  // src/lib/exe-daemon-client.ts
1279
1356
  import net from "net";
1280
1357
  import os4 from "os";
1281
1358
  import { spawn } from "child_process";
1282
1359
  import { randomUUID } from "crypto";
1283
- import { existsSync as existsSync4, unlinkSync as unlinkSync2, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
1284
- import path5 from "path";
1360
+ import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
1361
+ import path6 from "path";
1285
1362
  import { fileURLToPath } from "url";
1286
1363
  function handleData(chunk) {
1287
1364
  _buffer += chunk.toString();
@@ -1309,9 +1386,9 @@ function handleData(chunk) {
1309
1386
  }
1310
1387
  }
1311
1388
  function cleanupStaleFiles() {
1312
- if (existsSync4(PID_PATH)) {
1389
+ if (existsSync6(PID_PATH)) {
1313
1390
  try {
1314
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1391
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1315
1392
  if (pid > 0) {
1316
1393
  try {
1317
1394
  process.kill(pid, 0);
@@ -1332,11 +1409,11 @@ function cleanupStaleFiles() {
1332
1409
  }
1333
1410
  }
1334
1411
  function findPackageRoot() {
1335
- let dir = path5.dirname(fileURLToPath(import.meta.url));
1336
- const { root } = path5.parse(dir);
1412
+ let dir = path6.dirname(fileURLToPath(import.meta.url));
1413
+ const { root } = path6.parse(dir);
1337
1414
  while (dir !== root) {
1338
- if (existsSync4(path5.join(dir, "package.json"))) return dir;
1339
- dir = path5.dirname(dir);
1415
+ if (existsSync6(path6.join(dir, "package.json"))) return dir;
1416
+ dir = path6.dirname(dir);
1340
1417
  }
1341
1418
  return null;
1342
1419
  }
@@ -1362,16 +1439,17 @@ function spawnDaemon() {
1362
1439
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
1363
1440
  return;
1364
1441
  }
1365
- const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1366
- if (!existsSync4(daemonPath)) {
1442
+ const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1443
+ if (!existsSync6(daemonPath)) {
1367
1444
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1368
1445
  `);
1369
1446
  return;
1370
1447
  }
1371
1448
  const resolvedPath = daemonPath;
1449
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
1372
1450
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1373
1451
  `);
1374
- const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
1452
+ const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
1375
1453
  let stderrFd = "ignore";
1376
1454
  try {
1377
1455
  stderrFd = openSync(logPath, "a");
@@ -1389,7 +1467,8 @@ function spawnDaemon() {
1389
1467
  TMUX_PANE: void 0,
1390
1468
  // Prevents resolveExeSession() from scoping to one session
1391
1469
  EXE_DAEMON_SOCK: SOCKET_PATH,
1392
- EXE_DAEMON_PID: PID_PATH
1470
+ EXE_DAEMON_PID: PID_PATH,
1471
+ [DAEMON_TOKEN_ENV]: daemonToken
1393
1472
  }
1394
1473
  });
1395
1474
  child.unref();
@@ -1499,13 +1578,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
1499
1578
  return;
1500
1579
  }
1501
1580
  const id = randomUUID();
1581
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
1502
1582
  const timer = setTimeout(() => {
1503
1583
  _pending.delete(id);
1504
1584
  resolve({ error: "Request timeout" });
1505
1585
  }, timeoutMs);
1506
1586
  _pending.set(id, { resolve, timer });
1507
1587
  try {
1508
- _socket.write(JSON.stringify({ id, ...payload }) + "\n");
1588
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
1509
1589
  } catch {
1510
1590
  clearTimeout(timer);
1511
1591
  _pending.delete(id);
@@ -1534,9 +1614,9 @@ function killAndRespawnDaemon() {
1534
1614
  }
1535
1615
  try {
1536
1616
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
1537
- if (existsSync4(PID_PATH)) {
1617
+ if (existsSync6(PID_PATH)) {
1538
1618
  try {
1539
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1619
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1540
1620
  if (pid > 0) {
1541
1621
  try {
1542
1622
  process.kill(pid, "SIGKILL");
@@ -1656,17 +1736,19 @@ function disconnectClient() {
1656
1736
  function isClientConnected() {
1657
1737
  return _connected;
1658
1738
  }
1659
- 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;
1739
+ 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;
1660
1740
  var init_exe_daemon_client = __esm({
1661
1741
  "src/lib/exe-daemon-client.ts"() {
1662
1742
  "use strict";
1663
1743
  init_config();
1664
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
1665
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
1666
- SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
1744
+ init_daemon_auth();
1745
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
1746
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
1747
+ SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
1667
1748
  SPAWN_LOCK_STALE_MS = 3e4;
1668
1749
  CONNECT_TIMEOUT_MS = 15e3;
1669
1750
  REQUEST_TIMEOUT_MS = 3e4;
1751
+ DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
1670
1752
  _socket = null;
1671
1753
  _connected = false;
1672
1754
  _buffer = "";
@@ -2251,6 +2333,7 @@ async function ensureSchema() {
2251
2333
  project TEXT NOT NULL,
2252
2334
  summary TEXT NOT NULL,
2253
2335
  task_file TEXT,
2336
+ session_scope TEXT,
2254
2337
  read INTEGER NOT NULL DEFAULT 0,
2255
2338
  created_at TEXT NOT NULL
2256
2339
  );
@@ -2259,7 +2342,7 @@ async function ensureSchema() {
2259
2342
  ON notifications(read);
2260
2343
 
2261
2344
  CREATE INDEX IF NOT EXISTS idx_notifications_agent
2262
- ON notifications(agent_id);
2345
+ ON notifications(agent_id, session_scope);
2263
2346
 
2264
2347
  CREATE INDEX IF NOT EXISTS idx_notifications_task_file
2265
2348
  ON notifications(task_file);
@@ -2297,6 +2380,7 @@ async function ensureSchema() {
2297
2380
  target_agent TEXT NOT NULL,
2298
2381
  target_project TEXT,
2299
2382
  target_device TEXT NOT NULL DEFAULT 'local',
2383
+ session_scope TEXT,
2300
2384
  content TEXT NOT NULL,
2301
2385
  priority TEXT DEFAULT 'normal',
2302
2386
  status TEXT DEFAULT 'pending',
@@ -2310,10 +2394,31 @@ async function ensureSchema() {
2310
2394
  );
2311
2395
 
2312
2396
  CREATE INDEX IF NOT EXISTS idx_messages_target
2313
- ON messages(target_agent, status);
2397
+ ON messages(target_agent, session_scope, status);
2314
2398
 
2315
2399
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
2316
- ON messages(target_agent, from_agent, server_seq);
2400
+ ON messages(target_agent, session_scope, from_agent, server_seq);
2401
+ `);
2402
+ try {
2403
+ await client.execute({
2404
+ sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
2405
+ args: []
2406
+ });
2407
+ } catch {
2408
+ }
2409
+ try {
2410
+ await client.execute({
2411
+ sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
2412
+ args: []
2413
+ });
2414
+ } catch {
2415
+ }
2416
+ await client.executeMultiple(`
2417
+ CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
2418
+ ON notifications(agent_id, session_scope, read, created_at);
2419
+
2420
+ CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
2421
+ ON messages(target_agent, session_scope, status, created_at);
2317
2422
  `);
2318
2423
  try {
2319
2424
  await client.execute({
@@ -2897,6 +3002,13 @@ async function ensureSchema() {
2897
3002
  } catch {
2898
3003
  }
2899
3004
  }
3005
+ try {
3006
+ await client.execute({
3007
+ sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
3008
+ args: []
3009
+ });
3010
+ } catch {
3011
+ }
2900
3012
  }
2901
3013
  async function disposeDatabase() {
2902
3014
  if (_walCheckpointTimer) {
@@ -2936,14 +3048,14 @@ var init_database = __esm({
2936
3048
 
2937
3049
  // src/lib/keychain.ts
2938
3050
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
2939
- import { existsSync as existsSync5 } from "fs";
2940
- import path6 from "path";
3051
+ import { existsSync as existsSync7 } from "fs";
3052
+ import path7 from "path";
2941
3053
  import os5 from "os";
2942
3054
  function getKeyDir() {
2943
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
3055
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
2944
3056
  }
2945
3057
  function getKeyPath() {
2946
- return path6.join(getKeyDir(), "master.key");
3058
+ return path7.join(getKeyDir(), "master.key");
2947
3059
  }
2948
3060
  async function tryKeytar() {
2949
3061
  try {
@@ -2964,7 +3076,7 @@ async function getMasterKey() {
2964
3076
  }
2965
3077
  }
2966
3078
  const keyPath = getKeyPath();
2967
- if (!existsSync5(keyPath)) {
3079
+ if (!existsSync7(keyPath)) {
2968
3080
  process.stderr.write(
2969
3081
  `[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
2970
3082
  `
@@ -3051,6 +3163,7 @@ var shard_manager_exports = {};
3051
3163
  __export(shard_manager_exports, {
3052
3164
  disposeShards: () => disposeShards,
3053
3165
  ensureShardSchema: () => ensureShardSchema,
3166
+ getOpenShardCount: () => getOpenShardCount,
3054
3167
  getReadyShardClient: () => getReadyShardClient,
3055
3168
  getShardClient: () => getShardClient,
3056
3169
  getShardsDir: () => getShardsDir,
@@ -3059,15 +3172,18 @@ __export(shard_manager_exports, {
3059
3172
  listShards: () => listShards,
3060
3173
  shardExists: () => shardExists
3061
3174
  });
3062
- import path7 from "path";
3063
- import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync } from "fs";
3175
+ import path8 from "path";
3176
+ import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync } from "fs";
3064
3177
  import { createClient as createClient2 } from "@libsql/client";
3065
3178
  function initShardManager(encryptionKey) {
3066
3179
  _encryptionKey = encryptionKey;
3067
- if (!existsSync6(SHARDS_DIR)) {
3180
+ if (!existsSync8(SHARDS_DIR)) {
3068
3181
  mkdirSync2(SHARDS_DIR, { recursive: true });
3069
3182
  }
3070
3183
  _shardingEnabled = true;
3184
+ if (_evictionTimer) clearInterval(_evictionTimer);
3185
+ _evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
3186
+ _evictionTimer.unref();
3071
3187
  }
3072
3188
  function isShardingEnabled() {
3073
3189
  return _shardingEnabled;
@@ -3084,21 +3200,28 @@ function getShardClient(projectName) {
3084
3200
  throw new Error(`Invalid project name for shard: "${projectName}"`);
3085
3201
  }
3086
3202
  const cached = _shards.get(safeName);
3087
- if (cached) return cached;
3088
- const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
3203
+ if (cached) {
3204
+ _shardLastAccess.set(safeName, Date.now());
3205
+ return cached;
3206
+ }
3207
+ while (_shards.size >= MAX_OPEN_SHARDS) {
3208
+ evictLRU();
3209
+ }
3210
+ const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
3089
3211
  const client = createClient2({
3090
3212
  url: `file:${dbPath}`,
3091
3213
  encryptionKey: _encryptionKey
3092
3214
  });
3093
3215
  _shards.set(safeName, client);
3216
+ _shardLastAccess.set(safeName, Date.now());
3094
3217
  return client;
3095
3218
  }
3096
3219
  function shardExists(projectName) {
3097
3220
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3098
- return existsSync6(path7.join(SHARDS_DIR, `${safeName}.db`));
3221
+ return existsSync8(path8.join(SHARDS_DIR, `${safeName}.db`));
3099
3222
  }
3100
3223
  function listShards() {
3101
- if (!existsSync6(SHARDS_DIR)) return [];
3224
+ if (!existsSync8(SHARDS_DIR)) return [];
3102
3225
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
3103
3226
  }
3104
3227
  async function ensureShardSchema(client) {
@@ -3150,6 +3273,8 @@ async function ensureShardSchema(client) {
3150
3273
  for (const col of [
3151
3274
  "ALTER TABLE memories ADD COLUMN task_id TEXT",
3152
3275
  "ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
3276
+ "ALTER TABLE memories ADD COLUMN author_device_id TEXT",
3277
+ "ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
3153
3278
  "ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
3154
3279
  "ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
3155
3280
  "ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
@@ -3287,21 +3412,69 @@ async function getReadyShardClient(projectName) {
3287
3412
  await ensureShardSchema(client);
3288
3413
  return client;
3289
3414
  }
3415
+ function evictLRU() {
3416
+ let oldest = null;
3417
+ let oldestTime = Infinity;
3418
+ for (const [name, time] of _shardLastAccess) {
3419
+ if (time < oldestTime) {
3420
+ oldestTime = time;
3421
+ oldest = name;
3422
+ }
3423
+ }
3424
+ if (oldest) {
3425
+ const client = _shards.get(oldest);
3426
+ if (client) {
3427
+ client.close();
3428
+ }
3429
+ _shards.delete(oldest);
3430
+ _shardLastAccess.delete(oldest);
3431
+ }
3432
+ }
3433
+ function evictIdleShards() {
3434
+ const now = Date.now();
3435
+ const toEvict = [];
3436
+ for (const [name, lastAccess] of _shardLastAccess) {
3437
+ if (now - lastAccess > SHARD_IDLE_MS) {
3438
+ toEvict.push(name);
3439
+ }
3440
+ }
3441
+ for (const name of toEvict) {
3442
+ const client = _shards.get(name);
3443
+ if (client) {
3444
+ client.close();
3445
+ }
3446
+ _shards.delete(name);
3447
+ _shardLastAccess.delete(name);
3448
+ }
3449
+ }
3450
+ function getOpenShardCount() {
3451
+ return _shards.size;
3452
+ }
3290
3453
  function disposeShards() {
3454
+ if (_evictionTimer) {
3455
+ clearInterval(_evictionTimer);
3456
+ _evictionTimer = null;
3457
+ }
3291
3458
  for (const [, client] of _shards) {
3292
3459
  client.close();
3293
3460
  }
3294
3461
  _shards.clear();
3462
+ _shardLastAccess.clear();
3295
3463
  _shardingEnabled = false;
3296
3464
  _encryptionKey = null;
3297
3465
  }
3298
- var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
3466
+ var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
3299
3467
  var init_shard_manager = __esm({
3300
3468
  "src/lib/shard-manager.ts"() {
3301
3469
  "use strict";
3302
3470
  init_config();
3303
- SHARDS_DIR = path7.join(EXE_AI_DIR, "shards");
3471
+ SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
3472
+ SHARD_IDLE_MS = 5 * 60 * 1e3;
3473
+ MAX_OPEN_SHARDS = 10;
3474
+ EVICTION_INTERVAL_MS = 60 * 1e3;
3304
3475
  _shards = /* @__PURE__ */ new Map();
3476
+ _shardLastAccess = /* @__PURE__ */ new Map();
3477
+ _evictionTimer = null;
3305
3478
  _encryptionKey = null;
3306
3479
  _shardingEnabled = false;
3307
3480
  }
@@ -4163,8 +4336,8 @@ __export(reranker_exports, {
4163
4336
  rerankWithContext: () => rerankWithContext,
4164
4337
  rerankWithScores: () => rerankWithScores
4165
4338
  });
4166
- import path8 from "path";
4167
- import { existsSync as existsSync7 } from "fs";
4339
+ import path9 from "path";
4340
+ import { existsSync as existsSync9 } from "fs";
4168
4341
  function resetIdleTimer() {
4169
4342
  if (_idleTimer) clearTimeout(_idleTimer);
4170
4343
  _idleTimer = setTimeout(() => {
@@ -4175,18 +4348,18 @@ function resetIdleTimer() {
4175
4348
  }
4176
4349
  }
4177
4350
  function isRerankerAvailable() {
4178
- return existsSync7(path8.join(MODELS_DIR, RERANKER_MODEL_FILE));
4351
+ return existsSync9(path9.join(MODELS_DIR, RERANKER_MODEL_FILE));
4179
4352
  }
4180
4353
  function getRerankerModelPath() {
4181
- return path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
4354
+ return path9.join(MODELS_DIR, RERANKER_MODEL_FILE);
4182
4355
  }
4183
4356
  async function ensureLoaded() {
4184
4357
  if (_rerankerContext) {
4185
4358
  resetIdleTimer();
4186
4359
  return;
4187
4360
  }
4188
- const modelPath = path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
4189
- if (!existsSync7(modelPath)) {
4361
+ const modelPath = path9.join(MODELS_DIR, RERANKER_MODEL_FILE);
4362
+ if (!existsSync9(modelPath)) {
4190
4363
  throw new Error(
4191
4364
  `Reranker model not found at ${modelPath}. Run /exe-setup to download it.`
4192
4365
  );
@@ -4319,10 +4492,10 @@ async function disposeEmbedder() {
4319
4492
  async function embedDirect(text) {
4320
4493
  const llamaCpp = await import("node-llama-cpp");
4321
4494
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
4322
- const { existsSync: existsSync14 } = await import("fs");
4323
- const path18 = await import("path");
4324
- const modelPath = path18.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
4325
- if (!existsSync14(modelPath)) {
4495
+ const { existsSync: existsSync16 } = await import("fs");
4496
+ const path19 = await import("path");
4497
+ const modelPath = path19.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
4498
+ if (!existsSync16(modelPath)) {
4326
4499
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
4327
4500
  }
4328
4501
  const llama = await llamaCpp.getLlama();
@@ -4357,7 +4530,7 @@ __export(project_name_exports, {
4357
4530
  getProjectName: () => getProjectName
4358
4531
  });
4359
4532
  import { execSync as execSync2 } from "child_process";
4360
- import path9 from "path";
4533
+ import path10 from "path";
4361
4534
  function getProjectName(cwd) {
4362
4535
  const dir = cwd ?? process.cwd();
4363
4536
  if (_cached && _cachedCwd === dir) return _cached;
@@ -4370,7 +4543,7 @@ function getProjectName(cwd) {
4370
4543
  timeout: 2e3,
4371
4544
  stdio: ["pipe", "pipe", "pipe"]
4372
4545
  }).trim();
4373
- repoRoot = path9.dirname(gitCommonDir);
4546
+ repoRoot = path10.dirname(gitCommonDir);
4374
4547
  } catch {
4375
4548
  repoRoot = execSync2("git rev-parse --show-toplevel", {
4376
4549
  cwd: dir,
@@ -4379,11 +4552,11 @@ function getProjectName(cwd) {
4379
4552
  stdio: ["pipe", "pipe", "pipe"]
4380
4553
  }).trim();
4381
4554
  }
4382
- _cached = path9.basename(repoRoot);
4555
+ _cached = path10.basename(repoRoot);
4383
4556
  _cachedCwd = dir;
4384
4557
  return _cached;
4385
4558
  } catch {
4386
- _cached = path9.basename(dir);
4559
+ _cached = path10.basename(dir);
4387
4560
  _cachedCwd = dir;
4388
4561
  return _cached;
4389
4562
  }
@@ -4407,9 +4580,9 @@ __export(file_grep_exports, {
4407
4580
  grepProjectFiles: () => grepProjectFiles
4408
4581
  });
4409
4582
  import { execSync as execSync3 } from "child_process";
4410
- import { readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync8 } from "fs";
4411
- import path10 from "path";
4412
- import crypto from "crypto";
4583
+ import { readFileSync as readFileSync6, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync10 } from "fs";
4584
+ import path11 from "path";
4585
+ import crypto2 from "crypto";
4413
4586
  function hasRipgrep() {
4414
4587
  if (_hasRg === null) {
4415
4588
  try {
@@ -4442,13 +4615,13 @@ async function grepProjectFiles(query, projectRoot, options) {
4442
4615
  const chunkCtx = getChunkContext(hit.filePath, hit.lineNumber);
4443
4616
  const prefix = chunkCtx ? `[file: ${hit.filePath}:${hit.lineNumber} in ${chunkCtx}]` : `[file: ${hit.filePath}:${hit.lineNumber}]`;
4444
4617
  return {
4445
- id: crypto.createHash("sha256").update(`${hit.filePath}:${hit.lineNumber}`).digest("hex").slice(0, 36),
4618
+ id: crypto2.createHash("sha256").update(`${hit.filePath}:${hit.lineNumber}`).digest("hex").slice(0, 36),
4446
4619
  agent_id: "project",
4447
4620
  agent_role: "file",
4448
4621
  session_id: "file-grep",
4449
4622
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4450
4623
  tool_name: "file_grep",
4451
- project_name: path10.basename(projectRoot),
4624
+ project_name: path11.basename(projectRoot),
4452
4625
  has_error: false,
4453
4626
  raw_text: `${prefix} ${buildSnippet(hit, projectRoot)}`,
4454
4627
  vector: null,
@@ -4460,7 +4633,7 @@ function getChunkContext(filePath, lineNumber) {
4460
4633
  try {
4461
4634
  const ext = filePath.split(".").pop()?.toLowerCase();
4462
4635
  if (ext !== "ts" && ext !== "tsx" && ext !== "js" && ext !== "jsx") return "";
4463
- const source = readFileSync5(filePath, "utf8");
4636
+ const source = readFileSync6(filePath, "utf8");
4464
4637
  const lines = source.split("\n");
4465
4638
  for (let i = Math.min(lineNumber - 1, lines.length - 1); i >= 0; i--) {
4466
4639
  const line = lines[i];
@@ -4522,11 +4695,11 @@ function grepWithNodeFs(pattern, projectRoot, patterns) {
4522
4695
  const files = collectFiles(projectRoot, patterns ?? DEFAULT_PATTERNS);
4523
4696
  const hits = [];
4524
4697
  for (const filePath of files.slice(0, MAX_FILES)) {
4525
- const absPath = path10.join(projectRoot, filePath);
4698
+ const absPath = path11.join(projectRoot, filePath);
4526
4699
  try {
4527
4700
  const stat = statSync2(absPath);
4528
4701
  if (stat.size > MAX_FILE_SIZE) continue;
4529
- const content = readFileSync5(absPath, "utf8");
4702
+ const content = readFileSync6(absPath, "utf8");
4530
4703
  const lines = content.split("\n");
4531
4704
  const matches = content.match(regex);
4532
4705
  if (!matches || matches.length === 0) continue;
@@ -4549,15 +4722,15 @@ function collectFiles(root, patterns) {
4549
4722
  const files = [];
4550
4723
  function walk(dir, relative) {
4551
4724
  if (files.length >= MAX_FILES) return;
4552
- const basename = path10.basename(dir);
4725
+ const basename = path11.basename(dir);
4553
4726
  if (EXCLUDE_DIRS.includes(basename)) return;
4554
4727
  try {
4555
4728
  const entries = readdirSync2(dir, { withFileTypes: true });
4556
4729
  for (const entry of entries) {
4557
4730
  if (files.length >= MAX_FILES) return;
4558
- const rel = path10.join(relative, entry.name);
4731
+ const rel = path11.join(relative, entry.name);
4559
4732
  if (entry.isDirectory()) {
4560
- walk(path10.join(dir, entry.name), rel);
4733
+ walk(path11.join(dir, entry.name), rel);
4561
4734
  } else if (entry.isFile()) {
4562
4735
  for (const pat of patterns) {
4563
4736
  if (matchGlob(rel, pat)) {
@@ -4589,7 +4762,7 @@ function matchGlob(filePath, pattern) {
4589
4762
  if (slashIdx !== -1) {
4590
4763
  const dir = pattern.slice(0, slashIdx);
4591
4764
  const ext2 = pattern.slice(slashIdx + 1).replace("*", "");
4592
- const fileDir = path10.dirname(filePath);
4765
+ const fileDir = path11.dirname(filePath);
4593
4766
  return fileDir === dir && filePath.endsWith(ext2);
4594
4767
  }
4595
4768
  const ext = pattern.replace("*", "");
@@ -4597,9 +4770,9 @@ function matchGlob(filePath, pattern) {
4597
4770
  }
4598
4771
  function buildSnippet(hit, projectRoot) {
4599
4772
  try {
4600
- const absPath = path10.join(projectRoot, hit.filePath);
4601
- if (!existsSync8(absPath)) return hit.matchLine;
4602
- const lines = readFileSync5(absPath, "utf8").split("\n");
4773
+ const absPath = path11.join(projectRoot, hit.filePath);
4774
+ if (!existsSync10(absPath)) return hit.matchLine;
4775
+ const lines = readFileSync6(absPath, "utf8").split("\n");
4603
4776
  const start = Math.max(0, hit.lineNumber - 3);
4604
4777
  const end = Math.min(lines.length, hit.lineNumber + 2);
4605
4778
  return lines.slice(start, end).join("\n").slice(0, 500);
@@ -5815,9 +5988,9 @@ var init_session_key = __esm({
5815
5988
  });
5816
5989
 
5817
5990
  // src/lib/active-agent.ts
5818
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
5991
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
5819
5992
  import { execSync as execSync5 } from "child_process";
5820
- import path11 from "path";
5993
+ import path12 from "path";
5821
5994
  function isNameWithOptionalInstance(candidate, baseName) {
5822
5995
  if (candidate === baseName) return true;
5823
5996
  if (!candidate.startsWith(baseName)) return false;
@@ -5861,12 +6034,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
5861
6034
  return null;
5862
6035
  }
5863
6036
  function getMarkerPath() {
5864
- return path11.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
6037
+ return path12.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
5865
6038
  }
5866
6039
  function writeActiveAgent(agentId, agentRole) {
5867
6040
  try {
5868
6041
  mkdirSync3(CACHE_DIR, { recursive: true });
5869
- writeFileSync3(
6042
+ writeFileSync4(
5870
6043
  getMarkerPath(),
5871
6044
  JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
5872
6045
  );
@@ -5882,7 +6055,7 @@ function clearActiveAgent() {
5882
6055
  function getActiveAgent() {
5883
6056
  try {
5884
6057
  const markerPath = getMarkerPath();
5885
- const raw = readFileSync6(markerPath, "utf8");
6058
+ const raw = readFileSync7(markerPath, "utf8");
5886
6059
  const data = JSON.parse(raw);
5887
6060
  if (data.agentId) {
5888
6061
  if (data.startedAt) {
@@ -5930,14 +6103,14 @@ function getAllActiveAgents() {
5930
6103
  const key = file.slice("active-agent-".length, -".json".length);
5931
6104
  if (key === "undefined") continue;
5932
6105
  try {
5933
- const raw = readFileSync6(path11.join(CACHE_DIR, file), "utf8");
6106
+ const raw = readFileSync7(path12.join(CACHE_DIR, file), "utf8");
5934
6107
  const data = JSON.parse(raw);
5935
6108
  if (!data.agentId) continue;
5936
6109
  if (data.startedAt) {
5937
6110
  const age = Date.now() - new Date(data.startedAt).getTime();
5938
6111
  if (age > STALE_MS) {
5939
6112
  try {
5940
- unlinkSync3(path11.join(CACHE_DIR, file));
6113
+ unlinkSync3(path12.join(CACHE_DIR, file));
5941
6114
  } catch {
5942
6115
  }
5943
6116
  continue;
@@ -5960,11 +6133,11 @@ function getAllActiveAgents() {
5960
6133
  function cleanupSessionMarkers() {
5961
6134
  const key = getSessionKey();
5962
6135
  try {
5963
- unlinkSync3(path11.join(CACHE_DIR, `active-agent-${key}.json`));
6136
+ unlinkSync3(path12.join(CACHE_DIR, `active-agent-${key}.json`));
5964
6137
  } catch {
5965
6138
  }
5966
6139
  try {
5967
- unlinkSync3(path11.join(CACHE_DIR, "active-agent-undefined.json"));
6140
+ unlinkSync3(path12.join(CACHE_DIR, "active-agent-undefined.json"));
5968
6141
  } catch {
5969
6142
  }
5970
6143
  }
@@ -5975,7 +6148,7 @@ var init_active_agent = __esm({
5975
6148
  init_config();
5976
6149
  init_session_key();
5977
6150
  init_employees();
5978
- CACHE_DIR = path11.join(EXE_AI_DIR, "session-cache");
6151
+ CACHE_DIR = path12.join(EXE_AI_DIR, "session-cache");
5979
6152
  STALE_MS = 24 * 60 * 60 * 1e3;
5980
6153
  }
5981
6154
  });
@@ -5998,13 +6171,13 @@ var init_active_agent2 = __esm({
5998
6171
  });
5999
6172
 
6000
6173
  // src/lib/session-registry.ts
6001
- import path12 from "path";
6174
+ import path13 from "path";
6002
6175
  import os6 from "os";
6003
6176
  var REGISTRY_PATH;
6004
6177
  var init_session_registry = __esm({
6005
6178
  "src/lib/session-registry.ts"() {
6006
6179
  "use strict";
6007
- REGISTRY_PATH = path12.join(os6.homedir(), ".exe-os", "session-registry.json");
6180
+ REGISTRY_PATH = path13.join(os6.homedir(), ".exe-os", "session-registry.json");
6008
6181
  }
6009
6182
  });
6010
6183
 
@@ -6145,38 +6318,41 @@ var init_provider_table = __esm({
6145
6318
  });
6146
6319
 
6147
6320
  // src/lib/intercom-queue.ts
6148
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
6149
- import path13 from "path";
6321
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync11, mkdirSync as mkdirSync4 } from "fs";
6322
+ import path14 from "path";
6150
6323
  import os7 from "os";
6151
6324
  var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
6152
6325
  var init_intercom_queue = __esm({
6153
6326
  "src/lib/intercom-queue.ts"() {
6154
6327
  "use strict";
6155
- QUEUE_PATH = path13.join(os7.homedir(), ".exe-os", "intercom-queue.json");
6328
+ QUEUE_PATH = path14.join(os7.homedir(), ".exe-os", "intercom-queue.json");
6156
6329
  TTL_MS = 60 * 60 * 1e3;
6157
- INTERCOM_LOG = path13.join(os7.homedir(), ".exe-os", "intercom.log");
6330
+ INTERCOM_LOG = path14.join(os7.homedir(), ".exe-os", "intercom.log");
6158
6331
  }
6159
6332
  });
6160
6333
 
6161
6334
  // src/lib/license.ts
6162
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync10, mkdirSync as mkdirSync5 } from "fs";
6335
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, existsSync as existsSync12, mkdirSync as mkdirSync5 } from "fs";
6163
6336
  import { randomUUID as randomUUID3 } from "crypto";
6164
- import path14 from "path";
6337
+ import { createRequire as createRequire2 } from "module";
6338
+ import { pathToFileURL as pathToFileURL2 } from "url";
6339
+ import os8 from "os";
6340
+ import path15 from "path";
6165
6341
  import { jwtVerify, importSPKI } from "jose";
6166
6342
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
6167
6343
  var init_license = __esm({
6168
6344
  "src/lib/license.ts"() {
6169
6345
  "use strict";
6170
6346
  init_config();
6171
- LICENSE_PATH = path14.join(EXE_AI_DIR, "license.key");
6172
- CACHE_PATH = path14.join(EXE_AI_DIR, "license-cache.json");
6173
- DEVICE_ID_PATH = path14.join(EXE_AI_DIR, "device-id");
6347
+ LICENSE_PATH = path15.join(EXE_AI_DIR, "license.key");
6348
+ CACHE_PATH = path15.join(EXE_AI_DIR, "license-cache.json");
6349
+ DEVICE_ID_PATH = path15.join(EXE_AI_DIR, "device-id");
6174
6350
  }
6175
6351
  });
6176
6352
 
6177
6353
  // src/lib/plan-limits.ts
6178
- import { readFileSync as readFileSync9, existsSync as existsSync11 } from "fs";
6179
- import path15 from "path";
6354
+ import { readFileSync as readFileSync10, existsSync as existsSync13 } from "fs";
6355
+ import path16 from "path";
6180
6356
  var CACHE_PATH2;
6181
6357
  var init_plan_limits = __esm({
6182
6358
  "src/lib/plan-limits.ts"() {
@@ -6185,14 +6361,14 @@ var init_plan_limits = __esm({
6185
6361
  init_employees();
6186
6362
  init_license();
6187
6363
  init_config();
6188
- CACHE_PATH2 = path15.join(EXE_AI_DIR, "license-cache.json");
6364
+ CACHE_PATH2 = path16.join(EXE_AI_DIR, "license-cache.json");
6189
6365
  }
6190
6366
  });
6191
6367
 
6192
6368
  // src/lib/tmux-routing.ts
6193
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync4 } from "fs";
6194
- import path16 from "path";
6195
- import os8 from "os";
6369
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync4 } from "fs";
6370
+ import path17 from "path";
6371
+ import os9 from "os";
6196
6372
  import { fileURLToPath as fileURLToPath2 } from "url";
6197
6373
  function getMySession() {
6198
6374
  return getTransport().getMySession();
@@ -6205,7 +6381,7 @@ function extractRootExe(name) {
6205
6381
  }
6206
6382
  function getParentExe(sessionKey) {
6207
6383
  try {
6208
- const data = JSON.parse(readFileSync10(path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
6384
+ const data = JSON.parse(readFileSync11(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
6209
6385
  return data.parentExe || null;
6210
6386
  } catch {
6211
6387
  return null;
@@ -6248,10 +6424,10 @@ var init_tmux_routing = __esm({
6248
6424
  init_intercom_queue();
6249
6425
  init_plan_limits();
6250
6426
  init_employees();
6251
- SPAWN_LOCK_DIR = path16.join(os8.homedir(), ".exe-os", "spawn-locks");
6252
- SESSION_CACHE = path16.join(os8.homedir(), ".exe-os", "session-cache");
6253
- INTERCOM_LOG2 = path16.join(os8.homedir(), ".exe-os", "intercom.log");
6254
- DEBOUNCE_FILE = path16.join(SESSION_CACHE, "intercom-debounce.json");
6427
+ SPAWN_LOCK_DIR = path17.join(os9.homedir(), ".exe-os", "spawn-locks");
6428
+ SESSION_CACHE = path17.join(os9.homedir(), ".exe-os", "session-cache");
6429
+ INTERCOM_LOG2 = path17.join(os9.homedir(), ".exe-os", "intercom.log");
6430
+ DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
6255
6431
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
6256
6432
  }
6257
6433
  });
@@ -6260,7 +6436,8 @@ var init_tmux_routing = __esm({
6260
6436
  var task_scope_exports = {};
6261
6437
  __export(task_scope_exports, {
6262
6438
  getCurrentSessionScope: () => getCurrentSessionScope,
6263
- sessionScopeFilter: () => sessionScopeFilter
6439
+ sessionScopeFilter: () => sessionScopeFilter,
6440
+ strictSessionScopeFilter: () => strictSessionScopeFilter
6264
6441
  });
6265
6442
  function getCurrentSessionScope() {
6266
6443
  try {
@@ -6278,6 +6455,15 @@ function sessionScopeFilter(sessionScope, tableAlias) {
6278
6455
  args: [scope]
6279
6456
  };
6280
6457
  }
6458
+ function strictSessionScopeFilter(sessionScope, tableAlias) {
6459
+ const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
6460
+ if (!scope) return { sql: "", args: [] };
6461
+ const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
6462
+ return {
6463
+ sql: ` AND ${col} = ?`,
6464
+ args: [scope]
6465
+ };
6466
+ }
6281
6467
  var init_task_scope = __esm({
6282
6468
  "src/lib/task-scope.ts"() {
6283
6469
  "use strict";
@@ -6287,8 +6473,8 @@ var init_task_scope = __esm({
6287
6473
 
6288
6474
  // src/adapters/claude/hooks/session-start.ts
6289
6475
  init_config();
6290
- import path17 from "path";
6291
- import { unlinkSync as unlinkSync4, existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
6476
+ import path18 from "path";
6477
+ import { unlinkSync as unlinkSync4, existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
6292
6478
  if (!process.env.AGENT_ID) {
6293
6479
  process.env.AGENT_ID = "default";
6294
6480
  process.env.AGENT_ROLE = "employee";
@@ -6321,8 +6507,8 @@ process.stdin.on("end", async () => {
6321
6507
  const source = data.source ?? "startup";
6322
6508
  if (source === "startup") {
6323
6509
  try {
6324
- const undefinedPath = path17.join(
6325
- process.env.EXE_OS_DIR ?? path17.join(process.env.HOME ?? "", ".exe-os"),
6510
+ const undefinedPath = path18.join(
6511
+ process.env.EXE_OS_DIR ?? path18.join(process.env.HOME ?? "", ".exe-os"),
6326
6512
  "session-cache",
6327
6513
  "active-agent-undefined.json"
6328
6514
  );
@@ -6335,13 +6521,13 @@ process.stdin.on("end", async () => {
6335
6521
  const agentId = agent.agentId;
6336
6522
  if (agentId !== "default") {
6337
6523
  try {
6338
- const cacheDir = path17.join(
6339
- process.env.EXE_OS_DIR ?? path17.join(process.env.HOME ?? "", ".exe-os"),
6524
+ const cacheDir = path18.join(
6525
+ process.env.EXE_OS_DIR ?? path18.join(process.env.HOME ?? "", ".exe-os"),
6340
6526
  "session-cache"
6341
6527
  );
6342
- const markerPath = path17.join(cacheDir, `current-task-${agentId}.json`);
6343
- if (existsSync13(markerPath)) {
6344
- const marker = JSON.parse(readFileSync11(markerPath, "utf-8"));
6528
+ const markerPath = path18.join(cacheDir, `current-task-${agentId}.json`);
6529
+ if (existsSync15(markerPath)) {
6530
+ const marker = JSON.parse(readFileSync12(markerPath, "utf-8"));
6345
6531
  if (marker.taskId) {
6346
6532
  const client = getClient2();
6347
6533
  const result = await client.execute({