@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
@@ -22,12 +22,68 @@ var init_memory = __esm({
22
22
  }
23
23
  });
24
24
 
25
+ // src/lib/db-retry.ts
26
+ function isBusyError(err) {
27
+ if (err instanceof Error) {
28
+ const msg = err.message.toLowerCase();
29
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
30
+ }
31
+ return false;
32
+ }
33
+ function delay(ms) {
34
+ return new Promise((resolve) => setTimeout(resolve, ms));
35
+ }
36
+ async function retryOnBusy(fn, label) {
37
+ let lastError;
38
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
39
+ try {
40
+ return await fn();
41
+ } catch (err) {
42
+ lastError = err;
43
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
44
+ throw err;
45
+ }
46
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
47
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
48
+ process.stderr.write(
49
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
50
+ `
51
+ );
52
+ await delay(backoff + jitter);
53
+ }
54
+ }
55
+ throw lastError;
56
+ }
57
+ function wrapWithRetry(client) {
58
+ return new Proxy(client, {
59
+ get(target, prop, receiver) {
60
+ if (prop === "execute") {
61
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
62
+ }
63
+ if (prop === "batch") {
64
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
65
+ }
66
+ return Reflect.get(target, prop, receiver);
67
+ }
68
+ });
69
+ }
70
+ var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
71
+ var init_db_retry = __esm({
72
+ "src/lib/db-retry.ts"() {
73
+ "use strict";
74
+ MAX_RETRIES = 3;
75
+ BASE_DELAY_MS = 200;
76
+ MAX_JITTER_MS = 300;
77
+ }
78
+ });
79
+
25
80
  // src/lib/database.ts
26
81
  import { createClient } from "@libsql/client";
27
82
  async function initDatabase(config) {
28
83
  if (_client) {
29
84
  _client.close();
30
85
  _client = null;
86
+ _resilientClient = null;
31
87
  }
32
88
  const opts = {
33
89
  url: `file:${config.dbPath}`
@@ -36,17 +92,24 @@ async function initDatabase(config) {
36
92
  opts.encryptionKey = config.encryptionKey;
37
93
  }
38
94
  _client = createClient(opts);
95
+ _resilientClient = wrapWithRetry(_client);
39
96
  }
40
97
  function getClient() {
98
+ if (!_resilientClient) {
99
+ throw new Error("Database client not initialized. Call initDatabase() first.");
100
+ }
101
+ return _resilientClient;
102
+ }
103
+ function getRawClient() {
41
104
  if (!_client) {
42
105
  throw new Error("Database client not initialized. Call initDatabase() first.");
43
106
  }
44
107
  return _client;
45
108
  }
46
109
  async function ensureSchema() {
47
- const client = getClient();
110
+ const client = getRawClient();
48
111
  await client.execute("PRAGMA journal_mode = WAL");
49
- await client.execute("PRAGMA busy_timeout = 5000");
112
+ await client.execute("PRAGMA busy_timeout = 30000");
50
113
  try {
51
114
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
52
115
  } catch {
@@ -835,11 +898,13 @@ async function ensureSchema() {
835
898
  }
836
899
  }
837
900
  }
838
- var _client, initTurso;
901
+ var _client, _resilientClient, initTurso;
839
902
  var init_database = __esm({
840
903
  "src/lib/database.ts"() {
841
904
  "use strict";
905
+ init_db_retry();
842
906
  _client = null;
907
+ _resilientClient = null;
843
908
  initTurso = initDatabase;
844
909
  }
845
910
  });
@@ -1140,7 +1205,7 @@ function listShards() {
1140
1205
  }
1141
1206
  async function ensureShardSchema(client) {
1142
1207
  await client.execute("PRAGMA journal_mode = WAL");
1143
- await client.execute("PRAGMA busy_timeout = 5000");
1208
+ await client.execute("PRAGMA busy_timeout = 30000");
1144
1209
  try {
1145
1210
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
1146
1211
  } catch {
@@ -281,12 +281,68 @@ var init_crm_bridge = __esm({
281
281
  }
282
282
  });
283
283
 
284
+ // src/lib/db-retry.ts
285
+ function isBusyError(err) {
286
+ if (err instanceof Error) {
287
+ const msg = err.message.toLowerCase();
288
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
289
+ }
290
+ return false;
291
+ }
292
+ function delay(ms) {
293
+ return new Promise((resolve) => setTimeout(resolve, ms));
294
+ }
295
+ async function retryOnBusy(fn, label) {
296
+ let lastError;
297
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
298
+ try {
299
+ return await fn();
300
+ } catch (err) {
301
+ lastError = err;
302
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
303
+ throw err;
304
+ }
305
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
306
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
307
+ process.stderr.write(
308
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
309
+ `
310
+ );
311
+ await delay(backoff + jitter);
312
+ }
313
+ }
314
+ throw lastError;
315
+ }
316
+ function wrapWithRetry(client) {
317
+ return new Proxy(client, {
318
+ get(target, prop, receiver) {
319
+ if (prop === "execute") {
320
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
321
+ }
322
+ if (prop === "batch") {
323
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
324
+ }
325
+ return Reflect.get(target, prop, receiver);
326
+ }
327
+ });
328
+ }
329
+ var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
330
+ var init_db_retry = __esm({
331
+ "src/lib/db-retry.ts"() {
332
+ "use strict";
333
+ MAX_RETRIES = 3;
334
+ BASE_DELAY_MS = 200;
335
+ MAX_JITTER_MS = 300;
336
+ }
337
+ });
338
+
284
339
  // src/lib/database.ts
285
340
  import { createClient } from "@libsql/client";
286
341
  async function initDatabase(config2) {
287
342
  if (_client) {
288
343
  _client.close();
289
344
  _client = null;
345
+ _resilientClient = null;
290
346
  }
291
347
  const opts = {
292
348
  url: `file:${config2.dbPath}`
@@ -295,17 +351,24 @@ async function initDatabase(config2) {
295
351
  opts.encryptionKey = config2.encryptionKey;
296
352
  }
297
353
  _client = createClient(opts);
354
+ _resilientClient = wrapWithRetry(_client);
298
355
  }
299
356
  function getClient() {
357
+ if (!_resilientClient) {
358
+ throw new Error("Database client not initialized. Call initDatabase() first.");
359
+ }
360
+ return _resilientClient;
361
+ }
362
+ function getRawClient() {
300
363
  if (!_client) {
301
364
  throw new Error("Database client not initialized. Call initDatabase() first.");
302
365
  }
303
366
  return _client;
304
367
  }
305
368
  async function ensureSchema() {
306
- const client = getClient();
369
+ const client = getRawClient();
307
370
  await client.execute("PRAGMA journal_mode = WAL");
308
- await client.execute("PRAGMA busy_timeout = 5000");
371
+ await client.execute("PRAGMA busy_timeout = 30000");
309
372
  try {
310
373
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
311
374
  } catch {
@@ -1098,13 +1161,16 @@ async function disposeDatabase() {
1098
1161
  if (_client) {
1099
1162
  _client.close();
1100
1163
  _client = null;
1164
+ _resilientClient = null;
1101
1165
  }
1102
1166
  }
1103
- var _client, initTurso, disposeTurso;
1167
+ var _client, _resilientClient, initTurso, disposeTurso;
1104
1168
  var init_database = __esm({
1105
1169
  "src/lib/database.ts"() {
1106
1170
  "use strict";
1171
+ init_db_retry();
1107
1172
  _client = null;
1173
+ _resilientClient = null;
1108
1174
  initTurso = initDatabase;
1109
1175
  disposeTurso = disposeDatabase;
1110
1176
  }
@@ -1539,11 +1605,11 @@ async function connectEmbedDaemon() {
1539
1605
  }
1540
1606
  }
1541
1607
  const start = Date.now();
1542
- let delay = 100;
1608
+ let delay2 = 100;
1543
1609
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1544
- await new Promise((r) => setTimeout(r, delay));
1610
+ await new Promise((r) => setTimeout(r, delay2));
1545
1611
  if (await connectToSocket()) return true;
1546
- delay = Math.min(delay * 2, 3e3);
1612
+ delay2 = Math.min(delay2 * 2, 3e3);
1547
1613
  }
1548
1614
  return false;
1549
1615
  }
@@ -1635,11 +1701,11 @@ async function embedViaClient(text, priority = "high") {
1635
1701
  `);
1636
1702
  killAndRespawnDaemon();
1637
1703
  const start = Date.now();
1638
- let delay = 200;
1704
+ let delay2 = 200;
1639
1705
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1640
- await new Promise((r) => setTimeout(r, delay));
1706
+ await new Promise((r) => setTimeout(r, delay2));
1641
1707
  if (await connectToSocket()) break;
1642
- delay = Math.min(delay * 2, 3e3);
1708
+ delay2 = Math.min(delay2 * 2, 3e3);
1643
1709
  }
1644
1710
  if (!_connected) return null;
1645
1711
  }
@@ -1651,11 +1717,11 @@ async function embedViaClient(text, priority = "high") {
1651
1717
  `);
1652
1718
  killAndRespawnDaemon();
1653
1719
  const start = Date.now();
1654
- let delay = 200;
1720
+ let delay2 = 200;
1655
1721
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1656
- await new Promise((r) => setTimeout(r, delay));
1722
+ await new Promise((r) => setTimeout(r, delay2));
1657
1723
  if (await connectToSocket()) break;
1658
- delay = Math.min(delay * 2, 3e3);
1724
+ delay2 = Math.min(delay2 * 2, 3e3);
1659
1725
  }
1660
1726
  if (!_connected) return null;
1661
1727
  const retry = await sendRequest([text], priority);
@@ -1873,7 +1939,7 @@ function listShards() {
1873
1939
  }
1874
1940
  async function ensureShardSchema(client) {
1875
1941
  await client.execute("PRAGMA journal_mode = WAL");
1876
- await client.execute("PRAGMA busy_timeout = 5000");
1942
+ await client.execute("PRAGMA busy_timeout = 30000");
1877
1943
  try {
1878
1944
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
1879
1945
  } catch {
@@ -4510,25 +4576,43 @@ var init_intercom_queue = __esm({
4510
4576
 
4511
4577
  // src/lib/employees.ts
4512
4578
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
4513
- import { existsSync as existsSync8, symlinkSync, readlinkSync } from "fs";
4579
+ import { existsSync as existsSync8, symlinkSync, readlinkSync, readFileSync as readFileSync7 } from "fs";
4514
4580
  import { execSync as execSync3 } from "child_process";
4515
4581
  import path9 from "path";
4516
- var EMPLOYEES_PATH;
4582
+ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
4583
+ if (!existsSync8(employeesPath)) return [];
4584
+ try {
4585
+ return JSON.parse(readFileSync7(employeesPath, "utf-8"));
4586
+ } catch {
4587
+ return [];
4588
+ }
4589
+ }
4590
+ function getEmployee(employees, name) {
4591
+ return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
4592
+ }
4593
+ function isMultiInstance(agentName, employees) {
4594
+ const roster = employees ?? loadEmployeesSync();
4595
+ const emp = getEmployee(roster, agentName);
4596
+ if (!emp) return false;
4597
+ return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
4598
+ }
4599
+ var EMPLOYEES_PATH, MULTI_INSTANCE_ROLES;
4517
4600
  var init_employees = __esm({
4518
4601
  "src/lib/employees.ts"() {
4519
4602
  "use strict";
4520
4603
  init_config();
4521
4604
  EMPLOYEES_PATH = path9.join(EXE_AI_DIR, "exe-employees.json");
4605
+ MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
4522
4606
  }
4523
4607
  });
4524
4608
 
4525
4609
  // src/lib/plan-limits.ts
4526
- import { readFileSync as readFileSync7, existsSync as existsSync9 } from "fs";
4610
+ import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
4527
4611
  import path10 from "path";
4528
4612
  function getLicenseSync() {
4529
4613
  try {
4530
4614
  if (!existsSync9(CACHE_PATH2)) return freeLicense();
4531
- const raw = JSON.parse(readFileSync7(CACHE_PATH2, "utf8"));
4615
+ const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
4532
4616
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
4533
4617
  const parts = raw.token.split(".");
4534
4618
  if (parts.length !== 3) return freeLicense();
@@ -4567,7 +4651,7 @@ function assertEmployeeLimitSync(rosterPath) {
4567
4651
  let count = 0;
4568
4652
  try {
4569
4653
  if (existsSync9(filePath)) {
4570
- const raw = readFileSync7(filePath, "utf8");
4654
+ const raw = readFileSync8(filePath, "utf8");
4571
4655
  const employees = JSON.parse(raw);
4572
4656
  count = Array.isArray(employees) ? employees.length : 0;
4573
4657
  }
@@ -4602,7 +4686,7 @@ var init_plan_limits = __esm({
4602
4686
 
4603
4687
  // src/lib/tmux-routing.ts
4604
4688
  import { execFileSync as execFileSync2, execSync as execSync4 } from "child_process";
4605
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync4, mkdirSync as mkdirSync6, existsSync as existsSync10, appendFileSync } from "fs";
4689
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, mkdirSync as mkdirSync6, existsSync as existsSync10, appendFileSync } from "fs";
4606
4690
  import path11 from "path";
4607
4691
  import os4 from "os";
4608
4692
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -4651,7 +4735,7 @@ function extractRootExe(name) {
4651
4735
  }
4652
4736
  function getParentExe(sessionKey) {
4653
4737
  try {
4654
- const data = JSON.parse(readFileSync8(path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4738
+ const data = JSON.parse(readFileSync9(path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4655
4739
  return data.parentExe || null;
4656
4740
  } catch {
4657
4741
  return null;
@@ -4659,7 +4743,7 @@ function getParentExe(sessionKey) {
4659
4743
  }
4660
4744
  function getDispatchedBy(sessionKey) {
4661
4745
  try {
4662
- const data = JSON.parse(readFileSync8(
4746
+ const data = JSON.parse(readFileSync9(
4663
4747
  path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
4664
4748
  "utf8"
4665
4749
  ));
@@ -4696,7 +4780,7 @@ function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive =
4696
4780
  function readDebounceState() {
4697
4781
  try {
4698
4782
  if (!existsSync10(DEBOUNCE_FILE)) return {};
4699
- return JSON.parse(readFileSync8(DEBOUNCE_FILE, "utf8"));
4783
+ return JSON.parse(readFileSync9(DEBOUNCE_FILE, "utf8"));
4700
4784
  } catch {
4701
4785
  return {};
4702
4786
  }
@@ -4892,7 +4976,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4892
4976
  const claudeJsonPath = path11.join(os4.homedir(), ".claude.json");
4893
4977
  let claudeJson = {};
4894
4978
  try {
4895
- claudeJson = JSON.parse(readFileSync8(claudeJsonPath, "utf8"));
4979
+ claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
4896
4980
  } catch {
4897
4981
  }
4898
4982
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -4910,7 +4994,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4910
4994
  const settingsPath = path11.join(projSettingsDir, "settings.json");
4911
4995
  let settings = {};
4912
4996
  try {
4913
- settings = JSON.parse(readFileSync8(settingsPath, "utf8"));
4997
+ settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
4914
4998
  } catch {
4915
4999
  }
4916
5000
  const perms = settings.permissions ?? {};
@@ -5237,7 +5321,7 @@ async function deliverLocalMessage(messageId) {
5237
5321
  return true;
5238
5322
  } catch {
5239
5323
  const newRetryCount = msg.retryCount + 1;
5240
- if (newRetryCount >= MAX_RETRIES) {
5324
+ if (newRetryCount >= MAX_RETRIES2) {
5241
5325
  await markFailed(messageId, "session unavailable after 10 retries");
5242
5326
  } else {
5243
5327
  await client.execute({
@@ -5326,7 +5410,7 @@ async function retryPendingMessages() {
5326
5410
  sql: `SELECT * FROM messages
5327
5411
  WHERE status = 'pending' AND retry_count < ?
5328
5412
  ORDER BY id`,
5329
- args: [MAX_RETRIES]
5413
+ args: [MAX_RETRIES2]
5330
5414
  });
5331
5415
  let delivered = 0;
5332
5416
  for (const row of result.rows) {
@@ -5339,13 +5423,13 @@ async function retryPendingMessages() {
5339
5423
  }
5340
5424
  return delivered;
5341
5425
  }
5342
- var MAX_RETRIES, _wsClientSend;
5426
+ var MAX_RETRIES2, _wsClientSend;
5343
5427
  var init_messaging = __esm({
5344
5428
  "src/lib/messaging.ts"() {
5345
5429
  "use strict";
5346
5430
  init_database();
5347
5431
  init_tmux_routing();
5348
- MAX_RETRIES = 10;
5432
+ MAX_RETRIES2 = 10;
5349
5433
  _wsClientSend = null;
5350
5434
  }
5351
5435
  });
@@ -5355,7 +5439,7 @@ import crypto4 from "crypto";
5355
5439
  import path12 from "path";
5356
5440
  import os5 from "os";
5357
5441
  import {
5358
- readFileSync as readFileSync9,
5442
+ readFileSync as readFileSync10,
5359
5443
  readdirSync,
5360
5444
  unlinkSync as unlinkSync2,
5361
5445
  existsSync as existsSync11,
@@ -5407,7 +5491,7 @@ import crypto5 from "crypto";
5407
5491
  import path13 from "path";
5408
5492
  import { execSync as execSync5 } from "child_process";
5409
5493
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
5410
- import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
5494
+ import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
5411
5495
  async function writeCheckpoint(input) {
5412
5496
  const client = getClient();
5413
5497
  const row = await resolveTask(client, input.taskId);
@@ -5784,7 +5868,7 @@ async function ensureGitignoreExe(baseDir) {
5784
5868
  const gitignorePath = path13.join(baseDir, ".gitignore");
5785
5869
  try {
5786
5870
  if (existsSync12(gitignorePath)) {
5787
- const content = readFileSync10(gitignorePath, "utf-8");
5871
+ const content = readFileSync11(gitignorePath, "utf-8");
5788
5872
  if (/^\/?exe\/?$/m.test(content)) return;
5789
5873
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
5790
5874
  } else {
@@ -6160,7 +6244,7 @@ async function dispatchTaskToEmployee(input) {
6160
6244
  } else {
6161
6245
  const projectDir = input.projectDir ?? process.cwd();
6162
6246
  const result = ensureEmployee(input.assignedTo, exeSession, projectDir, {
6163
- autoInstance: input.assignedTo === "tom" || input.assignedTo === "sasha"
6247
+ autoInstance: isMultiInstance(input.assignedTo)
6164
6248
  });
6165
6249
  if (result.status === "failed") {
6166
6250
  process.stderr.write(
@@ -6195,6 +6279,7 @@ var init_tasks_notify = __esm({
6195
6279
  init_session_key();
6196
6280
  init_notifications();
6197
6281
  init_transport();
6282
+ init_employees();
6198
6283
  }
6199
6284
  });
6200
6285
 
@@ -6666,7 +6751,7 @@ var init_tasks = __esm({
6666
6751
  });
6667
6752
 
6668
6753
  // src/automation/trigger-engine.ts
6669
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, existsSync as existsSync14, mkdirSync as mkdirSync8 } from "fs";
6754
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync6, existsSync as existsSync14, mkdirSync as mkdirSync8 } from "fs";
6670
6755
  import { randomUUID as randomUUID7 } from "crypto";
6671
6756
  import path18 from "path";
6672
6757
  import os6 from "os";
@@ -6724,7 +6809,7 @@ function evaluateConditions(conditions, record) {
6724
6809
  function loadTriggers(project) {
6725
6810
  if (!existsSync14(TRIGGERS_PATH)) return [];
6726
6811
  try {
6727
- const raw = readFileSync11(TRIGGERS_PATH, "utf-8");
6812
+ const raw = readFileSync12(TRIGGERS_PATH, "utf-8");
6728
6813
  const all = JSON.parse(raw);
6729
6814
  if (!Array.isArray(all)) return [];
6730
6815
  if (project) {
@@ -7020,7 +7105,7 @@ var init_crm_webhook = __esm({
7020
7105
  });
7021
7106
 
7022
7107
  // src/bin/exe-gateway.ts
7023
- import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
7108
+ import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
7024
7109
  import path19 from "path";
7025
7110
  import os7 from "os";
7026
7111
 
@@ -7857,7 +7942,7 @@ function loadConfig2() {
7857
7942
  return {};
7858
7943
  }
7859
7944
  try {
7860
- const raw = readFileSync12(CONFIG_PATH3, "utf-8");
7945
+ const raw = readFileSync13(CONFIG_PATH3, "utf-8");
7861
7946
  return JSON.parse(raw);
7862
7947
  } catch (err) {
7863
7948
  console.error(
@@ -15,12 +15,68 @@ 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
  import { createClient } from "@libsql/client";
20
75
  async function initDatabase(config) {
21
76
  if (_client) {
22
77
  _client.close();
23
78
  _client = null;
79
+ _resilientClient = null;
24
80
  }
25
81
  const opts = {
26
82
  url: `file:${config.dbPath}`
@@ -29,17 +85,24 @@ async function initDatabase(config) {
29
85
  opts.encryptionKey = config.encryptionKey;
30
86
  }
31
87
  _client = createClient(opts);
88
+ _resilientClient = wrapWithRetry(_client);
32
89
  }
33
90
  function getClient() {
91
+ if (!_resilientClient) {
92
+ throw new Error("Database client not initialized. Call initDatabase() first.");
93
+ }
94
+ return _resilientClient;
95
+ }
96
+ function getRawClient() {
34
97
  if (!_client) {
35
98
  throw new Error("Database client not initialized. Call initDatabase() first.");
36
99
  }
37
100
  return _client;
38
101
  }
39
102
  async function ensureSchema() {
40
- const client = getClient();
103
+ const client = getRawClient();
41
104
  await client.execute("PRAGMA journal_mode = WAL");
42
- await client.execute("PRAGMA busy_timeout = 5000");
105
+ await client.execute("PRAGMA busy_timeout = 30000");
43
106
  try {
44
107
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
45
108
  } catch {
@@ -828,11 +891,13 @@ async function ensureSchema() {
828
891
  }
829
892
  }
830
893
  }
831
- var _client, initTurso;
894
+ var _client, _resilientClient, initTurso;
832
895
  var init_database = __esm({
833
896
  "src/lib/database.ts"() {
834
897
  "use strict";
898
+ init_db_retry();
835
899
  _client = null;
900
+ _resilientClient = null;
836
901
  initTurso = initDatabase;
837
902
  }
838
903
  });
@@ -1103,7 +1168,7 @@ function listShards() {
1103
1168
  }
1104
1169
  async function ensureShardSchema(client) {
1105
1170
  await client.execute("PRAGMA journal_mode = WAL");
1106
- await client.execute("PRAGMA busy_timeout = 5000");
1171
+ await client.execute("PRAGMA busy_timeout = 30000");
1107
1172
  try {
1108
1173
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
1109
1174
  } catch {
@@ -1289,7 +1354,7 @@ var init_shard_manager = __esm({
1289
1354
 
1290
1355
  // src/lib/employees.ts
1291
1356
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
1292
- import { existsSync as existsSync4, symlinkSync, readlinkSync } from "fs";
1357
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync2 } from "fs";
1293
1358
  import { execSync } from "child_process";
1294
1359
  import path4 from "path";
1295
1360
  var EMPLOYEES_PATH;
@@ -1306,7 +1371,7 @@ import crypto2 from "crypto";
1306
1371
  import path5 from "path";
1307
1372
  import os2 from "os";
1308
1373
  import {
1309
- readFileSync as readFileSync2,
1374
+ readFileSync as readFileSync3,
1310
1375
  readdirSync,
1311
1376
  unlinkSync,
1312
1377
  existsSync as existsSync5,
@@ -1324,7 +1389,7 @@ import crypto3 from "crypto";
1324
1389
  import path6 from "path";
1325
1390
  import { execSync as execSync2 } from "child_process";
1326
1391
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
1327
- import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
1392
+ import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
1328
1393
  var init_tasks_crud = __esm({
1329
1394
  "src/lib/tasks-crud.ts"() {
1330
1395
  "use strict";
@@ -1388,7 +1453,7 @@ var init_provider_table = __esm({
1388
1453
  });
1389
1454
 
1390
1455
  // src/lib/intercom-queue.ts
1391
- import { readFileSync as readFileSync4, writeFileSync, renameSync as renameSync2, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
1456
+ import { readFileSync as readFileSync5, writeFileSync, renameSync as renameSync2, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
1392
1457
  import path8 from "path";
1393
1458
  import os4 from "os";
1394
1459
  var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
@@ -1402,7 +1467,7 @@ var init_intercom_queue = __esm({
1402
1467
  });
1403
1468
 
1404
1469
  // src/lib/license.ts
1405
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
1470
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
1406
1471
  import { randomUUID } from "crypto";
1407
1472
  import path9 from "path";
1408
1473
  import { jwtVerify, importSPKI } from "jose";
@@ -1418,7 +1483,7 @@ var init_license = __esm({
1418
1483
  });
1419
1484
 
1420
1485
  // src/lib/plan-limits.ts
1421
- import { readFileSync as readFileSync6, existsSync as existsSync9 } from "fs";
1486
+ import { readFileSync as readFileSync7, existsSync as existsSync9 } from "fs";
1422
1487
  import path10 from "path";
1423
1488
  var CACHE_PATH2;
1424
1489
  var init_plan_limits = __esm({
@@ -1483,7 +1548,7 @@ var init_tasks_review = __esm({
1483
1548
 
1484
1549
  // src/bin/exe-heartbeat.ts
1485
1550
  import { createHash } from "crypto";
1486
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "fs";
1551
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "fs";
1487
1552
  import os6 from "os";
1488
1553
  import path13 from "path";
1489
1554
 
@@ -1622,7 +1687,7 @@ function getMarkerPath() {
1622
1687
  var UNREAD_MESSAGE_STATUSES = ["pending", "delivered"];
1623
1688
  function readMarker() {
1624
1689
  try {
1625
- const raw = readFileSync7(getMarkerPath(), "utf8");
1690
+ const raw = readFileSync8(getMarkerPath(), "utf8");
1626
1691
  const parsed = JSON.parse(raw);
1627
1692
  if (typeof parsed.lastFiredAt !== "string" || typeof parsed.lastSurfaceHash !== "string") {
1628
1693
  return null;
@@ -1740,7 +1805,6 @@ async function runHeartbeat() {
1740
1805
  writeMarker({ lastFiredAt: new Date(now).toISOString(), lastSurfaceHash: hash });
1741
1806
  try {
1742
1807
  const client = getClient();
1743
- await client.execute({ sql: "PRAGMA busy_timeout = 5000", args: [] });
1744
1808
  await client.execute({
1745
1809
  sql: `UPDATE messages SET status = 'acknowledged', processed_at = datetime('now')
1746
1810
  WHERE target_agent = 'exe' AND status IN ('pending', 'delivered')`,