@askexenow/exe-os 0.8.33 → 0.8.37

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 (89) hide show
  1. package/dist/bin/backfill-conversations.js +341 -349
  2. package/dist/bin/backfill-responses.js +81 -13
  3. package/dist/bin/backfill-vectors.js +72 -12
  4. package/dist/bin/cleanup-stale-review-tasks.js +63 -3
  5. package/dist/bin/cli.js +1737 -1117
  6. package/dist/bin/exe-assign.js +89 -19
  7. package/dist/bin/exe-boot.js +951 -101
  8. package/dist/bin/exe-call.js +61 -2
  9. package/dist/bin/exe-dispatch.js +61 -13
  10. package/dist/bin/exe-doctor.js +63 -3
  11. package/dist/bin/exe-export-behaviors.js +71 -3
  12. package/dist/bin/exe-forget.js +69 -4
  13. package/dist/bin/exe-gateway.js +178 -45
  14. package/dist/bin/exe-heartbeat.js +79 -14
  15. package/dist/bin/exe-kill.js +71 -3
  16. package/dist/bin/exe-launch-agent.js +148 -14
  17. package/dist/bin/exe-link.js +1437 -0
  18. package/dist/bin/exe-new-employee.js +98 -13
  19. package/dist/bin/exe-pending-messages.js +74 -8
  20. package/dist/bin/exe-pending-notifications.js +63 -3
  21. package/dist/bin/exe-pending-reviews.js +77 -11
  22. package/dist/bin/exe-rename.js +1287 -0
  23. package/dist/bin/exe-review.js +73 -5
  24. package/dist/bin/exe-search.js +88 -14
  25. package/dist/bin/exe-session-cleanup.js +102 -28
  26. package/dist/bin/exe-status.js +64 -4
  27. package/dist/bin/exe-team.js +64 -4
  28. package/dist/bin/git-sweep.js +80 -5
  29. package/dist/bin/graph-backfill.js +71 -3
  30. package/dist/bin/graph-export.js +71 -3
  31. package/dist/bin/install.js +38 -8
  32. package/dist/bin/scan-tasks.js +80 -5
  33. package/dist/bin/setup.js +128 -10
  34. package/dist/bin/shard-migrate.js +71 -3
  35. package/dist/bin/wiki-sync.js +71 -3
  36. package/dist/gateway/index.js +179 -46
  37. package/dist/hooks/bug-report-worker.js +254 -28
  38. package/dist/hooks/commit-complete.js +80 -5
  39. package/dist/hooks/error-recall.js +89 -15
  40. package/dist/hooks/exe-heartbeat-hook.js +1 -1
  41. package/dist/hooks/ingest-worker.js +185 -51
  42. package/dist/hooks/ingest.js +1 -1
  43. package/dist/hooks/instructions-loaded.js +81 -6
  44. package/dist/hooks/notification.js +81 -6
  45. package/dist/hooks/post-compact.js +81 -6
  46. package/dist/hooks/pre-compact.js +81 -6
  47. package/dist/hooks/pre-tool-use.js +423 -196
  48. package/dist/hooks/prompt-ingest-worker.js +91 -23
  49. package/dist/hooks/prompt-submit.js +159 -45
  50. package/dist/hooks/response-ingest-worker.js +96 -23
  51. package/dist/hooks/session-end.js +81 -6
  52. package/dist/hooks/session-start.js +89 -15
  53. package/dist/hooks/stop.js +81 -6
  54. package/dist/hooks/subagent-stop.js +81 -6
  55. package/dist/hooks/summary-worker.js +807 -55
  56. package/dist/index.js +198 -60
  57. package/dist/lib/cloud-sync.js +703 -18
  58. package/dist/lib/consolidation.js +4 -4
  59. package/dist/lib/database.js +64 -2
  60. package/dist/lib/device-registry.js +70 -3
  61. package/dist/lib/employee-templates.js +26 -0
  62. package/dist/lib/employees.js +34 -1
  63. package/dist/lib/exe-daemon.js +207 -74
  64. package/dist/lib/hybrid-search.js +88 -14
  65. package/dist/lib/identity-templates.js +51 -0
  66. package/dist/lib/identity.js +3 -3
  67. package/dist/lib/messaging.js +65 -17
  68. package/dist/lib/reminders.js +3 -3
  69. package/dist/lib/schedules.js +63 -3
  70. package/dist/lib/skill-learning.js +3 -3
  71. package/dist/lib/status-brief.js +63 -5
  72. package/dist/lib/store.js +73 -4
  73. package/dist/lib/task-router.js +4 -2
  74. package/dist/lib/tasks.js +95 -28
  75. package/dist/lib/tmux-routing.js +92 -23
  76. package/dist/mcp/server.js +800 -74
  77. package/dist/mcp/tools/complete-reminder.js +3 -3
  78. package/dist/mcp/tools/create-reminder.js +3 -3
  79. package/dist/mcp/tools/create-task.js +198 -31
  80. package/dist/mcp/tools/deactivate-behavior.js +4 -4
  81. package/dist/mcp/tools/list-reminders.js +3 -3
  82. package/dist/mcp/tools/list-tasks.js +19 -9
  83. package/dist/mcp/tools/send-message.js +69 -21
  84. package/dist/mcp/tools/update-task.js +28 -18
  85. package/dist/runtime/index.js +166 -28
  86. package/dist/tui/App.js +193 -40
  87. package/package.json +7 -3
  88. package/src/commands/exe/afk.md +116 -0
  89. package/src/commands/exe/rename.md +12 -0
@@ -262,7 +262,7 @@ function listShards() {
262
262
  }
263
263
  async function ensureShardSchema(client) {
264
264
  await client.execute("PRAGMA journal_mode = WAL");
265
- await client.execute("PRAGMA busy_timeout = 5000");
265
+ await client.execute("PRAGMA busy_timeout = 30000");
266
266
  try {
267
267
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
268
268
  } catch {
@@ -459,12 +459,65 @@ var EMBEDDING_DIM = 1024;
459
459
 
460
460
  // src/lib/database.ts
461
461
  import { createClient } from "@libsql/client";
462
+
463
+ // src/lib/db-retry.ts
464
+ var MAX_RETRIES = 3;
465
+ var BASE_DELAY_MS = 200;
466
+ var MAX_JITTER_MS = 300;
467
+ function isBusyError(err) {
468
+ if (err instanceof Error) {
469
+ const msg = err.message.toLowerCase();
470
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
471
+ }
472
+ return false;
473
+ }
474
+ function delay(ms) {
475
+ return new Promise((resolve) => setTimeout(resolve, ms));
476
+ }
477
+ async function retryOnBusy(fn, label) {
478
+ let lastError;
479
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
480
+ try {
481
+ return await fn();
482
+ } catch (err) {
483
+ lastError = err;
484
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
485
+ throw err;
486
+ }
487
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
488
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
489
+ process.stderr.write(
490
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
491
+ `
492
+ );
493
+ await delay(backoff + jitter);
494
+ }
495
+ }
496
+ throw lastError;
497
+ }
498
+ function wrapWithRetry(client) {
499
+ return new Proxy(client, {
500
+ get(target, prop, receiver) {
501
+ if (prop === "execute") {
502
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
503
+ }
504
+ if (prop === "batch") {
505
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
506
+ }
507
+ return Reflect.get(target, prop, receiver);
508
+ }
509
+ });
510
+ }
511
+
512
+ // src/lib/database.ts
462
513
  var _client = null;
514
+ var _resilientClient = null;
463
515
  var initTurso = initDatabase;
464
516
  async function initDatabase(config) {
465
517
  if (_client) {
466
518
  _client.close();
467
519
  _client = null;
520
+ _resilientClient = null;
468
521
  }
469
522
  const opts = {
470
523
  url: `file:${config.dbPath}`
@@ -473,17 +526,24 @@ async function initDatabase(config) {
473
526
  opts.encryptionKey = config.encryptionKey;
474
527
  }
475
528
  _client = createClient(opts);
529
+ _resilientClient = wrapWithRetry(_client);
476
530
  }
477
531
  function getClient() {
532
+ if (!_resilientClient) {
533
+ throw new Error("Database client not initialized. Call initDatabase() first.");
534
+ }
535
+ return _resilientClient;
536
+ }
537
+ function getRawClient() {
478
538
  if (!_client) {
479
539
  throw new Error("Database client not initialized. Call initDatabase() first.");
480
540
  }
481
541
  return _client;
482
542
  }
483
543
  async function ensureSchema() {
484
- const client = getClient();
544
+ const client = getRawClient();
485
545
  await client.execute("PRAGMA journal_mode = WAL");
486
- await client.execute("PRAGMA busy_timeout = 5000");
546
+ await client.execute("PRAGMA busy_timeout = 30000");
487
547
  try {
488
548
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
489
549
  } catch {
@@ -1384,7 +1444,8 @@ async function writeMemory(record) {
1384
1444
  has_error: record.has_error ? 1 : 0,
1385
1445
  raw_text: record.raw_text,
1386
1446
  vector: record.vector,
1387
- version: _nextVersion++,
1447
+ version: 0,
1448
+ // Placeholder — assigned atomically at flush time
1388
1449
  task_id: record.task_id ?? null,
1389
1450
  importance: record.importance ?? 5,
1390
1451
  status: record.status ?? "active",
@@ -1418,6 +1479,13 @@ async function flushBatch() {
1418
1479
  _flushing = true;
1419
1480
  try {
1420
1481
  const batch = _pendingRecords.slice(0);
1482
+ const client = getClient();
1483
+ const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1484
+ let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1485
+ for (const row of batch) {
1486
+ row.version = baseVersion++;
1487
+ }
1488
+ _nextVersion = baseVersion;
1421
1489
  const buildStmt = (row) => {
1422
1490
  const hasVector = row.vector !== null;
1423
1491
  const taskId = row.task_id ?? null;
@@ -1732,11 +1800,11 @@ async function connectEmbedDaemon() {
1732
1800
  }
1733
1801
  }
1734
1802
  const start = Date.now();
1735
- let delay = 100;
1803
+ let delay2 = 100;
1736
1804
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1737
- await new Promise((r) => setTimeout(r, delay));
1805
+ await new Promise((r) => setTimeout(r, delay2));
1738
1806
  if (await connectToSocket()) return true;
1739
- delay = Math.min(delay * 2, 3e3);
1807
+ delay2 = Math.min(delay2 * 2, 3e3);
1740
1808
  }
1741
1809
  return false;
1742
1810
  }
@@ -1828,11 +1896,11 @@ async function embedViaClient(text, priority = "high") {
1828
1896
  `);
1829
1897
  killAndRespawnDaemon();
1830
1898
  const start = Date.now();
1831
- let delay = 200;
1899
+ let delay2 = 200;
1832
1900
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1833
- await new Promise((r) => setTimeout(r, delay));
1901
+ await new Promise((r) => setTimeout(r, delay2));
1834
1902
  if (await connectToSocket()) break;
1835
- delay = Math.min(delay * 2, 3e3);
1903
+ delay2 = Math.min(delay2 * 2, 3e3);
1836
1904
  }
1837
1905
  if (!_connected) return null;
1838
1906
  }
@@ -1844,11 +1912,11 @@ async function embedViaClient(text, priority = "high") {
1844
1912
  `);
1845
1913
  killAndRespawnDaemon();
1846
1914
  const start = Date.now();
1847
- let delay = 200;
1915
+ let delay2 = 200;
1848
1916
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1849
- await new Promise((r) => setTimeout(r, delay));
1917
+ await new Promise((r) => setTimeout(r, delay2));
1850
1918
  if (await connectToSocket()) break;
1851
- delay = Math.min(delay * 2, 3e3);
1919
+ delay2 = Math.min(delay2 * 2, 3e3);
1852
1920
  }
1853
1921
  if (!_connected) return null;
1854
1922
  const retry = await sendRequest([text], priority);
@@ -262,7 +262,7 @@ function listShards() {
262
262
  }
263
263
  async function ensureShardSchema(client) {
264
264
  await client.execute("PRAGMA journal_mode = WAL");
265
- await client.execute("PRAGMA busy_timeout = 5000");
265
+ await client.execute("PRAGMA busy_timeout = 30000");
266
266
  try {
267
267
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
268
268
  } catch {
@@ -448,12 +448,65 @@ var init_shard_manager = __esm({
448
448
 
449
449
  // src/lib/database.ts
450
450
  import { createClient } from "@libsql/client";
451
+
452
+ // src/lib/db-retry.ts
453
+ var MAX_RETRIES = 3;
454
+ var BASE_DELAY_MS = 200;
455
+ var MAX_JITTER_MS = 300;
456
+ function isBusyError(err) {
457
+ if (err instanceof Error) {
458
+ const msg = err.message.toLowerCase();
459
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
460
+ }
461
+ return false;
462
+ }
463
+ function delay(ms) {
464
+ return new Promise((resolve) => setTimeout(resolve, ms));
465
+ }
466
+ async function retryOnBusy(fn, label) {
467
+ let lastError;
468
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
469
+ try {
470
+ return await fn();
471
+ } catch (err) {
472
+ lastError = err;
473
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
474
+ throw err;
475
+ }
476
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
477
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
478
+ process.stderr.write(
479
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
480
+ `
481
+ );
482
+ await delay(backoff + jitter);
483
+ }
484
+ }
485
+ throw lastError;
486
+ }
487
+ function wrapWithRetry(client) {
488
+ return new Proxy(client, {
489
+ get(target, prop, receiver) {
490
+ if (prop === "execute") {
491
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
492
+ }
493
+ if (prop === "batch") {
494
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
495
+ }
496
+ return Reflect.get(target, prop, receiver);
497
+ }
498
+ });
499
+ }
500
+
501
+ // src/lib/database.ts
451
502
  var _client = null;
503
+ var _resilientClient = null;
452
504
  var initTurso = initDatabase;
453
505
  async function initDatabase(config) {
454
506
  if (_client) {
455
507
  _client.close();
456
508
  _client = null;
509
+ _resilientClient = null;
457
510
  }
458
511
  const opts = {
459
512
  url: `file:${config.dbPath}`
@@ -462,17 +515,24 @@ async function initDatabase(config) {
462
515
  opts.encryptionKey = config.encryptionKey;
463
516
  }
464
517
  _client = createClient(opts);
518
+ _resilientClient = wrapWithRetry(_client);
465
519
  }
466
520
  function getClient() {
521
+ if (!_resilientClient) {
522
+ throw new Error("Database client not initialized. Call initDatabase() first.");
523
+ }
524
+ return _resilientClient;
525
+ }
526
+ function getRawClient() {
467
527
  if (!_client) {
468
528
  throw new Error("Database client not initialized. Call initDatabase() first.");
469
529
  }
470
530
  return _client;
471
531
  }
472
532
  async function ensureSchema() {
473
- const client = getClient();
533
+ const client = getRawClient();
474
534
  await client.execute("PRAGMA journal_mode = WAL");
475
- await client.execute("PRAGMA busy_timeout = 5000");
535
+ await client.execute("PRAGMA busy_timeout = 30000");
476
536
  try {
477
537
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
478
538
  } catch {
@@ -1546,11 +1606,11 @@ async function connectEmbedDaemon() {
1546
1606
  }
1547
1607
  }
1548
1608
  const start = Date.now();
1549
- let delay = 100;
1609
+ let delay2 = 100;
1550
1610
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1551
- await new Promise((r) => setTimeout(r, delay));
1611
+ await new Promise((r) => setTimeout(r, delay2));
1552
1612
  if (await connectToSocket()) return true;
1553
- delay = Math.min(delay * 2, 3e3);
1613
+ delay2 = Math.min(delay2 * 2, 3e3);
1554
1614
  }
1555
1615
  return false;
1556
1616
  }
@@ -1642,11 +1702,11 @@ async function embedViaClient(text, priority = "high") {
1642
1702
  `);
1643
1703
  killAndRespawnDaemon();
1644
1704
  const start = Date.now();
1645
- let delay = 200;
1705
+ let delay2 = 200;
1646
1706
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1647
- await new Promise((r) => setTimeout(r, delay));
1707
+ await new Promise((r) => setTimeout(r, delay2));
1648
1708
  if (await connectToSocket()) break;
1649
- delay = Math.min(delay * 2, 3e3);
1709
+ delay2 = Math.min(delay2 * 2, 3e3);
1650
1710
  }
1651
1711
  if (!_connected) return null;
1652
1712
  }
@@ -1658,11 +1718,11 @@ async function embedViaClient(text, priority = "high") {
1658
1718
  `);
1659
1719
  killAndRespawnDaemon();
1660
1720
  const start = Date.now();
1661
- let delay = 200;
1721
+ let delay2 = 200;
1662
1722
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1663
- await new Promise((r) => setTimeout(r, delay));
1723
+ await new Promise((r) => setTimeout(r, delay2));
1664
1724
  if (await connectToSocket()) break;
1665
- delay = Math.min(delay * 2, 3e3);
1725
+ delay2 = Math.min(delay2 * 2, 3e3);
1666
1726
  }
1667
1727
  if (!_connected) return null;
1668
1728
  const retry = await sendRequest([text], priority);
@@ -262,7 +262,7 @@ function listShards() {
262
262
  }
263
263
  async function ensureShardSchema(client) {
264
264
  await client.execute("PRAGMA journal_mode = WAL");
265
- await client.execute("PRAGMA busy_timeout = 5000");
265
+ await client.execute("PRAGMA busy_timeout = 30000");
266
266
  try {
267
267
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
268
268
  } catch {
@@ -448,12 +448,65 @@ var init_shard_manager = __esm({
448
448
 
449
449
  // src/lib/database.ts
450
450
  import { createClient } from "@libsql/client";
451
+
452
+ // src/lib/db-retry.ts
453
+ var MAX_RETRIES = 3;
454
+ var BASE_DELAY_MS = 200;
455
+ var MAX_JITTER_MS = 300;
456
+ function isBusyError(err) {
457
+ if (err instanceof Error) {
458
+ const msg = err.message.toLowerCase();
459
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
460
+ }
461
+ return false;
462
+ }
463
+ function delay(ms) {
464
+ return new Promise((resolve) => setTimeout(resolve, ms));
465
+ }
466
+ async function retryOnBusy(fn, label) {
467
+ let lastError;
468
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
469
+ try {
470
+ return await fn();
471
+ } catch (err) {
472
+ lastError = err;
473
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
474
+ throw err;
475
+ }
476
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
477
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
478
+ process.stderr.write(
479
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
480
+ `
481
+ );
482
+ await delay(backoff + jitter);
483
+ }
484
+ }
485
+ throw lastError;
486
+ }
487
+ function wrapWithRetry(client) {
488
+ return new Proxy(client, {
489
+ get(target, prop, receiver) {
490
+ if (prop === "execute") {
491
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
492
+ }
493
+ if (prop === "batch") {
494
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
495
+ }
496
+ return Reflect.get(target, prop, receiver);
497
+ }
498
+ });
499
+ }
500
+
501
+ // src/lib/database.ts
451
502
  var _client = null;
503
+ var _resilientClient = null;
452
504
  var initTurso = initDatabase;
453
505
  async function initDatabase(config) {
454
506
  if (_client) {
455
507
  _client.close();
456
508
  _client = null;
509
+ _resilientClient = null;
457
510
  }
458
511
  const opts = {
459
512
  url: `file:${config.dbPath}`
@@ -462,17 +515,24 @@ async function initDatabase(config) {
462
515
  opts.encryptionKey = config.encryptionKey;
463
516
  }
464
517
  _client = createClient(opts);
518
+ _resilientClient = wrapWithRetry(_client);
465
519
  }
466
520
  function getClient() {
521
+ if (!_resilientClient) {
522
+ throw new Error("Database client not initialized. Call initDatabase() first.");
523
+ }
524
+ return _resilientClient;
525
+ }
526
+ function getRawClient() {
467
527
  if (!_client) {
468
528
  throw new Error("Database client not initialized. Call initDatabase() first.");
469
529
  }
470
530
  return _client;
471
531
  }
472
532
  async function ensureSchema() {
473
- const client = getClient();
533
+ const client = getRawClient();
474
534
  await client.execute("PRAGMA journal_mode = WAL");
475
- await client.execute("PRAGMA busy_timeout = 5000");
535
+ await client.execute("PRAGMA busy_timeout = 30000");
476
536
  try {
477
537
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
478
538
  } catch {