@askexenow/exe-os 0.8.38 → 0.8.39

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 (91) hide show
  1. package/README.md +17 -8
  2. package/dist/bin/backfill-conversations.js +46 -10
  3. package/dist/bin/backfill-responses.js +46 -10
  4. package/dist/bin/backfill-vectors.js +42 -8
  5. package/dist/bin/cleanup-stale-review-tasks.js +37 -8
  6. package/dist/bin/cli.js +281 -154
  7. package/dist/bin/exe-agent.js +19 -4
  8. package/dist/bin/exe-assign.js +39 -5
  9. package/dist/bin/exe-boot.js +237 -111
  10. package/dist/bin/exe-call.js +11 -6
  11. package/dist/bin/exe-cloud.js +99 -28
  12. package/dist/bin/exe-dispatch.js +1 -1
  13. package/dist/bin/exe-doctor.js +37 -8
  14. package/dist/bin/exe-export-behaviors.js +39 -10
  15. package/dist/bin/exe-forget.js +38 -9
  16. package/dist/bin/exe-gateway.js +109 -42
  17. package/dist/bin/exe-heartbeat.js +49 -20
  18. package/dist/bin/exe-kill.js +39 -10
  19. package/dist/bin/exe-launch-agent.js +58 -22
  20. package/dist/bin/exe-link.js +184 -85
  21. package/dist/bin/exe-new-employee.js +21 -7
  22. package/dist/bin/exe-pending-messages.js +46 -17
  23. package/dist/bin/exe-pending-notifications.js +37 -8
  24. package/dist/bin/exe-pending-reviews.js +47 -18
  25. package/dist/bin/exe-rename.js +21 -7
  26. package/dist/bin/exe-review.js +34 -5
  27. package/dist/bin/exe-search.js +47 -10
  28. package/dist/bin/exe-session-cleanup.js +56 -19
  29. package/dist/bin/exe-settings.js +63 -2
  30. package/dist/bin/exe-status.js +34 -5
  31. package/dist/bin/exe-team.js +34 -5
  32. package/dist/bin/git-sweep.js +38 -9
  33. package/dist/bin/graph-backfill.js +37 -8
  34. package/dist/bin/graph-export.js +37 -8
  35. package/dist/bin/install.js +1 -1
  36. package/dist/bin/scan-tasks.js +40 -11
  37. package/dist/bin/setup.js +58 -24
  38. package/dist/bin/shard-migrate.js +37 -8
  39. package/dist/bin/wiki-sync.js +39 -9
  40. package/dist/gateway/index.js +102 -37
  41. package/dist/hooks/bug-report-worker.js +62 -28
  42. package/dist/hooks/commit-complete.js +38 -9
  43. package/dist/hooks/error-recall.js +49 -8
  44. package/dist/hooks/exe-heartbeat-hook.js +3 -2
  45. package/dist/hooks/ingest-worker.js +151 -37
  46. package/dist/hooks/ingest.js +74 -28
  47. package/dist/hooks/instructions-loaded.js +39 -9
  48. package/dist/hooks/notification.js +37 -7
  49. package/dist/hooks/post-compact.js +37 -7
  50. package/dist/hooks/pre-compact.js +35 -6
  51. package/dist/hooks/pre-tool-use.js +52 -14
  52. package/dist/hooks/prompt-ingest-worker.js +56 -10
  53. package/dist/hooks/prompt-submit.js +61 -23
  54. package/dist/hooks/response-ingest-worker.js +57 -11
  55. package/dist/hooks/session-end.js +43 -10
  56. package/dist/hooks/session-start.js +46 -8
  57. package/dist/hooks/stop.js +37 -7
  58. package/dist/hooks/subagent-stop.js +37 -7
  59. package/dist/hooks/summary-worker.js +317 -99
  60. package/dist/index.js +87 -22
  61. package/dist/lib/cloud-sync.js +172 -78
  62. package/dist/lib/config.js +4 -1
  63. package/dist/lib/consolidation.js +5 -4
  64. package/dist/lib/database.js +1 -0
  65. package/dist/lib/device-registry.js +2 -1
  66. package/dist/lib/embedder.js +9 -1
  67. package/dist/lib/employees.js +11 -6
  68. package/dist/lib/exe-daemon-client.js +6 -1
  69. package/dist/lib/exe-daemon.js +71 -28
  70. package/dist/lib/hybrid-search.js +47 -10
  71. package/dist/lib/identity.js +1 -1
  72. package/dist/lib/keychain.js +2 -1
  73. package/dist/lib/license.js +13 -4
  74. package/dist/lib/messaging.js +1 -1
  75. package/dist/lib/reminders.js +2 -2
  76. package/dist/lib/schedules.js +37 -8
  77. package/dist/lib/skill-learning.js +1 -1
  78. package/dist/lib/store.js +37 -8
  79. package/dist/lib/tasks.js +1 -1
  80. package/dist/lib/tmux-routing.js +1 -1
  81. package/dist/mcp/server.js +97 -43
  82. package/dist/mcp/tools/complete-reminder.js +1 -1
  83. package/dist/mcp/tools/create-task.js +14 -6
  84. package/dist/mcp/tools/deactivate-behavior.js +2 -2
  85. package/dist/mcp/tools/list-reminders.js +1 -1
  86. package/dist/mcp/tools/list-tasks.js +1 -1
  87. package/dist/mcp/tools/send-message.js +1 -1
  88. package/dist/mcp/tools/update-task.js +1 -1
  89. package/dist/runtime/index.js +35 -6
  90. package/dist/tui/App.js +177 -95
  91. package/package.json +3 -3
@@ -10,15 +10,15 @@ var __export = (target, all) => {
10
10
  };
11
11
 
12
12
  // src/lib/config.ts
13
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
13
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
14
14
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
15
15
  import path2 from "path";
16
- import os from "os";
16
+ import os2 from "os";
17
17
  function resolveDataDir() {
18
18
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
19
19
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
20
- const newDir = path2.join(os.homedir(), ".exe-os");
21
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
20
+ const newDir = path2.join(os2.homedir(), ".exe-os");
21
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
22
22
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
23
23
  try {
24
24
  renameSync(legacyDir, newDir);
@@ -105,7 +105,7 @@ async function loadConfig() {
105
105
  normalizeAutoUpdate(migratedCfg);
106
106
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
107
107
  if (config.dbPath.startsWith("~")) {
108
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
108
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
109
109
  }
110
110
  return config;
111
111
  } catch {
@@ -529,6 +529,7 @@ async function ensureSchema() {
529
529
  const client = getRawClient();
530
530
  await client.execute("PRAGMA journal_mode = WAL");
531
531
  await client.execute("PRAGMA busy_timeout = 30000");
532
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
532
533
  try {
533
534
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
534
535
  } catch {
@@ -1330,11 +1331,12 @@ async function disposeDatabase() {
1330
1331
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1331
1332
  import { existsSync } from "fs";
1332
1333
  import path from "path";
1334
+ import os from "os";
1333
1335
  import crypto from "crypto";
1334
1336
  var SERVICE = "exe-mem";
1335
1337
  var ACCOUNT = "master-key";
1336
1338
  function getKeyDir() {
1337
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
1339
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
1338
1340
  }
1339
1341
  function getKeyPath() {
1340
1342
  return path.join(getKeyDir(), "master.key");
@@ -1371,6 +1373,30 @@ async function getMasterKey() {
1371
1373
 
1372
1374
  // src/lib/store.ts
1373
1375
  init_config();
1376
+ var INIT_MAX_RETRIES = 3;
1377
+ var INIT_RETRY_DELAY_MS = 1e3;
1378
+ function isBusyError2(err) {
1379
+ if (err instanceof Error) {
1380
+ const msg = err.message.toLowerCase();
1381
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1382
+ }
1383
+ return false;
1384
+ }
1385
+ async function retryOnBusy2(fn, label) {
1386
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1387
+ try {
1388
+ return await fn();
1389
+ } catch (err) {
1390
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1391
+ process.stderr.write(
1392
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1393
+ `
1394
+ );
1395
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1396
+ }
1397
+ }
1398
+ throw new Error("unreachable");
1399
+ }
1374
1400
  var _pendingRecords = [];
1375
1401
  var _batchSize = 20;
1376
1402
  var _flushIntervalMs = 1e4;
@@ -1405,14 +1431,17 @@ async function initStore(options) {
1405
1431
  dbPath,
1406
1432
  encryptionKey: hexKey
1407
1433
  });
1408
- await ensureSchema();
1434
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1409
1435
  try {
1410
1436
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1411
1437
  initShardManager2(hexKey);
1412
1438
  } catch {
1413
1439
  }
1414
1440
  const client = getClient();
1415
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1441
+ const vResult = await retryOnBusy2(
1442
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1443
+ "version-query"
1444
+ );
1416
1445
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1417
1446
  }
1418
1447
  async function flushBatch() {
@@ -1594,8 +1623,8 @@ async function recordSessionKill(input) {
1594
1623
  // src/lib/session-registry.ts
1595
1624
  import { readFileSync as readFileSync2, writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
1596
1625
  import path4 from "path";
1597
- import os2 from "os";
1598
- var REGISTRY_PATH = path4.join(os2.homedir(), ".exe-os", "session-registry.json");
1626
+ import os3 from "os";
1627
+ var REGISTRY_PATH = path4.join(os3.homedir(), ".exe-os", "session-registry.json");
1599
1628
  function listSessions() {
1600
1629
  try {
1601
1630
  const raw = readFileSync2(REGISTRY_PATH, "utf8");
@@ -16,15 +16,15 @@ var __export = (target, all) => {
16
16
  };
17
17
 
18
18
  // src/lib/config.ts
19
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
19
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
20
20
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
21
21
  import path2 from "path";
22
- import os from "os";
22
+ import os2 from "os";
23
23
  function resolveDataDir() {
24
24
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
25
25
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
26
- const newDir = path2.join(os.homedir(), ".exe-os");
27
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
26
+ const newDir = path2.join(os2.homedir(), ".exe-os");
27
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
28
28
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
29
29
  try {
30
30
  renameSync(legacyDir, newDir);
@@ -111,7 +111,7 @@ async function loadConfig() {
111
111
  normalizeAutoUpdate(migratedCfg);
112
112
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
113
113
  if (config.dbPath.startsWith("~")) {
114
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
114
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
115
115
  }
116
116
  return config;
117
117
  } catch {
@@ -532,15 +532,20 @@ function addEmployee(employees, employee) {
532
532
  }
533
533
  return [...employees, normalized];
534
534
  }
535
+ function findExeBin() {
536
+ try {
537
+ return execSync2(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
538
+ } catch {
539
+ return null;
540
+ }
541
+ }
535
542
  function registerBinSymlinks(name) {
536
543
  const created = [];
537
544
  const skipped = [];
538
545
  const errors = [];
539
- let exeBinPath;
540
- try {
541
- exeBinPath = execSync2("which exe", { encoding: "utf-8" }).trim();
542
- } catch {
543
- errors.push("Could not find 'exe' in PATH");
546
+ const exeBinPath = findExeBin();
547
+ if (!exeBinPath) {
548
+ errors.push("Could not find 'exe-os' in PATH");
544
549
  return { created, skipped, errors };
545
550
  }
546
551
  const binDir = path5.dirname(exeBinPath);
@@ -1372,7 +1377,7 @@ var init_active_agent = __esm({
1372
1377
  });
1373
1378
 
1374
1379
  // src/bin/exe-launch-agent.ts
1375
- import os3 from "os";
1380
+ import os4 from "os";
1376
1381
  import path7 from "path";
1377
1382
  import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, readdirSync as readdirSync4 } from "fs";
1378
1383
  import { spawnSync } from "child_process";
@@ -1464,6 +1469,7 @@ async function ensureSchema() {
1464
1469
  const client = getRawClient();
1465
1470
  await client.execute("PRAGMA journal_mode = WAL");
1466
1471
  await client.execute("PRAGMA busy_timeout = 30000");
1472
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
1467
1473
  try {
1468
1474
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
1469
1475
  } catch {
@@ -2265,11 +2271,12 @@ async function disposeDatabase() {
2265
2271
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
2266
2272
  import { existsSync } from "fs";
2267
2273
  import path from "path";
2274
+ import os from "os";
2268
2275
  import crypto from "crypto";
2269
2276
  var SERVICE = "exe-mem";
2270
2277
  var ACCOUNT = "master-key";
2271
2278
  function getKeyDir() {
2272
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
2279
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
2273
2280
  }
2274
2281
  function getKeyPath() {
2275
2282
  return path.join(getKeyDir(), "master.key");
@@ -2306,6 +2313,30 @@ async function getMasterKey() {
2306
2313
 
2307
2314
  // src/lib/store.ts
2308
2315
  init_config();
2316
+ var INIT_MAX_RETRIES = 3;
2317
+ var INIT_RETRY_DELAY_MS = 1e3;
2318
+ function isBusyError2(err) {
2319
+ if (err instanceof Error) {
2320
+ const msg = err.message.toLowerCase();
2321
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
2322
+ }
2323
+ return false;
2324
+ }
2325
+ async function retryOnBusy2(fn, label) {
2326
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
2327
+ try {
2328
+ return await fn();
2329
+ } catch (err) {
2330
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
2331
+ process.stderr.write(
2332
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
2333
+ `
2334
+ );
2335
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
2336
+ }
2337
+ }
2338
+ throw new Error("unreachable");
2339
+ }
2309
2340
  var _pendingRecords = [];
2310
2341
  var _batchSize = 20;
2311
2342
  var _flushIntervalMs = 1e4;
@@ -2340,14 +2371,17 @@ async function initStore(options) {
2340
2371
  dbPath,
2341
2372
  encryptionKey: hexKey
2342
2373
  });
2343
- await ensureSchema();
2374
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
2344
2375
  try {
2345
2376
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
2346
2377
  initShardManager2(hexKey);
2347
2378
  } catch {
2348
2379
  }
2349
2380
  const client = getClient();
2350
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
2381
+ const vResult = await retryOnBusy2(
2382
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
2383
+ "version-query"
2384
+ );
2351
2385
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
2352
2386
  }
2353
2387
  async function flushBatch() {
@@ -2499,7 +2533,7 @@ function vectorToBlob(vector) {
2499
2533
  }
2500
2534
 
2501
2535
  // src/lib/behaviors-export.ts
2502
- import os2 from "os";
2536
+ import os3 from "os";
2503
2537
  import path4 from "path";
2504
2538
  import {
2505
2539
  existsSync as existsSync4,
@@ -2542,7 +2576,7 @@ async function listBehaviors(agentId, projectName, limit = 30) {
2542
2576
 
2543
2577
  // src/lib/behaviors-export.ts
2544
2578
  var BEHAVIORS_EXPORT_DIR = path4.join(
2545
- os2.homedir(),
2579
+ os3.homedir(),
2546
2580
  ".exe-os",
2547
2581
  "behaviors-export"
2548
2582
  );
@@ -2647,7 +2681,9 @@ var DEFAULT_PROVIDER = "default";
2647
2681
  function getKnownAgents() {
2648
2682
  try {
2649
2683
  return loadEmployeesSync().map((e) => e.name);
2650
- } catch {
2684
+ } catch (err) {
2685
+ process.stderr.write(`[exe-launch-agent] roster load failed, using default: ${err instanceof Error ? err.message : String(err)}
2686
+ `);
2651
2687
  return ["exe"];
2652
2688
  }
2653
2689
  }
@@ -2686,10 +2722,10 @@ async function isKnownAgent(agent) {
2686
2722
  }
2687
2723
  }
2688
2724
  function identityPathFor(agent) {
2689
- return path7.join(os3.homedir(), ".exe-os", "identity", `${agent}.md`);
2725
+ return path7.join(os4.homedir(), ".exe-os", "identity", `${agent}.md`);
2690
2726
  }
2691
2727
  function leanMcpConfigFor(agent) {
2692
- const p = path7.join(os3.homedir(), ".exe-os", "mcp-configs", `${agent}-lean.json`);
2728
+ const p = path7.join(os4.homedir(), ".exe-os", "mcp-configs", `${agent}-lean.json`);
2693
2729
  return existsSync6(p) ? p : null;
2694
2730
  }
2695
2731
  var _ccHelpOutput = null;
@@ -2713,7 +2749,7 @@ function _resetCcHelpCache() {
2713
2749
  function buildLaunchPlan(agent, behaviorsPath, passthrough, _hasAgentFlag, _provider) {
2714
2750
  const args = ["--dangerously-skip-permissions"];
2715
2751
  const idPath = identityPathFor(agent);
2716
- const ccAgentPath = path7.join(os3.homedir(), ".claude", "agents", `${agent}.md`);
2752
+ const ccAgentPath = path7.join(os4.homedir(), ".claude", "agents", `${agent}.md`);
2717
2753
  const effectiveIdPath = existsSync6(idPath) ? idPath : existsSync6(ccAgentPath) ? ccAgentPath : null;
2718
2754
  if (effectiveIdPath) {
2719
2755
  if (ccSupportsFlag("--system-prompt")) {
@@ -2845,7 +2881,7 @@ async function main() {
2845
2881
  _resetCcAgentSupportCache();
2846
2882
  const hasAgentFlag = claudeSupportsAgentFlag();
2847
2883
  if (hasAgentFlag) {
2848
- const ccAgentDir = path7.join(os3.homedir(), ".claude", "agents");
2884
+ const ccAgentDir = path7.join(os4.homedir(), ".claude", "agents");
2849
2885
  const ccAgentFile = path7.join(ccAgentDir, `${agent}.md`);
2850
2886
  if (!existsSync6(ccAgentFile)) {
2851
2887
  const exeIdentity = identityPathFor(agent);
@@ -2881,7 +2917,7 @@ async function main() {
2881
2917
  const empRole = (() => {
2882
2918
  try {
2883
2919
  const emps = __require("fs").readFileSync(
2884
- path7.join(os3.homedir(), ".exe-os", "exe-employees.json"),
2920
+ path7.join(os4.homedir(), ".exe-os", "exe-employees.json"),
2885
2921
  "utf-8"
2886
2922
  );
2887
2923
  const found = JSON.parse(emps).find(