@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 {
@@ -452,7 +452,7 @@ import { randomUUID } from "crypto";
452
452
  // src/lib/employees.ts
453
453
  init_config();
454
454
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
455
- import { existsSync as existsSync2, symlinkSync, readlinkSync } from "fs";
455
+ import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2 } from "fs";
456
456
  import { execSync } from "child_process";
457
457
  import path2 from "path";
458
458
  var EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
@@ -476,12 +476,65 @@ var EMBEDDING_DIM = 1024;
476
476
 
477
477
  // src/lib/database.ts
478
478
  import { createClient } from "@libsql/client";
479
+
480
+ // src/lib/db-retry.ts
481
+ var MAX_RETRIES = 3;
482
+ var BASE_DELAY_MS = 200;
483
+ var MAX_JITTER_MS = 300;
484
+ function isBusyError(err) {
485
+ if (err instanceof Error) {
486
+ const msg = err.message.toLowerCase();
487
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
488
+ }
489
+ return false;
490
+ }
491
+ function delay(ms) {
492
+ return new Promise((resolve) => setTimeout(resolve, ms));
493
+ }
494
+ async function retryOnBusy(fn, label) {
495
+ let lastError;
496
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
497
+ try {
498
+ return await fn();
499
+ } catch (err) {
500
+ lastError = err;
501
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
502
+ throw err;
503
+ }
504
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
505
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
506
+ process.stderr.write(
507
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
508
+ `
509
+ );
510
+ await delay(backoff + jitter);
511
+ }
512
+ }
513
+ throw lastError;
514
+ }
515
+ function wrapWithRetry(client) {
516
+ return new Proxy(client, {
517
+ get(target, prop, receiver) {
518
+ if (prop === "execute") {
519
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
520
+ }
521
+ if (prop === "batch") {
522
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
523
+ }
524
+ return Reflect.get(target, prop, receiver);
525
+ }
526
+ });
527
+ }
528
+
529
+ // src/lib/database.ts
479
530
  var _client = null;
531
+ var _resilientClient = null;
480
532
  var initTurso = initDatabase;
481
533
  async function initDatabase(config) {
482
534
  if (_client) {
483
535
  _client.close();
484
536
  _client = null;
537
+ _resilientClient = null;
485
538
  }
486
539
  const opts = {
487
540
  url: `file:${config.dbPath}`
@@ -490,17 +543,24 @@ async function initDatabase(config) {
490
543
  opts.encryptionKey = config.encryptionKey;
491
544
  }
492
545
  _client = createClient(opts);
546
+ _resilientClient = wrapWithRetry(_client);
493
547
  }
494
548
  function getClient() {
549
+ if (!_resilientClient) {
550
+ throw new Error("Database client not initialized. Call initDatabase() first.");
551
+ }
552
+ return _resilientClient;
553
+ }
554
+ function getRawClient() {
495
555
  if (!_client) {
496
556
  throw new Error("Database client not initialized. Call initDatabase() first.");
497
557
  }
498
558
  return _client;
499
559
  }
500
560
  async function ensureSchema() {
501
- const client = getClient();
561
+ const client = getRawClient();
502
562
  await client.execute("PRAGMA journal_mode = WAL");
503
- await client.execute("PRAGMA busy_timeout = 5000");
563
+ await client.execute("PRAGMA busy_timeout = 30000");
504
564
  try {
505
565
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
506
566
  } catch {
@@ -24,12 +24,68 @@ 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
  import { createClient } from "@libsql/client";
29
84
  async function initDatabase(config) {
30
85
  if (_client) {
31
86
  _client.close();
32
87
  _client = null;
88
+ _resilientClient = null;
33
89
  }
34
90
  const opts = {
35
91
  url: `file:${config.dbPath}`
@@ -38,17 +94,24 @@ async function initDatabase(config) {
38
94
  opts.encryptionKey = config.encryptionKey;
39
95
  }
40
96
  _client = createClient(opts);
97
+ _resilientClient = wrapWithRetry(_client);
41
98
  }
42
99
  function getClient() {
100
+ if (!_resilientClient) {
101
+ throw new Error("Database client not initialized. Call initDatabase() first.");
102
+ }
103
+ return _resilientClient;
104
+ }
105
+ function getRawClient() {
43
106
  if (!_client) {
44
107
  throw new Error("Database client not initialized. Call initDatabase() first.");
45
108
  }
46
109
  return _client;
47
110
  }
48
111
  async function ensureSchema() {
49
- const client = getClient();
112
+ const client = getRawClient();
50
113
  await client.execute("PRAGMA journal_mode = WAL");
51
- await client.execute("PRAGMA busy_timeout = 5000");
114
+ await client.execute("PRAGMA busy_timeout = 30000");
52
115
  try {
53
116
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
54
117
  } catch {
@@ -841,13 +904,16 @@ async function disposeDatabase() {
841
904
  if (_client) {
842
905
  _client.close();
843
906
  _client = null;
907
+ _resilientClient = null;
844
908
  }
845
909
  }
846
- var _client, initTurso, disposeTurso;
910
+ var _client, _resilientClient, initTurso, disposeTurso;
847
911
  var init_database = __esm({
848
912
  "src/lib/database.ts"() {
849
913
  "use strict";
914
+ init_db_retry();
850
915
  _client = null;
916
+ _resilientClient = null;
851
917
  initTurso = initDatabase;
852
918
  disposeTurso = disposeDatabase;
853
919
  }
@@ -1203,7 +1269,7 @@ function listShards() {
1203
1269
  }
1204
1270
  async function ensureShardSchema(client) {
1205
1271
  await client.execute("PRAGMA journal_mode = WAL");
1206
- await client.execute("PRAGMA busy_timeout = 5000");
1272
+ await client.execute("PRAGMA busy_timeout = 30000");
1207
1273
  try {
1208
1274
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
1209
1275
  } catch {
@@ -2088,11 +2154,11 @@ async function connectEmbedDaemon() {
2088
2154
  }
2089
2155
  }
2090
2156
  const start = Date.now();
2091
- let delay = 100;
2157
+ let delay2 = 100;
2092
2158
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
2093
- await new Promise((r) => setTimeout(r, delay));
2159
+ await new Promise((r) => setTimeout(r, delay2));
2094
2160
  if (await connectToSocket()) return true;
2095
- delay = Math.min(delay * 2, 3e3);
2161
+ delay2 = Math.min(delay2 * 2, 3e3);
2096
2162
  }
2097
2163
  return false;
2098
2164
  }
@@ -2184,11 +2250,11 @@ async function embedViaClient(text, priority = "high") {
2184
2250
  `);
2185
2251
  killAndRespawnDaemon();
2186
2252
  const start = Date.now();
2187
- let delay = 200;
2253
+ let delay2 = 200;
2188
2254
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
2189
- await new Promise((r) => setTimeout(r, delay));
2255
+ await new Promise((r) => setTimeout(r, delay2));
2190
2256
  if (await connectToSocket()) break;
2191
- delay = Math.min(delay * 2, 3e3);
2257
+ delay2 = Math.min(delay2 * 2, 3e3);
2192
2258
  }
2193
2259
  if (!_connected) return null;
2194
2260
  }
@@ -2200,11 +2266,11 @@ async function embedViaClient(text, priority = "high") {
2200
2266
  `);
2201
2267
  killAndRespawnDaemon();
2202
2268
  const start = Date.now();
2203
- let delay = 200;
2269
+ let delay2 = 200;
2204
2270
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
2205
- await new Promise((r) => setTimeout(r, delay));
2271
+ await new Promise((r) => setTimeout(r, delay2));
2206
2272
  if (await connectToSocket()) break;
2207
- delay = Math.min(delay * 2, 3e3);
2273
+ delay2 = Math.min(delay2 * 2, 3e3);
2208
2274
  }
2209
2275
  if (!_connected) return null;
2210
2276
  const retry = await sendRequest([text], priority);
@@ -35,12 +35,68 @@ var init_memory = __esm({
35
35
  }
36
36
  });
37
37
 
38
+ // src/lib/db-retry.ts
39
+ function isBusyError(err) {
40
+ if (err instanceof Error) {
41
+ const msg = err.message.toLowerCase();
42
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
43
+ }
44
+ return false;
45
+ }
46
+ function delay(ms) {
47
+ return new Promise((resolve) => setTimeout(resolve, ms));
48
+ }
49
+ async function retryOnBusy(fn, label) {
50
+ let lastError;
51
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
52
+ try {
53
+ return await fn();
54
+ } catch (err) {
55
+ lastError = err;
56
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
57
+ throw err;
58
+ }
59
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
60
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
61
+ process.stderr.write(
62
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
63
+ `
64
+ );
65
+ await delay(backoff + jitter);
66
+ }
67
+ }
68
+ throw lastError;
69
+ }
70
+ function wrapWithRetry(client) {
71
+ return new Proxy(client, {
72
+ get(target, prop, receiver) {
73
+ if (prop === "execute") {
74
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
75
+ }
76
+ if (prop === "batch") {
77
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
78
+ }
79
+ return Reflect.get(target, prop, receiver);
80
+ }
81
+ });
82
+ }
83
+ var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
84
+ var init_db_retry = __esm({
85
+ "src/lib/db-retry.ts"() {
86
+ "use strict";
87
+ MAX_RETRIES = 3;
88
+ BASE_DELAY_MS = 200;
89
+ MAX_JITTER_MS = 300;
90
+ }
91
+ });
92
+
38
93
  // src/lib/database.ts
39
94
  import { createClient } from "@libsql/client";
40
95
  async function initDatabase(config) {
41
96
  if (_client) {
42
97
  _client.close();
43
98
  _client = null;
99
+ _resilientClient = null;
44
100
  }
45
101
  const opts = {
46
102
  url: `file:${config.dbPath}`
@@ -49,17 +105,24 @@ async function initDatabase(config) {
49
105
  opts.encryptionKey = config.encryptionKey;
50
106
  }
51
107
  _client = createClient(opts);
108
+ _resilientClient = wrapWithRetry(_client);
52
109
  }
53
110
  function getClient() {
111
+ if (!_resilientClient) {
112
+ throw new Error("Database client not initialized. Call initDatabase() first.");
113
+ }
114
+ return _resilientClient;
115
+ }
116
+ function getRawClient() {
54
117
  if (!_client) {
55
118
  throw new Error("Database client not initialized. Call initDatabase() first.");
56
119
  }
57
120
  return _client;
58
121
  }
59
122
  async function ensureSchema() {
60
- const client = getClient();
123
+ const client = getRawClient();
61
124
  await client.execute("PRAGMA journal_mode = WAL");
62
- await client.execute("PRAGMA busy_timeout = 5000");
125
+ await client.execute("PRAGMA busy_timeout = 30000");
63
126
  try {
64
127
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
65
128
  } catch {
@@ -848,11 +911,13 @@ async function ensureSchema() {
848
911
  }
849
912
  }
850
913
  }
851
- var _client, initTurso;
914
+ var _client, _resilientClient, initTurso;
852
915
  var init_database = __esm({
853
916
  "src/lib/database.ts"() {
854
917
  "use strict";
918
+ init_db_retry();
855
919
  _client = null;
920
+ _resilientClient = null;
856
921
  initTurso = initDatabase;
857
922
  }
858
923
  });
@@ -1158,7 +1223,7 @@ function listShards() {
1158
1223
  }
1159
1224
  async function ensureShardSchema(client) {
1160
1225
  await client.execute("PRAGMA journal_mode = WAL");
1161
- await client.execute("PRAGMA busy_timeout = 5000");
1226
+ await client.execute("PRAGMA busy_timeout = 30000");
1162
1227
  try {
1163
1228
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
1164
1229
  } catch {
@@ -1561,11 +1626,11 @@ async function connectEmbedDaemon() {
1561
1626
  }
1562
1627
  }
1563
1628
  const start = Date.now();
1564
- let delay = 100;
1629
+ let delay2 = 100;
1565
1630
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1566
- await new Promise((r) => setTimeout(r, delay));
1631
+ await new Promise((r) => setTimeout(r, delay2));
1567
1632
  if (await connectToSocket()) return true;
1568
- delay = Math.min(delay * 2, 3e3);
1633
+ delay2 = Math.min(delay2 * 2, 3e3);
1569
1634
  }
1570
1635
  return false;
1571
1636
  }
@@ -1657,11 +1722,11 @@ async function embedViaClient(text, priority = "high") {
1657
1722
  `);
1658
1723
  killAndRespawnDaemon();
1659
1724
  const start = Date.now();
1660
- let delay = 200;
1725
+ let delay2 = 200;
1661
1726
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1662
- await new Promise((r) => setTimeout(r, delay));
1727
+ await new Promise((r) => setTimeout(r, delay2));
1663
1728
  if (await connectToSocket()) break;
1664
- delay = Math.min(delay * 2, 3e3);
1729
+ delay2 = Math.min(delay2 * 2, 3e3);
1665
1730
  }
1666
1731
  if (!_connected) return null;
1667
1732
  }
@@ -1673,11 +1738,11 @@ async function embedViaClient(text, priority = "high") {
1673
1738
  `);
1674
1739
  killAndRespawnDaemon();
1675
1740
  const start = Date.now();
1676
- let delay = 200;
1741
+ let delay2 = 200;
1677
1742
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1678
- await new Promise((r) => setTimeout(r, delay));
1743
+ await new Promise((r) => setTimeout(r, delay2));
1679
1744
  if (await connectToSocket()) break;
1680
- delay = Math.min(delay * 2, 3e3);
1745
+ delay2 = Math.min(delay2 * 2, 3e3);
1681
1746
  }
1682
1747
  if (!_connected) return null;
1683
1748
  const retry = await sendRequest([text], priority);
@@ -1790,7 +1855,7 @@ var init_embedder = __esm({
1790
1855
 
1791
1856
  // src/lib/employees.ts
1792
1857
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
1793
- import { existsSync as existsSync5, symlinkSync, readlinkSync } from "fs";
1858
+ import { existsSync as existsSync5, symlinkSync, readlinkSync, readFileSync as readFileSync3 } from "fs";
1794
1859
  import { execSync as execSync2 } from "child_process";
1795
1860
  import path6 from "path";
1796
1861
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
@@ -1821,7 +1886,7 @@ import crypto2 from "crypto";
1821
1886
  import path7 from "path";
1822
1887
  import os2 from "os";
1823
1888
  import {
1824
- readFileSync as readFileSync3,
1889
+ readFileSync as readFileSync4,
1825
1890
  readdirSync,
1826
1891
  unlinkSync as unlinkSync2,
1827
1892
  existsSync as existsSync6,
@@ -1863,7 +1928,7 @@ import crypto3 from "crypto";
1863
1928
  import path8 from "path";
1864
1929
  import { execSync as execSync3 } from "child_process";
1865
1930
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
1866
- import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
1931
+ import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
1867
1932
  function extractParentFromContext(contextBody) {
1868
1933
  if (!contextBody) return null;
1869
1934
  const match = contextBody.match(
@@ -2053,7 +2118,7 @@ async function ensureGitignoreExe(baseDir) {
2053
2118
  const gitignorePath = path8.join(baseDir, ".gitignore");
2054
2119
  try {
2055
2120
  if (existsSync7(gitignorePath)) {
2056
- const content = readFileSync4(gitignorePath, "utf-8");
2121
+ const content = readFileSync5(gitignorePath, "utf-8");
2057
2122
  if (/^\/?exe\/?$/m.test(content)) return;
2058
2123
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
2059
2124
  } else {
@@ -2252,7 +2317,7 @@ var init_provider_table = __esm({
2252
2317
  });
2253
2318
 
2254
2319
  // src/lib/intercom-queue.ts
2255
- import { readFileSync as readFileSync5, writeFileSync, renameSync as renameSync2, existsSync as existsSync8, mkdirSync as mkdirSync2 } from "fs";
2320
+ import { readFileSync as readFileSync6, writeFileSync, renameSync as renameSync2, existsSync as existsSync8, mkdirSync as mkdirSync2 } from "fs";
2256
2321
  import path10 from "path";
2257
2322
  import os4 from "os";
2258
2323
  function ensureDir() {
@@ -2262,7 +2327,7 @@ function ensureDir() {
2262
2327
  function readQueue() {
2263
2328
  try {
2264
2329
  if (!existsSync8(QUEUE_PATH)) return [];
2265
- return JSON.parse(readFileSync5(QUEUE_PATH, "utf8"));
2330
+ return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
2266
2331
  } catch {
2267
2332
  return [];
2268
2333
  }
@@ -2301,7 +2366,7 @@ var init_intercom_queue = __esm({
2301
2366
  });
2302
2367
 
2303
2368
  // src/lib/license.ts
2304
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync3 } from "fs";
2369
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync3 } from "fs";
2305
2370
  import { randomUUID as randomUUID2 } from "crypto";
2306
2371
  import path11 from "path";
2307
2372
  import { jwtVerify, importSPKI } from "jose";
@@ -2317,7 +2382,7 @@ var init_license = __esm({
2317
2382
  });
2318
2383
 
2319
2384
  // src/lib/plan-limits.ts
2320
- import { readFileSync as readFileSync7, existsSync as existsSync10 } from "fs";
2385
+ import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
2321
2386
  import path12 from "path";
2322
2387
  var CACHE_PATH2;
2323
2388
  var init_plan_limits = __esm({
@@ -2332,13 +2397,13 @@ var init_plan_limits = __esm({
2332
2397
  });
2333
2398
 
2334
2399
  // src/lib/tmux-routing.ts
2335
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync11, appendFileSync } from "fs";
2400
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync11, appendFileSync } from "fs";
2336
2401
  import path13 from "path";
2337
2402
  import os5 from "os";
2338
2403
  import { fileURLToPath as fileURLToPath2 } from "url";
2339
2404
  function getParentExe(sessionKey) {
2340
2405
  try {
2341
- const data = JSON.parse(readFileSync8(path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
2406
+ const data = JSON.parse(readFileSync9(path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
2342
2407
  return data.parentExe || null;
2343
2408
  } catch {
2344
2409
  return null;
@@ -2347,7 +2412,7 @@ function getParentExe(sessionKey) {
2347
2412
  function readDebounceState() {
2348
2413
  try {
2349
2414
  if (!existsSync11(DEBOUNCE_FILE)) return {};
2350
- return JSON.parse(readFileSync8(DEBOUNCE_FILE, "utf8"));
2415
+ return JSON.parse(readFileSync9(DEBOUNCE_FILE, "utf8"));
2351
2416
  } catch {
2352
2417
  return {};
2353
2418
  }
@@ -2723,7 +2788,7 @@ __export(worktree_exports, {
2723
2788
  worktreePath: () => worktreePath
2724
2789
  });
2725
2790
  import { execSync as execSync6 } from "child_process";
2726
- import { existsSync as existsSync13, readFileSync as readFileSync9, appendFileSync as appendFileSync2, mkdirSync as mkdirSync5, realpathSync } from "fs";
2791
+ import { existsSync as existsSync13, readFileSync as readFileSync10, appendFileSync as appendFileSync2, mkdirSync as mkdirSync5, realpathSync } from "fs";
2727
2792
  import path15 from "path";
2728
2793
  function getGitRoot(dir) {
2729
2794
  try {
@@ -2903,7 +2968,7 @@ function ensureGitignoreEntry(repoRoot, entry) {
2903
2968
  try {
2904
2969
  const gitignorePath = path15.join(repoRoot, ".gitignore");
2905
2970
  if (existsSync13(gitignorePath)) {
2906
- const content = readFileSync9(gitignorePath, "utf-8");
2971
+ const content = readFileSync10(gitignorePath, "utf-8");
2907
2972
  if (content.includes(entry)) return;
2908
2973
  appendFileSync2(gitignorePath, `
2909
2974
  # Agent worktrees (exe-os)
@@ -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 {