@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
@@ -70,9 +70,47 @@ var init_db_retry = __esm({
70
70
  }
71
71
  });
72
72
 
73
+ // src/lib/secure-files.ts
74
+ import { chmodSync, existsSync, mkdirSync } from "fs";
75
+ import { chmod, mkdir } from "fs/promises";
76
+ async function ensurePrivateDir(dirPath) {
77
+ await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
78
+ try {
79
+ await chmod(dirPath, PRIVATE_DIR_MODE);
80
+ } catch {
81
+ }
82
+ }
83
+ function ensurePrivateDirSync(dirPath) {
84
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
85
+ try {
86
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
87
+ } catch {
88
+ }
89
+ }
90
+ async function enforcePrivateFile(filePath) {
91
+ try {
92
+ await chmod(filePath, PRIVATE_FILE_MODE);
93
+ } catch {
94
+ }
95
+ }
96
+ function enforcePrivateFileSync(filePath) {
97
+ try {
98
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
99
+ } catch {
100
+ }
101
+ }
102
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
103
+ var init_secure_files = __esm({
104
+ "src/lib/secure-files.ts"() {
105
+ "use strict";
106
+ PRIVATE_DIR_MODE = 448;
107
+ PRIVATE_FILE_MODE = 384;
108
+ }
109
+ });
110
+
73
111
  // src/lib/config.ts
74
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
75
- import { readFileSync, existsSync, renameSync } from "fs";
112
+ import { readFile, writeFile } from "fs/promises";
113
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
76
114
  import path from "path";
77
115
  import os from "os";
78
116
  function resolveDataDir() {
@@ -80,7 +118,7 @@ function resolveDataDir() {
80
118
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
81
119
  const newDir = path.join(os.homedir(), ".exe-os");
82
120
  const legacyDir = path.join(os.homedir(), ".exe-mem");
83
- if (!existsSync(newDir) && existsSync(legacyDir)) {
121
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
84
122
  try {
85
123
  renameSync(legacyDir, newDir);
86
124
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -143,9 +181,9 @@ function normalizeAutoUpdate(raw) {
143
181
  }
144
182
  async function loadConfig() {
145
183
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
146
- await mkdir(dir, { recursive: true });
184
+ await ensurePrivateDir(dir);
147
185
  const configPath = path.join(dir, "config.json");
148
- if (!existsSync(configPath)) {
186
+ if (!existsSync2(configPath)) {
149
187
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
150
188
  }
151
189
  const raw = await readFile(configPath, "utf-8");
@@ -158,6 +196,7 @@ async function loadConfig() {
158
196
  `);
159
197
  try {
160
198
  await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
199
+ await enforcePrivateFile(configPath);
161
200
  } catch {
162
201
  }
163
202
  }
@@ -177,6 +216,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
177
216
  var init_config = __esm({
178
217
  "src/lib/config.ts"() {
179
218
  "use strict";
219
+ init_secure_files();
180
220
  EXE_AI_DIR = resolveDataDir();
181
221
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
182
222
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -293,10 +333,10 @@ __export(agent_config_exports, {
293
333
  saveAgentConfig: () => saveAgentConfig,
294
334
  setAgentRuntime: () => setAgentRuntime
295
335
  });
296
- import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
336
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
297
337
  import path2 from "path";
298
338
  function loadAgentConfig() {
299
- if (!existsSync2(AGENT_CONFIG_PATH)) return {};
339
+ if (!existsSync3(AGENT_CONFIG_PATH)) return {};
300
340
  try {
301
341
  return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
302
342
  } catch {
@@ -305,8 +345,9 @@ function loadAgentConfig() {
305
345
  }
306
346
  function saveAgentConfig(config) {
307
347
  const dir = path2.dirname(AGENT_CONFIG_PATH);
308
- if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
348
+ ensurePrivateDirSync(dir);
309
349
  writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
350
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
310
351
  }
311
352
  function getAgentRuntime(agentId) {
312
353
  const config = loadAgentConfig();
@@ -346,6 +387,7 @@ var init_agent_config = __esm({
346
387
  "use strict";
347
388
  init_config();
348
389
  init_runtime_table();
390
+ init_secure_files();
349
391
  AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
350
392
  KNOWN_RUNTIMES = {
351
393
  claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
@@ -393,7 +435,7 @@ __export(employees_exports, {
393
435
  validateEmployeeName: () => validateEmployeeName
394
436
  });
395
437
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
396
- import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
438
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
397
439
  import { execSync } from "child_process";
398
440
  import path3 from "path";
399
441
  import os2 from "os";
@@ -432,7 +474,7 @@ function validateEmployeeName(name) {
432
474
  return { valid: true };
433
475
  }
434
476
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
435
- if (!existsSync3(employeesPath)) {
477
+ if (!existsSync4(employeesPath)) {
436
478
  return [];
437
479
  }
438
480
  const raw = await readFile2(employeesPath, "utf-8");
@@ -447,7 +489,7 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
447
489
  await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
448
490
  }
449
491
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
450
- if (!existsSync3(employeesPath)) return [];
492
+ if (!existsSync4(employeesPath)) return [];
451
493
  try {
452
494
  return JSON.parse(readFileSync3(employeesPath, "utf-8"));
453
495
  } catch {
@@ -495,7 +537,7 @@ function appendToCoordinatorTeam(employee) {
495
537
  const coordinator = getCoordinatorEmployee(loadEmployeesSync());
496
538
  if (!coordinator) return;
497
539
  const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
498
- if (!existsSync3(idPath)) return;
540
+ if (!existsSync4(idPath)) return;
499
541
  const content = readFileSync3(idPath, "utf-8");
500
542
  if (content.includes(`**${capitalize(employee.name)}`)) return;
501
543
  const teamMatch = content.match(TEAM_SECTION_RE);
@@ -549,9 +591,9 @@ async function normalizeRosterCase(rosterPath) {
549
591
  const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
550
592
  const oldPath = path3.join(identityDir, `${oldName}.md`);
551
593
  const newPath = path3.join(identityDir, `${emp.name}.md`);
552
- if (existsSync3(oldPath) && !existsSync3(newPath)) {
594
+ if (existsSync4(oldPath) && !existsSync4(newPath)) {
553
595
  renameSync2(oldPath, newPath);
554
- } else if (existsSync3(oldPath) && oldPath !== newPath) {
596
+ } else if (existsSync4(oldPath) && oldPath !== newPath) {
555
597
  const content = readFileSync3(oldPath, "utf-8");
556
598
  writeFileSync2(newPath, content, "utf-8");
557
599
  if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
@@ -594,7 +636,7 @@ function registerBinSymlinks(name) {
594
636
  for (const suffix of ["", "-opencode"]) {
595
637
  const linkName = `${name}${suffix}`;
596
638
  const linkPath = path3.join(binDir, linkName);
597
- if (existsSync3(linkPath)) {
639
+ if (existsSync4(linkPath)) {
598
640
  skipped.push(linkName);
599
641
  continue;
600
642
  }
@@ -1547,6 +1589,7 @@ async function ensureSchema() {
1547
1589
  project TEXT NOT NULL,
1548
1590
  summary TEXT NOT NULL,
1549
1591
  task_file TEXT,
1592
+ session_scope TEXT,
1550
1593
  read INTEGER NOT NULL DEFAULT 0,
1551
1594
  created_at TEXT NOT NULL
1552
1595
  );
@@ -1555,7 +1598,7 @@ async function ensureSchema() {
1555
1598
  ON notifications(read);
1556
1599
 
1557
1600
  CREATE INDEX IF NOT EXISTS idx_notifications_agent
1558
- ON notifications(agent_id);
1601
+ ON notifications(agent_id, session_scope);
1559
1602
 
1560
1603
  CREATE INDEX IF NOT EXISTS idx_notifications_task_file
1561
1604
  ON notifications(task_file);
@@ -1593,6 +1636,7 @@ async function ensureSchema() {
1593
1636
  target_agent TEXT NOT NULL,
1594
1637
  target_project TEXT,
1595
1638
  target_device TEXT NOT NULL DEFAULT 'local',
1639
+ session_scope TEXT,
1596
1640
  content TEXT NOT NULL,
1597
1641
  priority TEXT DEFAULT 'normal',
1598
1642
  status TEXT DEFAULT 'pending',
@@ -1606,10 +1650,31 @@ async function ensureSchema() {
1606
1650
  );
1607
1651
 
1608
1652
  CREATE INDEX IF NOT EXISTS idx_messages_target
1609
- ON messages(target_agent, status);
1653
+ ON messages(target_agent, session_scope, status);
1610
1654
 
1611
1655
  CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
1612
- ON messages(target_agent, from_agent, server_seq);
1656
+ ON messages(target_agent, session_scope, from_agent, server_seq);
1657
+ `);
1658
+ try {
1659
+ await client.execute({
1660
+ sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
1661
+ args: []
1662
+ });
1663
+ } catch {
1664
+ }
1665
+ try {
1666
+ await client.execute({
1667
+ sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
1668
+ args: []
1669
+ });
1670
+ } catch {
1671
+ }
1672
+ await client.executeMultiple(`
1673
+ CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
1674
+ ON notifications(agent_id, session_scope, read, created_at);
1675
+
1676
+ CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
1677
+ ON messages(target_agent, session_scope, status, created_at);
1613
1678
  `);
1614
1679
  try {
1615
1680
  await client.execute({
@@ -2193,6 +2258,13 @@ async function ensureSchema() {
2193
2258
  } catch {
2194
2259
  }
2195
2260
  }
2261
+ try {
2262
+ await client.execute({
2263
+ sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
2264
+ args: []
2265
+ });
2266
+ } catch {
2267
+ }
2196
2268
  }
2197
2269
  async function disposeDatabase() {
2198
2270
  if (_walCheckpointTimer) {
@@ -2235,6 +2307,7 @@ var shard_manager_exports = {};
2235
2307
  __export(shard_manager_exports, {
2236
2308
  disposeShards: () => disposeShards,
2237
2309
  ensureShardSchema: () => ensureShardSchema,
2310
+ getOpenShardCount: () => getOpenShardCount,
2238
2311
  getReadyShardClient: () => getReadyShardClient,
2239
2312
  getShardClient: () => getShardClient,
2240
2313
  getShardsDir: () => getShardsDir,
@@ -2244,14 +2317,17 @@ __export(shard_manager_exports, {
2244
2317
  shardExists: () => shardExists
2245
2318
  });
2246
2319
  import path6 from "path";
2247
- import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
2320
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync } from "fs";
2248
2321
  import { createClient as createClient2 } from "@libsql/client";
2249
2322
  function initShardManager(encryptionKey) {
2250
2323
  _encryptionKey = encryptionKey;
2251
- if (!existsSync5(SHARDS_DIR)) {
2324
+ if (!existsSync6(SHARDS_DIR)) {
2252
2325
  mkdirSync2(SHARDS_DIR, { recursive: true });
2253
2326
  }
2254
2327
  _shardingEnabled = true;
2328
+ if (_evictionTimer) clearInterval(_evictionTimer);
2329
+ _evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
2330
+ _evictionTimer.unref();
2255
2331
  }
2256
2332
  function isShardingEnabled() {
2257
2333
  return _shardingEnabled;
@@ -2268,21 +2344,28 @@ function getShardClient(projectName) {
2268
2344
  throw new Error(`Invalid project name for shard: "${projectName}"`);
2269
2345
  }
2270
2346
  const cached = _shards.get(safeName);
2271
- if (cached) return cached;
2347
+ if (cached) {
2348
+ _shardLastAccess.set(safeName, Date.now());
2349
+ return cached;
2350
+ }
2351
+ while (_shards.size >= MAX_OPEN_SHARDS) {
2352
+ evictLRU();
2353
+ }
2272
2354
  const dbPath = path6.join(SHARDS_DIR, `${safeName}.db`);
2273
2355
  const client = createClient2({
2274
2356
  url: `file:${dbPath}`,
2275
2357
  encryptionKey: _encryptionKey
2276
2358
  });
2277
2359
  _shards.set(safeName, client);
2360
+ _shardLastAccess.set(safeName, Date.now());
2278
2361
  return client;
2279
2362
  }
2280
2363
  function shardExists(projectName) {
2281
2364
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
2282
- return existsSync5(path6.join(SHARDS_DIR, `${safeName}.db`));
2365
+ return existsSync6(path6.join(SHARDS_DIR, `${safeName}.db`));
2283
2366
  }
2284
2367
  function listShards() {
2285
- if (!existsSync5(SHARDS_DIR)) return [];
2368
+ if (!existsSync6(SHARDS_DIR)) return [];
2286
2369
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
2287
2370
  }
2288
2371
  async function ensureShardSchema(client) {
@@ -2334,6 +2417,8 @@ async function ensureShardSchema(client) {
2334
2417
  for (const col of [
2335
2418
  "ALTER TABLE memories ADD COLUMN task_id TEXT",
2336
2419
  "ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
2420
+ "ALTER TABLE memories ADD COLUMN author_device_id TEXT",
2421
+ "ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
2337
2422
  "ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
2338
2423
  "ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
2339
2424
  "ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
@@ -2471,21 +2556,69 @@ async function getReadyShardClient(projectName) {
2471
2556
  await ensureShardSchema(client);
2472
2557
  return client;
2473
2558
  }
2559
+ function evictLRU() {
2560
+ let oldest = null;
2561
+ let oldestTime = Infinity;
2562
+ for (const [name, time] of _shardLastAccess) {
2563
+ if (time < oldestTime) {
2564
+ oldestTime = time;
2565
+ oldest = name;
2566
+ }
2567
+ }
2568
+ if (oldest) {
2569
+ const client = _shards.get(oldest);
2570
+ if (client) {
2571
+ client.close();
2572
+ }
2573
+ _shards.delete(oldest);
2574
+ _shardLastAccess.delete(oldest);
2575
+ }
2576
+ }
2577
+ function evictIdleShards() {
2578
+ const now = Date.now();
2579
+ const toEvict = [];
2580
+ for (const [name, lastAccess] of _shardLastAccess) {
2581
+ if (now - lastAccess > SHARD_IDLE_MS) {
2582
+ toEvict.push(name);
2583
+ }
2584
+ }
2585
+ for (const name of toEvict) {
2586
+ const client = _shards.get(name);
2587
+ if (client) {
2588
+ client.close();
2589
+ }
2590
+ _shards.delete(name);
2591
+ _shardLastAccess.delete(name);
2592
+ }
2593
+ }
2594
+ function getOpenShardCount() {
2595
+ return _shards.size;
2596
+ }
2474
2597
  function disposeShards() {
2598
+ if (_evictionTimer) {
2599
+ clearInterval(_evictionTimer);
2600
+ _evictionTimer = null;
2601
+ }
2475
2602
  for (const [, client] of _shards) {
2476
2603
  client.close();
2477
2604
  }
2478
2605
  _shards.clear();
2606
+ _shardLastAccess.clear();
2479
2607
  _shardingEnabled = false;
2480
2608
  _encryptionKey = null;
2481
2609
  }
2482
- var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
2610
+ var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
2483
2611
  var init_shard_manager = __esm({
2484
2612
  "src/lib/shard-manager.ts"() {
2485
2613
  "use strict";
2486
2614
  init_config();
2487
2615
  SHARDS_DIR = path6.join(EXE_AI_DIR, "shards");
2616
+ SHARD_IDLE_MS = 5 * 60 * 1e3;
2617
+ MAX_OPEN_SHARDS = 10;
2618
+ EVICTION_INTERVAL_MS = 60 * 1e3;
2488
2619
  _shards = /* @__PURE__ */ new Map();
2620
+ _shardLastAccess = /* @__PURE__ */ new Map();
2621
+ _evictionTimer = null;
2489
2622
  _encryptionKey = null;
2490
2623
  _shardingEnabled = false;
2491
2624
  }
@@ -3541,7 +3674,7 @@ var init_active_agent = __esm({
3541
3674
  // src/bin/exe-launch-agent.ts
3542
3675
  import os6 from "os";
3543
3676
  import path9 from "path";
3544
- import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync4 } from "fs";
3677
+ import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync4 } from "fs";
3545
3678
  import { spawnSync } from "child_process";
3546
3679
 
3547
3680
  // src/lib/store.ts
@@ -3550,7 +3683,7 @@ init_database();
3550
3683
 
3551
3684
  // src/lib/keychain.ts
3552
3685
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3553
- import { existsSync as existsSync4 } from "fs";
3686
+ import { existsSync as existsSync5 } from "fs";
3554
3687
  import path5 from "path";
3555
3688
  import os4 from "os";
3556
3689
  var SERVICE = "exe-mem";
@@ -3580,7 +3713,7 @@ async function getMasterKey() {
3580
3713
  }
3581
3714
  }
3582
3715
  const keyPath = getKeyPath();
3583
- if (!existsSync4(keyPath)) {
3716
+ if (!existsSync5(keyPath)) {
3584
3717
  process.stderr.write(
3585
3718
  `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
3586
3719
  `
@@ -3896,7 +4029,7 @@ function vectorToBlob(vector) {
3896
4029
  import os5 from "os";
3897
4030
  import path7 from "path";
3898
4031
  import {
3899
- existsSync as existsSync6,
4032
+ existsSync as existsSync7,
3900
4033
  mkdirSync as mkdirSync3,
3901
4034
  readdirSync as readdirSync2,
3902
4035
  statSync,
@@ -3944,7 +4077,7 @@ var BEHAVIORS_EXPORT_DIR = path7.join(
3944
4077
  var STALE_EXPORT_AGE_MS = 60 * 60 * 1e3;
3945
4078
  var EXPORT_BEHAVIOR_LIMIT = 30;
3946
4079
  function sweepStaleBehaviorExports(now = Date.now()) {
3947
- if (!existsSync6(BEHAVIORS_EXPORT_DIR)) return;
4080
+ if (!existsSync7(BEHAVIORS_EXPORT_DIR)) return;
3948
4081
  let entries;
3949
4082
  try {
3950
4083
  entries = readdirSync2(BEHAVIORS_EXPORT_DIR);
@@ -4090,7 +4223,7 @@ async function isKnownAgent(agent) {
4090
4223
  function identityPathFor(agent) {
4091
4224
  const dir = path9.join(os6.homedir(), ".exe-os", "identity");
4092
4225
  const exactPath = path9.join(dir, `${agent}.md`);
4093
- if (existsSync7(exactPath)) return exactPath;
4226
+ if (existsSync8(exactPath)) return exactPath;
4094
4227
  try {
4095
4228
  const files = readdirSync4(dir);
4096
4229
  const match = files.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
@@ -4101,7 +4234,7 @@ function identityPathFor(agent) {
4101
4234
  }
4102
4235
  function leanMcpConfigFor(agent) {
4103
4236
  const p = path9.join(os6.homedir(), ".exe-os", "mcp-configs", `${agent}-lean.json`);
4104
- return existsSync7(p) ? p : null;
4237
+ return existsSync8(p) ? p : null;
4105
4238
  }
4106
4239
  var _ccHelpOutput = null;
4107
4240
  function getCcHelpOutput() {
@@ -4126,7 +4259,7 @@ function buildLaunchPlan(agent, behaviorsPath, passthrough, _hasAgentFlag, _prov
4126
4259
  const idPath = identityPathFor(agent);
4127
4260
  const ccAgentPath = path9.join(os6.homedir(), ".claude", "agents", `${agent}.md`);
4128
4261
  let effectiveCcPath = null;
4129
- if (existsSync7(ccAgentPath)) {
4262
+ if (existsSync8(ccAgentPath)) {
4130
4263
  effectiveCcPath = ccAgentPath;
4131
4264
  } else {
4132
4265
  try {
@@ -4137,9 +4270,9 @@ function buildLaunchPlan(agent, behaviorsPath, passthrough, _hasAgentFlag, _prov
4137
4270
  } catch {
4138
4271
  }
4139
4272
  }
4140
- const effectiveIdPath = existsSync7(idPath) ? idPath : effectiveCcPath;
4273
+ const effectiveIdPath = existsSync8(idPath) ? idPath : effectiveCcPath;
4141
4274
  let identityContent = null;
4142
- if (effectiveIdPath && existsSync7(effectiveIdPath)) {
4275
+ if (effectiveIdPath && existsSync8(effectiveIdPath)) {
4143
4276
  try {
4144
4277
  const content = readFileSync5(effectiveIdPath, "utf-8");
4145
4278
  if (content.trim().length > 0) identityContent = content;
@@ -4149,14 +4282,14 @@ function buildLaunchPlan(agent, behaviorsPath, passthrough, _hasAgentFlag, _prov
4149
4282
  if (!identityContent) {
4150
4283
  try {
4151
4284
  const rosterPath = path9.join(os6.homedir(), ".exe-os", "exe-employees.json");
4152
- if (existsSync7(rosterPath)) {
4285
+ if (existsSync8(rosterPath)) {
4153
4286
  const roster = JSON.parse(readFileSync5(rosterPath, "utf8"));
4154
4287
  const emp = roster.find((e) => e.name.toLowerCase() === agent.toLowerCase());
4155
4288
  if (emp?.systemPrompt && emp.systemPrompt.trim().length > 20) {
4156
4289
  identityContent = emp.systemPrompt;
4157
4290
  try {
4158
4291
  const dir = path9.dirname(idPath);
4159
- if (!existsSync7(dir)) mkdirSync5(dir, { recursive: true });
4292
+ if (!existsSync8(dir)) mkdirSync5(dir, { recursive: true });
4160
4293
  const hasFrontmatter = identityContent.trimStart().startsWith("---");
4161
4294
  const fileContent = hasFrontmatter ? identityContent : `---
4162
4295
  role: ${(emp.role ?? "employee").toLowerCase()}
@@ -4204,7 +4337,7 @@ ${identityContent}`;
4204
4337
  }
4205
4338
  }
4206
4339
  }
4207
- if (behaviorsPath && existsSync7(behaviorsPath)) {
4340
+ if (behaviorsPath && existsSync8(behaviorsPath)) {
4208
4341
  args.push("--append-system-prompt-file", behaviorsPath);
4209
4342
  }
4210
4343
  const leanMcp = leanMcpConfigFor(agent);
@@ -4327,10 +4460,10 @@ async function main() {
4327
4460
  if (hasAgentFlag) {
4328
4461
  const ccAgentDir = path9.join(os6.homedir(), ".claude", "agents");
4329
4462
  const ccAgentFile = path9.join(ccAgentDir, `${agent}.md`);
4330
- if (!existsSync7(ccAgentFile)) {
4463
+ if (!existsSync8(ccAgentFile)) {
4331
4464
  const exeIdentity = identityPathFor(agent);
4332
4465
  let sourceFile = null;
4333
- if (existsSync7(exeIdentity)) {
4466
+ if (existsSync8(exeIdentity)) {
4334
4467
  sourceFile = exeIdentity;
4335
4468
  } else {
4336
4469
  try {