@askexenow/exe-os 0.8.32 → 0.8.36

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 (87) hide show
  1. package/dist/bin/backfill-conversations.js +332 -348
  2. package/dist/bin/backfill-responses.js +72 -12
  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 +1518 -1122
  6. package/dist/bin/exe-agent.js +4 -4
  7. package/dist/bin/exe-assign.js +80 -18
  8. package/dist/bin/exe-boot.js +408 -89
  9. package/dist/bin/exe-call.js +83 -24
  10. package/dist/bin/exe-dispatch.js +18 -10
  11. package/dist/bin/exe-doctor.js +63 -3
  12. package/dist/bin/exe-export-behaviors.js +64 -3
  13. package/dist/bin/exe-forget.js +69 -4
  14. package/dist/bin/exe-gateway.js +121 -36
  15. package/dist/bin/exe-heartbeat.js +77 -13
  16. package/dist/bin/exe-kill.js +64 -3
  17. package/dist/bin/exe-launch-agent.js +162 -35
  18. package/dist/bin/exe-link.js +946 -0
  19. package/dist/bin/exe-new-employee.js +121 -36
  20. package/dist/bin/exe-pending-messages.js +72 -7
  21. package/dist/bin/exe-pending-notifications.js +63 -3
  22. package/dist/bin/exe-pending-reviews.js +75 -10
  23. package/dist/bin/exe-rename.js +1287 -0
  24. package/dist/bin/exe-review.js +64 -4
  25. package/dist/bin/exe-search.js +79 -13
  26. package/dist/bin/exe-session-cleanup.js +91 -26
  27. package/dist/bin/exe-status.js +64 -4
  28. package/dist/bin/exe-team.js +64 -4
  29. package/dist/bin/git-sweep.js +71 -4
  30. package/dist/bin/graph-backfill.js +64 -3
  31. package/dist/bin/graph-export.js +64 -3
  32. package/dist/bin/install.js +3 -3
  33. package/dist/bin/scan-tasks.js +71 -4
  34. package/dist/bin/setup.js +156 -38
  35. package/dist/bin/shard-migrate.js +64 -3
  36. package/dist/bin/wiki-sync.js +64 -3
  37. package/dist/gateway/index.js +122 -37
  38. package/dist/hooks/bug-report-worker.js +209 -23
  39. package/dist/hooks/commit-complete.js +71 -4
  40. package/dist/hooks/error-recall.js +79 -13
  41. package/dist/hooks/ingest-worker.js +129 -43
  42. package/dist/hooks/instructions-loaded.js +71 -4
  43. package/dist/hooks/notification.js +71 -4
  44. package/dist/hooks/post-compact.js +71 -4
  45. package/dist/hooks/pre-compact.js +71 -4
  46. package/dist/hooks/pre-tool-use.js +413 -194
  47. package/dist/hooks/prompt-ingest-worker.js +82 -22
  48. package/dist/hooks/prompt-submit.js +103 -37
  49. package/dist/hooks/response-ingest-worker.js +87 -22
  50. package/dist/hooks/session-end.js +71 -4
  51. package/dist/hooks/session-start.js +79 -13
  52. package/dist/hooks/stop.js +71 -4
  53. package/dist/hooks/subagent-stop.js +71 -4
  54. package/dist/hooks/summary-worker.js +303 -50
  55. package/dist/index.js +134 -46
  56. package/dist/lib/cloud-sync.js +209 -15
  57. package/dist/lib/consolidation.js +4 -4
  58. package/dist/lib/database.js +64 -2
  59. package/dist/lib/device-registry.js +70 -3
  60. package/dist/lib/employee-templates.js +48 -22
  61. package/dist/lib/employees.js +34 -1
  62. package/dist/lib/exe-daemon.js +136 -53
  63. package/dist/lib/hybrid-search.js +79 -13
  64. package/dist/lib/identity-templates.js +57 -6
  65. package/dist/lib/identity.js +3 -3
  66. package/dist/lib/messaging.js +22 -14
  67. package/dist/lib/reminders.js +3 -3
  68. package/dist/lib/schedules.js +63 -3
  69. package/dist/lib/skill-learning.js +3 -3
  70. package/dist/lib/status-brief.js +63 -5
  71. package/dist/lib/store.js +64 -3
  72. package/dist/lib/task-router.js +4 -2
  73. package/dist/lib/tasks.js +48 -21
  74. package/dist/lib/tmux-routing.js +47 -20
  75. package/dist/mcp/server.js +727 -58
  76. package/dist/mcp/tools/complete-reminder.js +3 -3
  77. package/dist/mcp/tools/create-reminder.js +3 -3
  78. package/dist/mcp/tools/create-task.js +151 -24
  79. package/dist/mcp/tools/deactivate-behavior.js +3 -3
  80. package/dist/mcp/tools/list-reminders.js +3 -3
  81. package/dist/mcp/tools/list-tasks.js +17 -8
  82. package/dist/mcp/tools/send-message.js +24 -16
  83. package/dist/mcp/tools/update-task.js +25 -16
  84. package/dist/runtime/index.js +112 -24
  85. package/dist/tui/App.js +139 -36
  86. package/package.json +6 -2
  87. 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 {
@@ -449,7 +449,7 @@ var init_shard_manager = __esm({
449
449
  // src/lib/employees.ts
450
450
  init_config();
451
451
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
452
- import { existsSync as existsSync2, symlinkSync, readlinkSync } from "fs";
452
+ import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2 } from "fs";
453
453
  import { execSync } from "child_process";
454
454
  import path2 from "path";
455
455
  var EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
@@ -467,12 +467,65 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
467
467
 
468
468
  // src/lib/database.ts
469
469
  import { createClient } from "@libsql/client";
470
+
471
+ // src/lib/db-retry.ts
472
+ var MAX_RETRIES = 3;
473
+ var BASE_DELAY_MS = 200;
474
+ var MAX_JITTER_MS = 300;
475
+ function isBusyError(err) {
476
+ if (err instanceof Error) {
477
+ const msg = err.message.toLowerCase();
478
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
479
+ }
480
+ return false;
481
+ }
482
+ function delay(ms) {
483
+ return new Promise((resolve) => setTimeout(resolve, ms));
484
+ }
485
+ async function retryOnBusy(fn, label) {
486
+ let lastError;
487
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
488
+ try {
489
+ return await fn();
490
+ } catch (err) {
491
+ lastError = err;
492
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
493
+ throw err;
494
+ }
495
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
496
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
497
+ process.stderr.write(
498
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
499
+ `
500
+ );
501
+ await delay(backoff + jitter);
502
+ }
503
+ }
504
+ throw lastError;
505
+ }
506
+ function wrapWithRetry(client) {
507
+ return new Proxy(client, {
508
+ get(target, prop, receiver) {
509
+ if (prop === "execute") {
510
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
511
+ }
512
+ if (prop === "batch") {
513
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
514
+ }
515
+ return Reflect.get(target, prop, receiver);
516
+ }
517
+ });
518
+ }
519
+
520
+ // src/lib/database.ts
470
521
  var _client = null;
522
+ var _resilientClient = null;
471
523
  var initTurso = initDatabase;
472
524
  async function initDatabase(config) {
473
525
  if (_client) {
474
526
  _client.close();
475
527
  _client = null;
528
+ _resilientClient = null;
476
529
  }
477
530
  const opts = {
478
531
  url: `file:${config.dbPath}`
@@ -481,17 +534,24 @@ async function initDatabase(config) {
481
534
  opts.encryptionKey = config.encryptionKey;
482
535
  }
483
536
  _client = createClient(opts);
537
+ _resilientClient = wrapWithRetry(_client);
484
538
  }
485
539
  function getClient() {
540
+ if (!_resilientClient) {
541
+ throw new Error("Database client not initialized. Call initDatabase() first.");
542
+ }
543
+ return _resilientClient;
544
+ }
545
+ function getRawClient() {
486
546
  if (!_client) {
487
547
  throw new Error("Database client not initialized. Call initDatabase() first.");
488
548
  }
489
549
  return _client;
490
550
  }
491
551
  async function ensureSchema() {
492
- const client = getClient();
552
+ const client = getRawClient();
493
553
  await client.execute("PRAGMA journal_mode = WAL");
494
- await client.execute("PRAGMA busy_timeout = 5000");
554
+ await client.execute("PRAGMA busy_timeout = 30000");
495
555
  try {
496
556
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
497
557
  } catch {
@@ -24,6 +24,61 @@ var init_memory = __esm({
24
24
  }
25
25
  });
26
26
 
27
+ // src/lib/db-retry.ts
28
+ function isBusyError(err) {
29
+ if (err instanceof Error) {
30
+ const msg = err.message.toLowerCase();
31
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
32
+ }
33
+ return false;
34
+ }
35
+ function delay(ms) {
36
+ return new Promise((resolve) => setTimeout(resolve, ms));
37
+ }
38
+ async function retryOnBusy(fn, label) {
39
+ let lastError;
40
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
41
+ try {
42
+ return await fn();
43
+ } catch (err) {
44
+ lastError = err;
45
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
46
+ throw err;
47
+ }
48
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
49
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
50
+ process.stderr.write(
51
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
52
+ `
53
+ );
54
+ await delay(backoff + jitter);
55
+ }
56
+ }
57
+ throw lastError;
58
+ }
59
+ function wrapWithRetry(client) {
60
+ return new Proxy(client, {
61
+ get(target, prop, receiver) {
62
+ if (prop === "execute") {
63
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
64
+ }
65
+ if (prop === "batch") {
66
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
67
+ }
68
+ return Reflect.get(target, prop, receiver);
69
+ }
70
+ });
71
+ }
72
+ var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
73
+ var init_db_retry = __esm({
74
+ "src/lib/db-retry.ts"() {
75
+ "use strict";
76
+ MAX_RETRIES = 3;
77
+ BASE_DELAY_MS = 200;
78
+ MAX_JITTER_MS = 300;
79
+ }
80
+ });
81
+
27
82
  // src/lib/database.ts
28
83
  var database_exports = {};
29
84
  __export(database_exports, {
@@ -31,6 +86,7 @@ __export(database_exports, {
31
86
  disposeTurso: () => disposeTurso,
32
87
  ensureSchema: () => ensureSchema,
33
88
  getClient: () => getClient,
89
+ getRawClient: () => getRawClient,
34
90
  initDatabase: () => initDatabase,
35
91
  initTurso: () => initTurso,
36
92
  isInitialized: () => isInitialized
@@ -40,6 +96,7 @@ async function initDatabase(config) {
40
96
  if (_client) {
41
97
  _client.close();
42
98
  _client = null;
99
+ _resilientClient = null;
43
100
  }
44
101
  const opts = {
45
102
  url: `file:${config.dbPath}`
@@ -48,20 +105,27 @@ async function initDatabase(config) {
48
105
  opts.encryptionKey = config.encryptionKey;
49
106
  }
50
107
  _client = createClient(opts);
108
+ _resilientClient = wrapWithRetry(_client);
51
109
  }
52
110
  function isInitialized() {
53
111
  return _client !== null;
54
112
  }
55
113
  function getClient() {
114
+ if (!_resilientClient) {
115
+ throw new Error("Database client not initialized. Call initDatabase() first.");
116
+ }
117
+ return _resilientClient;
118
+ }
119
+ function getRawClient() {
56
120
  if (!_client) {
57
121
  throw new Error("Database client not initialized. Call initDatabase() first.");
58
122
  }
59
123
  return _client;
60
124
  }
61
125
  async function ensureSchema() {
62
- const client = getClient();
126
+ const client = getRawClient();
63
127
  await client.execute("PRAGMA journal_mode = WAL");
64
- await client.execute("PRAGMA busy_timeout = 5000");
128
+ await client.execute("PRAGMA busy_timeout = 30000");
65
129
  try {
66
130
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
67
131
  } catch {
@@ -854,13 +918,16 @@ async function disposeDatabase() {
854
918
  if (_client) {
855
919
  _client.close();
856
920
  _client = null;
921
+ _resilientClient = null;
857
922
  }
858
923
  }
859
- var _client, initTurso, disposeTurso;
924
+ var _client, _resilientClient, initTurso, disposeTurso;
860
925
  var init_database = __esm({
861
926
  "src/lib/database.ts"() {
862
927
  "use strict";
928
+ init_db_retry();
863
929
  _client = null;
930
+ _resilientClient = null;
864
931
  initTurso = initDatabase;
865
932
  disposeTurso = disposeDatabase;
866
933
  }
@@ -1162,7 +1229,7 @@ function listShards() {
1162
1229
  }
1163
1230
  async function ensureShardSchema(client) {
1164
1231
  await client.execute("PRAGMA journal_mode = WAL");
1165
- await client.execute("PRAGMA busy_timeout = 5000");
1232
+ await client.execute("PRAGMA busy_timeout = 30000");
1166
1233
  try {
1167
1234
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
1168
1235
  } catch {
@@ -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 {
@@ -1266,6 +1326,7 @@ async function disposeDatabase() {
1266
1326
  if (_client) {
1267
1327
  _client.close();
1268
1328
  _client = null;
1329
+ _resilientClient = null;
1269
1330
  }
1270
1331
  }
1271
1332
 
@@ -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 {
@@ -452,12 +452,65 @@ import path4 from "path";
452
452
 
453
453
  // src/lib/database.ts
454
454
  import { createClient } from "@libsql/client";
455
+
456
+ // src/lib/db-retry.ts
457
+ var MAX_RETRIES = 3;
458
+ var BASE_DELAY_MS = 200;
459
+ var MAX_JITTER_MS = 300;
460
+ function isBusyError(err) {
461
+ if (err instanceof Error) {
462
+ const msg = err.message.toLowerCase();
463
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
464
+ }
465
+ return false;
466
+ }
467
+ function delay(ms) {
468
+ return new Promise((resolve) => setTimeout(resolve, ms));
469
+ }
470
+ async function retryOnBusy(fn, label) {
471
+ let lastError;
472
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
473
+ try {
474
+ return await fn();
475
+ } catch (err) {
476
+ lastError = err;
477
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
478
+ throw err;
479
+ }
480
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
481
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
482
+ process.stderr.write(
483
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
484
+ `
485
+ );
486
+ await delay(backoff + jitter);
487
+ }
488
+ }
489
+ throw lastError;
490
+ }
491
+ function wrapWithRetry(client) {
492
+ return new Proxy(client, {
493
+ get(target, prop, receiver) {
494
+ if (prop === "execute") {
495
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
496
+ }
497
+ if (prop === "batch") {
498
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
499
+ }
500
+ return Reflect.get(target, prop, receiver);
501
+ }
502
+ });
503
+ }
504
+
505
+ // src/lib/database.ts
455
506
  var _client = null;
507
+ var _resilientClient = null;
456
508
  var initTurso = initDatabase;
457
509
  async function initDatabase(config) {
458
510
  if (_client) {
459
511
  _client.close();
460
512
  _client = null;
513
+ _resilientClient = null;
461
514
  }
462
515
  const opts = {
463
516
  url: `file:${config.dbPath}`
@@ -466,17 +519,24 @@ async function initDatabase(config) {
466
519
  opts.encryptionKey = config.encryptionKey;
467
520
  }
468
521
  _client = createClient(opts);
522
+ _resilientClient = wrapWithRetry(_client);
469
523
  }
470
524
  function getClient() {
525
+ if (!_resilientClient) {
526
+ throw new Error("Database client not initialized. Call initDatabase() first.");
527
+ }
528
+ return _resilientClient;
529
+ }
530
+ function getRawClient() {
471
531
  if (!_client) {
472
532
  throw new Error("Database client not initialized. Call initDatabase() first.");
473
533
  }
474
534
  return _client;
475
535
  }
476
536
  async function ensureSchema() {
477
- const client = getClient();
537
+ const client = getRawClient();
478
538
  await client.execute("PRAGMA journal_mode = WAL");
479
- await client.execute("PRAGMA busy_timeout = 5000");
539
+ await client.execute("PRAGMA busy_timeout = 30000");
480
540
  try {
481
541
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
482
542
  } catch {
@@ -1270,6 +1330,7 @@ async function disposeDatabase() {
1270
1330
  if (_client) {
1271
1331
  _client.close();
1272
1332
  _client = null;
1333
+ _resilientClient = null;
1273
1334
  }
1274
1335
  }
1275
1336
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/adapters/claude/installer.ts
4
4
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir } from "fs/promises";
5
- import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
5
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
6
6
  import path4 from "path";
7
7
  import os3 from "os";
8
8
  import { fileURLToPath } from "url";
@@ -20,7 +20,7 @@ import {
20
20
 
21
21
  // src/lib/employees.ts
22
22
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
23
- import { existsSync as existsSync2, symlinkSync, readlinkSync } from "fs";
23
+ import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2 } from "fs";
24
24
  import { execSync } from "child_process";
25
25
  import path2 from "path";
26
26
 
@@ -208,7 +208,7 @@ function resolvePackageRoot() {
208
208
  const pkgPath = path4.join(dir, "package.json");
209
209
  if (existsSync4(pkgPath)) {
210
210
  try {
211
- const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
211
+ const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
212
212
  if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
213
213
  } catch {
214
214
  }
@@ -15,6 +15,61 @@ var __export = (target, all) => {
15
15
  __defProp(target, name, { get: all[name], enumerable: true });
16
16
  };
17
17
 
18
+ // src/lib/db-retry.ts
19
+ function isBusyError(err) {
20
+ if (err instanceof Error) {
21
+ const msg = err.message.toLowerCase();
22
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
23
+ }
24
+ return false;
25
+ }
26
+ function delay(ms) {
27
+ return new Promise((resolve) => setTimeout(resolve, ms));
28
+ }
29
+ async function retryOnBusy(fn, label) {
30
+ let lastError;
31
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
32
+ try {
33
+ return await fn();
34
+ } catch (err) {
35
+ lastError = err;
36
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
37
+ throw err;
38
+ }
39
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
40
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
41
+ process.stderr.write(
42
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
43
+ `
44
+ );
45
+ await delay(backoff + jitter);
46
+ }
47
+ }
48
+ throw lastError;
49
+ }
50
+ function wrapWithRetry(client) {
51
+ return new Proxy(client, {
52
+ get(target, prop, receiver) {
53
+ if (prop === "execute") {
54
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
55
+ }
56
+ if (prop === "batch") {
57
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
58
+ }
59
+ return Reflect.get(target, prop, receiver);
60
+ }
61
+ });
62
+ }
63
+ var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
64
+ var init_db_retry = __esm({
65
+ "src/lib/db-retry.ts"() {
66
+ "use strict";
67
+ MAX_RETRIES = 3;
68
+ BASE_DELAY_MS = 200;
69
+ MAX_JITTER_MS = 300;
70
+ }
71
+ });
72
+
18
73
  // src/lib/database.ts
19
74
  var database_exports = {};
20
75
  __export(database_exports, {
@@ -22,6 +77,7 @@ __export(database_exports, {
22
77
  disposeTurso: () => disposeTurso,
23
78
  ensureSchema: () => ensureSchema,
24
79
  getClient: () => getClient,
80
+ getRawClient: () => getRawClient,
25
81
  initDatabase: () => initDatabase,
26
82
  initTurso: () => initTurso,
27
83
  isInitialized: () => isInitialized
@@ -31,6 +87,7 @@ async function initDatabase(config) {
31
87
  if (_client) {
32
88
  _client.close();
33
89
  _client = null;
90
+ _resilientClient = null;
34
91
  }
35
92
  const opts = {
36
93
  url: `file:${config.dbPath}`
@@ -39,20 +96,27 @@ async function initDatabase(config) {
39
96
  opts.encryptionKey = config.encryptionKey;
40
97
  }
41
98
  _client = createClient(opts);
99
+ _resilientClient = wrapWithRetry(_client);
42
100
  }
43
101
  function isInitialized() {
44
102
  return _client !== null;
45
103
  }
46
104
  function getClient() {
105
+ if (!_resilientClient) {
106
+ throw new Error("Database client not initialized. Call initDatabase() first.");
107
+ }
108
+ return _resilientClient;
109
+ }
110
+ function getRawClient() {
47
111
  if (!_client) {
48
112
  throw new Error("Database client not initialized. Call initDatabase() first.");
49
113
  }
50
114
  return _client;
51
115
  }
52
116
  async function ensureSchema() {
53
- const client = getClient();
117
+ const client = getRawClient();
54
118
  await client.execute("PRAGMA journal_mode = WAL");
55
- await client.execute("PRAGMA busy_timeout = 5000");
119
+ await client.execute("PRAGMA busy_timeout = 30000");
56
120
  try {
57
121
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
58
122
  } catch {
@@ -845,13 +909,16 @@ async function disposeDatabase() {
845
909
  if (_client) {
846
910
  _client.close();
847
911
  _client = null;
912
+ _resilientClient = null;
848
913
  }
849
914
  }
850
- var _client, initTurso, disposeTurso;
915
+ var _client, _resilientClient, initTurso, disposeTurso;
851
916
  var init_database = __esm({
852
917
  "src/lib/database.ts"() {
853
918
  "use strict";
919
+ init_db_retry();
854
920
  _client = null;
921
+ _resilientClient = null;
855
922
  initTurso = initDatabase;
856
923
  disposeTurso = disposeDatabase;
857
924
  }
@@ -1162,7 +1229,7 @@ function listShards() {
1162
1229
  }
1163
1230
  async function ensureShardSchema(client) {
1164
1231
  await client.execute("PRAGMA journal_mode = WAL");
1165
- await client.execute("PRAGMA busy_timeout = 5000");
1232
+ await client.execute("PRAGMA busy_timeout = 30000");
1166
1233
  try {
1167
1234
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
1168
1235
  } catch {