@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
@@ -277,6 +277,61 @@ var init_memory = __esm({
277
277
  }
278
278
  });
279
279
 
280
+ // src/lib/db-retry.ts
281
+ function isBusyError(err) {
282
+ if (err instanceof Error) {
283
+ const msg = err.message.toLowerCase();
284
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
285
+ }
286
+ return false;
287
+ }
288
+ function delay(ms) {
289
+ return new Promise((resolve) => setTimeout(resolve, ms));
290
+ }
291
+ async function retryOnBusy(fn, label) {
292
+ let lastError;
293
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
294
+ try {
295
+ return await fn();
296
+ } catch (err) {
297
+ lastError = err;
298
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
299
+ throw err;
300
+ }
301
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
302
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
303
+ process.stderr.write(
304
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
305
+ `
306
+ );
307
+ await delay(backoff + jitter);
308
+ }
309
+ }
310
+ throw lastError;
311
+ }
312
+ function wrapWithRetry(client) {
313
+ return new Proxy(client, {
314
+ get(target, prop, receiver) {
315
+ if (prop === "execute") {
316
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
317
+ }
318
+ if (prop === "batch") {
319
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
320
+ }
321
+ return Reflect.get(target, prop, receiver);
322
+ }
323
+ });
324
+ }
325
+ var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
326
+ var init_db_retry = __esm({
327
+ "src/lib/db-retry.ts"() {
328
+ "use strict";
329
+ MAX_RETRIES = 3;
330
+ BASE_DELAY_MS = 200;
331
+ MAX_JITTER_MS = 300;
332
+ }
333
+ });
334
+
280
335
  // src/lib/database.ts
281
336
  var database_exports = {};
282
337
  __export(database_exports, {
@@ -284,6 +339,7 @@ __export(database_exports, {
284
339
  disposeTurso: () => disposeTurso,
285
340
  ensureSchema: () => ensureSchema,
286
341
  getClient: () => getClient,
342
+ getRawClient: () => getRawClient,
287
343
  initDatabase: () => initDatabase,
288
344
  initTurso: () => initTurso,
289
345
  isInitialized: () => isInitialized
@@ -293,6 +349,7 @@ async function initDatabase(config) {
293
349
  if (_client) {
294
350
  _client.close();
295
351
  _client = null;
352
+ _resilientClient = null;
296
353
  }
297
354
  const opts = {
298
355
  url: `file:${config.dbPath}`
@@ -301,20 +358,27 @@ async function initDatabase(config) {
301
358
  opts.encryptionKey = config.encryptionKey;
302
359
  }
303
360
  _client = createClient(opts);
361
+ _resilientClient = wrapWithRetry(_client);
304
362
  }
305
363
  function isInitialized() {
306
364
  return _client !== null;
307
365
  }
308
366
  function getClient() {
367
+ if (!_resilientClient) {
368
+ throw new Error("Database client not initialized. Call initDatabase() first.");
369
+ }
370
+ return _resilientClient;
371
+ }
372
+ function getRawClient() {
309
373
  if (!_client) {
310
374
  throw new Error("Database client not initialized. Call initDatabase() first.");
311
375
  }
312
376
  return _client;
313
377
  }
314
378
  async function ensureSchema() {
315
- const client = getClient();
379
+ const client = getRawClient();
316
380
  await client.execute("PRAGMA journal_mode = WAL");
317
- await client.execute("PRAGMA busy_timeout = 5000");
381
+ await client.execute("PRAGMA busy_timeout = 30000");
318
382
  try {
319
383
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
320
384
  } catch {
@@ -1107,13 +1171,16 @@ async function disposeDatabase() {
1107
1171
  if (_client) {
1108
1172
  _client.close();
1109
1173
  _client = null;
1174
+ _resilientClient = null;
1110
1175
  }
1111
1176
  }
1112
- var _client, initTurso, disposeTurso;
1177
+ var _client, _resilientClient, initTurso, disposeTurso;
1113
1178
  var init_database = __esm({
1114
1179
  "src/lib/database.ts"() {
1115
1180
  "use strict";
1181
+ init_db_retry();
1116
1182
  _client = null;
1183
+ _resilientClient = null;
1117
1184
  initTurso = initDatabase;
1118
1185
  disposeTurso = disposeDatabase;
1119
1186
  }
@@ -1323,7 +1390,7 @@ function listShards() {
1323
1390
  }
1324
1391
  async function ensureShardSchema(client) {
1325
1392
  await client.execute("PRAGMA journal_mode = WAL");
1326
- await client.execute("PRAGMA busy_timeout = 5000");
1393
+ await client.execute("PRAGMA busy_timeout = 30000");
1327
1394
  try {
1328
1395
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
1329
1396
  } catch {
@@ -2296,8 +2363,8 @@ function drainQueue(isSessionBusy2, sendKeys) {
2296
2363
  } catch {
2297
2364
  }
2298
2365
  item.attempts++;
2299
- if (item.attempts >= MAX_RETRIES) {
2300
- logQueue(`FAILED \u2192 ${item.targetSession} (${MAX_RETRIES} retries exhausted, reason: ${item.reason})`);
2366
+ if (item.attempts >= MAX_RETRIES2) {
2367
+ logQueue(`FAILED \u2192 ${item.targetSession} (${MAX_RETRIES2} retries exhausted, reason: ${item.reason})`);
2301
2368
  failed++;
2302
2369
  continue;
2303
2370
  }
@@ -2330,12 +2397,12 @@ function logQueue(msg) {
2330
2397
  } catch {
2331
2398
  }
2332
2399
  }
2333
- var QUEUE_PATH, MAX_RETRIES, TTL_MS, INTERCOM_LOG;
2400
+ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
2334
2401
  var init_intercom_queue = __esm({
2335
2402
  "src/lib/intercom-queue.ts"() {
2336
2403
  "use strict";
2337
2404
  QUEUE_PATH = path5.join(os3.homedir(), ".exe-os", "intercom-queue.json");
2338
- MAX_RETRIES = 5;
2405
+ MAX_RETRIES2 = 5;
2339
2406
  TTL_MS = 60 * 60 * 1e3;
2340
2407
  INTERCOM_LOG = path5.join(os3.homedir(), ".exe-os", "intercom.log");
2341
2408
  }
@@ -2343,7 +2410,7 @@ var init_intercom_queue = __esm({
2343
2410
 
2344
2411
  // src/lib/employees.ts
2345
2412
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
2346
- import { existsSync as existsSync6, symlinkSync, readlinkSync } from "fs";
2413
+ import { existsSync as existsSync6, symlinkSync, readlinkSync, readFileSync as readFileSync4 } from "fs";
2347
2414
  import { execSync as execSync4 } from "child_process";
2348
2415
  import path6 from "path";
2349
2416
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
@@ -2357,20 +2424,35 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
2357
2424
  return [];
2358
2425
  }
2359
2426
  }
2427
+ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
2428
+ if (!existsSync6(employeesPath)) return [];
2429
+ try {
2430
+ return JSON.parse(readFileSync4(employeesPath, "utf-8"));
2431
+ } catch {
2432
+ return [];
2433
+ }
2434
+ }
2360
2435
  function getEmployee(employees, name) {
2361
2436
  return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
2362
2437
  }
2363
- var EMPLOYEES_PATH;
2438
+ function isMultiInstance(agentName, employees) {
2439
+ const roster = employees ?? loadEmployeesSync();
2440
+ const emp = getEmployee(roster, agentName);
2441
+ if (!emp) return false;
2442
+ return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
2443
+ }
2444
+ var EMPLOYEES_PATH, MULTI_INSTANCE_ROLES;
2364
2445
  var init_employees = __esm({
2365
2446
  "src/lib/employees.ts"() {
2366
2447
  "use strict";
2367
2448
  init_config();
2368
2449
  EMPLOYEES_PATH = path6.join(EXE_AI_DIR, "exe-employees.json");
2450
+ MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
2369
2451
  }
2370
2452
  });
2371
2453
 
2372
2454
  // src/lib/license.ts
2373
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
2455
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
2374
2456
  import { randomUUID } from "crypto";
2375
2457
  import path7 from "path";
2376
2458
  import { jwtVerify, importSPKI } from "jose";
@@ -2393,12 +2475,12 @@ var init_license = __esm({
2393
2475
  });
2394
2476
 
2395
2477
  // src/lib/plan-limits.ts
2396
- import { readFileSync as readFileSync5, existsSync as existsSync8 } from "fs";
2478
+ import { readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
2397
2479
  import path8 from "path";
2398
2480
  function getLicenseSync() {
2399
2481
  try {
2400
2482
  if (!existsSync8(CACHE_PATH2)) return freeLicense();
2401
- const raw = JSON.parse(readFileSync5(CACHE_PATH2, "utf8"));
2483
+ const raw = JSON.parse(readFileSync6(CACHE_PATH2, "utf8"));
2402
2484
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
2403
2485
  const parts = raw.token.split(".");
2404
2486
  if (parts.length !== 3) return freeLicense();
@@ -2437,7 +2519,7 @@ function assertEmployeeLimitSync(rosterPath) {
2437
2519
  let count = 0;
2438
2520
  try {
2439
2521
  if (existsSync8(filePath)) {
2440
- const raw = readFileSync5(filePath, "utf8");
2522
+ const raw = readFileSync6(filePath, "utf8");
2441
2523
  const employees = JSON.parse(raw);
2442
2524
  count = Array.isArray(employees) ? employees.length : 0;
2443
2525
  }
@@ -2475,7 +2557,7 @@ import crypto2 from "crypto";
2475
2557
  import path9 from "path";
2476
2558
  import os4 from "os";
2477
2559
  import {
2478
- readFileSync as readFileSync6,
2560
+ readFileSync as readFileSync7,
2479
2561
  readdirSync,
2480
2562
  unlinkSync,
2481
2563
  existsSync as existsSync9,
@@ -2631,7 +2713,7 @@ import crypto4 from "crypto";
2631
2713
  import path10 from "path";
2632
2714
  import { execSync as execSync5 } from "child_process";
2633
2715
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
2634
- import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
2716
+ import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
2635
2717
  async function writeCheckpoint(input) {
2636
2718
  const client = getClient();
2637
2719
  const row = await resolveTask(client, input.taskId);
@@ -3008,7 +3090,7 @@ async function ensureGitignoreExe(baseDir) {
3008
3090
  const gitignorePath = path10.join(baseDir, ".gitignore");
3009
3091
  try {
3010
3092
  if (existsSync10(gitignorePath)) {
3011
- const content = readFileSync7(gitignorePath, "utf-8");
3093
+ const content = readFileSync8(gitignorePath, "utf-8");
3012
3094
  if (/^\/?exe\/?$/m.test(content)) return;
3013
3095
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
3014
3096
  } else {
@@ -3483,7 +3565,7 @@ async function dispatchTaskToEmployee(input) {
3483
3565
  } else {
3484
3566
  const projectDir = input.projectDir ?? process.cwd();
3485
3567
  const result = ensureEmployee(input.assignedTo, exeSession, projectDir, {
3486
- autoInstance: input.assignedTo === "tom" || input.assignedTo === "sasha"
3568
+ autoInstance: isMultiInstance(input.assignedTo)
3487
3569
  });
3488
3570
  if (result.status === "failed") {
3489
3571
  process.stderr.write(
@@ -3518,6 +3600,7 @@ var init_tasks_notify = __esm({
3518
3600
  init_session_key();
3519
3601
  init_notifications();
3520
3602
  init_transport();
3603
+ init_employees();
3521
3604
  }
3522
3605
  });
3523
3606
 
@@ -4291,7 +4374,7 @@ __export(tmux_routing_exports, {
4291
4374
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
4292
4375
  });
4293
4376
  import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
4294
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync } from "fs";
4377
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync } from "fs";
4295
4378
  import path15 from "path";
4296
4379
  import os5 from "os";
4297
4380
  import { fileURLToPath } from "url";
@@ -4358,7 +4441,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4358
4441
  }
4359
4442
  function getParentExe(sessionKey) {
4360
4443
  try {
4361
- const data = JSON.parse(readFileSync8(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4444
+ const data = JSON.parse(readFileSync9(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4362
4445
  return data.parentExe || null;
4363
4446
  } catch {
4364
4447
  return null;
@@ -4366,7 +4449,7 @@ function getParentExe(sessionKey) {
4366
4449
  }
4367
4450
  function getDispatchedBy(sessionKey) {
4368
4451
  try {
4369
- const data = JSON.parse(readFileSync8(
4452
+ const data = JSON.parse(readFileSync9(
4370
4453
  path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
4371
4454
  "utf8"
4372
4455
  ));
@@ -4429,7 +4512,7 @@ async function verifyPaneAtCapacity(sessionName) {
4429
4512
  function readDebounceState() {
4430
4513
  try {
4431
4514
  if (!existsSync12(DEBOUNCE_FILE)) return {};
4432
- return JSON.parse(readFileSync8(DEBOUNCE_FILE, "utf8"));
4515
+ return JSON.parse(readFileSync9(DEBOUNCE_FILE, "utf8"));
4433
4516
  } catch {
4434
4517
  return {};
4435
4518
  }
@@ -4629,7 +4712,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4629
4712
  const claudeJsonPath = path15.join(os5.homedir(), ".claude.json");
4630
4713
  let claudeJson = {};
4631
4714
  try {
4632
- claudeJson = JSON.parse(readFileSync8(claudeJsonPath, "utf8"));
4715
+ claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
4633
4716
  } catch {
4634
4717
  }
4635
4718
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -4647,7 +4730,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4647
4730
  const settingsPath = path15.join(projSettingsDir, "settings.json");
4648
4731
  let settings = {};
4649
4732
  try {
4650
- settings = JSON.parse(readFileSync8(settingsPath, "utf8"));
4733
+ settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
4651
4734
  } catch {
4652
4735
  }
4653
4736
  const perms = settings.permissions ?? {};
@@ -5319,7 +5402,7 @@ __export(agent_signals_exports, {
5319
5402
  hasOpenTasks: () => hasOpenTasks,
5320
5403
  hasUnreadInbox: () => hasUnreadInbox
5321
5404
  });
5322
- import { readFileSync as readFileSync9, existsSync as existsSync13 } from "fs";
5405
+ import { readFileSync as readFileSync10, existsSync as existsSync13 } from "fs";
5323
5406
  import os6 from "os";
5324
5407
  import path16 from "path";
5325
5408
  async function hasOpenTasks(client, agentId) {
@@ -5364,7 +5447,7 @@ async function hasUnreadInbox(client, agentId) {
5364
5447
  function hadRecentIntercomAck(sessionName, windowMs, nowMs = Date.now(), intercomLog = path16.join(os6.homedir(), ".exe-os", "intercom.log")) {
5365
5448
  if (!existsSync13(intercomLog)) return false;
5366
5449
  try {
5367
- const raw = readFileSync9(intercomLog, "utf8");
5450
+ const raw = readFileSync10(intercomLog, "utf8");
5368
5451
  const lines = raw.split("\n");
5369
5452
  for (let i = lines.length - 1; i >= 0; i--) {
5370
5453
  const line = lines[i];
@@ -5796,11 +5879,11 @@ ${synthesisText}`;
5796
5879
  }
5797
5880
  const insertSql = vector ? `INSERT INTO memories
5798
5881
  (id, agent_id, agent_role, session_id, timestamp,
5799
- tool_name, project_name, has_error, raw_text, vector, version, consolidated)
5800
- VALUES (?, ?, 'consolidation', 'daemon-consolidation', ?, 'consolidation', ?, 0, ?, vector32(?), 0, 1)` : `INSERT INTO memories
5882
+ tool_name, project_name, has_error, raw_text, vector, version, consolidated, importance)
5883
+ VALUES (?, ?, 'consolidation', 'daemon-consolidation', ?, 'consolidation', ?, 0, ?, vector32(?), 0, 1, 9)` : `INSERT INTO memories
5801
5884
  (id, agent_id, agent_role, session_id, timestamp,
5802
- tool_name, project_name, has_error, raw_text, vector, version, consolidated)
5803
- VALUES (?, ?, 'consolidation', 'daemon-consolidation', ?, 'consolidation', ?, 0, ?, NULL, 0, 1)`;
5885
+ tool_name, project_name, has_error, raw_text, vector, version, consolidated, importance)
5886
+ VALUES (?, ?, 'consolidation', 'daemon-consolidation', ?, 'consolidation', ?, 0, ?, NULL, 0, 1, 9)`;
5804
5887
  const insertArgs = vector ? [consolidatedId, cluster.agentId, now, cluster.projectName, rawText, vectorToBlob(vector)] : [consolidatedId, cluster.agentId, now, cluster.projectName, rawText];
5805
5888
  await client.execute({ sql: insertSql, args: insertArgs });
5806
5889
  const sourceIds = cluster.memories.map((m) => m.id);
@@ -6047,7 +6130,7 @@ var init_consolidation = __esm({
6047
6130
  import net from "net";
6048
6131
  import { spawn } from "child_process";
6049
6132
  import { randomUUID as randomUUID3 } from "crypto";
6050
- import { existsSync as existsSync14, unlinkSync as unlinkSync4, readFileSync as readFileSync10, openSync, closeSync, statSync } from "fs";
6133
+ import { existsSync as existsSync14, unlinkSync as unlinkSync4, readFileSync as readFileSync11, openSync, closeSync, statSync } from "fs";
6051
6134
  import path17 from "path";
6052
6135
  import { fileURLToPath as fileURLToPath2 } from "url";
6053
6136
  function handleData(chunk) {
@@ -6072,7 +6155,7 @@ function handleData(chunk) {
6072
6155
  function cleanupStaleFiles() {
6073
6156
  if (existsSync14(PID_PATH)) {
6074
6157
  try {
6075
- const pid = parseInt(readFileSync10(PID_PATH, "utf8").trim(), 10);
6158
+ const pid = parseInt(readFileSync11(PID_PATH, "utf8").trim(), 10);
6076
6159
  if (pid > 0) {
6077
6160
  try {
6078
6161
  process.kill(pid, 0);
@@ -6220,11 +6303,11 @@ async function connectEmbedDaemon() {
6220
6303
  }
6221
6304
  }
6222
6305
  const start = Date.now();
6223
- let delay = 100;
6306
+ let delay2 = 100;
6224
6307
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
6225
- await new Promise((r) => setTimeout(r, delay));
6308
+ await new Promise((r) => setTimeout(r, delay2));
6226
6309
  if (await connectToSocket()) return true;
6227
- delay = Math.min(delay * 2, 3e3);
6310
+ delay2 = Math.min(delay2 * 2, 3e3);
6228
6311
  }
6229
6312
  return false;
6230
6313
  }
@@ -6280,7 +6363,7 @@ function killAndRespawnDaemon() {
6280
6363
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
6281
6364
  if (existsSync14(PID_PATH)) {
6282
6365
  try {
6283
- const pid = parseInt(readFileSync10(PID_PATH, "utf8").trim(), 10);
6366
+ const pid = parseInt(readFileSync11(PID_PATH, "utf8").trim(), 10);
6284
6367
  if (pid > 0) {
6285
6368
  try {
6286
6369
  process.kill(pid, "SIGKILL");
@@ -6316,11 +6399,11 @@ async function embedViaClient(text, priority = "high") {
6316
6399
  `);
6317
6400
  killAndRespawnDaemon();
6318
6401
  const start = Date.now();
6319
- let delay = 200;
6402
+ let delay2 = 200;
6320
6403
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
6321
- await new Promise((r) => setTimeout(r, delay));
6404
+ await new Promise((r) => setTimeout(r, delay2));
6322
6405
  if (await connectToSocket()) break;
6323
- delay = Math.min(delay * 2, 3e3);
6406
+ delay2 = Math.min(delay2 * 2, 3e3);
6324
6407
  }
6325
6408
  if (!_connected) return null;
6326
6409
  }
@@ -6332,11 +6415,11 @@ async function embedViaClient(text, priority = "high") {
6332
6415
  `);
6333
6416
  killAndRespawnDaemon();
6334
6417
  const start = Date.now();
6335
- let delay = 200;
6418
+ let delay2 = 200;
6336
6419
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
6337
- await new Promise((r) => setTimeout(r, delay));
6420
+ await new Promise((r) => setTimeout(r, delay2));
6338
6421
  if (await connectToSocket()) break;
6339
- delay = Math.min(delay * 2, 3e3);
6422
+ delay2 = Math.min(delay2 * 2, 3e3);
6340
6423
  }
6341
6424
  if (!_connected) return null;
6342
6425
  const retry = await sendRequest([text], priority);
@@ -7232,11 +7315,11 @@ __export(update_check_exports, {
7232
7315
  getRemoteVersion: () => getRemoteVersion
7233
7316
  });
7234
7317
  import { execSync as execSync11 } from "child_process";
7235
- import { readFileSync as readFileSync11 } from "fs";
7318
+ import { readFileSync as readFileSync12 } from "fs";
7236
7319
  import path18 from "path";
7237
7320
  function getLocalVersion(packageRoot) {
7238
7321
  const pkgPath = path18.join(packageRoot, "package.json");
7239
- const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
7322
+ const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
7240
7323
  return pkg.version;
7241
7324
  }
7242
7325
  function getRemoteVersion() {
@@ -7308,12 +7391,12 @@ __export(device_registry_exports, {
7308
7391
  });
7309
7392
  import crypto9 from "crypto";
7310
7393
  import os7 from "os";
7311
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, existsSync as existsSync15 } from "fs";
7394
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, existsSync as existsSync15 } from "fs";
7312
7395
  import path19 from "path";
7313
7396
  function getDeviceInfo() {
7314
7397
  if (existsSync15(DEVICE_JSON_PATH)) {
7315
7398
  try {
7316
- const raw = readFileSync12(DEVICE_JSON_PATH, "utf8");
7399
+ const raw = readFileSync13(DEVICE_JSON_PATH, "utf8");
7317
7400
  const data = JSON.parse(raw);
7318
7401
  if (data.deviceId && data.friendlyName && data.hostname) {
7319
7402
  return data;
@@ -7691,7 +7774,7 @@ async function deliverLocalMessage(messageId) {
7691
7774
  return true;
7692
7775
  } catch {
7693
7776
  const newRetryCount = msg.retryCount + 1;
7694
- if (newRetryCount >= MAX_RETRIES2) {
7777
+ if (newRetryCount >= MAX_RETRIES3) {
7695
7778
  await markFailed(messageId, "session unavailable after 10 retries");
7696
7779
  } else {
7697
7780
  await client.execute({
@@ -7780,7 +7863,7 @@ async function retryPendingMessages() {
7780
7863
  sql: `SELECT * FROM messages
7781
7864
  WHERE status = 'pending' AND retry_count < ?
7782
7865
  ORDER BY id`,
7783
- args: [MAX_RETRIES2]
7866
+ args: [MAX_RETRIES3]
7784
7867
  });
7785
7868
  let delivered = 0;
7786
7869
  for (const row of result.rows) {
@@ -7793,13 +7876,13 @@ async function retryPendingMessages() {
7793
7876
  }
7794
7877
  return delivered;
7795
7878
  }
7796
- var MAX_RETRIES2, _wsClientSend;
7879
+ var MAX_RETRIES3, _wsClientSend;
7797
7880
  var init_messaging = __esm({
7798
7881
  "src/lib/messaging.ts"() {
7799
7882
  "use strict";
7800
7883
  init_database();
7801
7884
  init_tmux_routing();
7802
- MAX_RETRIES2 = 10;
7885
+ MAX_RETRIES3 = 10;
7803
7886
  _wsClientSend = null;
7804
7887
  }
7805
7888
  });
@@ -7808,7 +7891,7 @@ var init_messaging = __esm({
7808
7891
  init_config();
7809
7892
  init_memory();
7810
7893
  import net2 from "net";
7811
- import { writeFileSync as writeFileSync7, unlinkSync as unlinkSync5, mkdirSync as mkdirSync8, existsSync as existsSync16, readFileSync as readFileSync13 } from "fs";
7894
+ import { writeFileSync as writeFileSync7, unlinkSync as unlinkSync5, mkdirSync as mkdirSync8, existsSync as existsSync16, readFileSync as readFileSync14 } from "fs";
7812
7895
  import path20 from "path";
7813
7896
  import { getLlama } from "node-llama-cpp";
7814
7897
  var SOCKET_PATH2 = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path20.join(EXE_AI_DIR, "exed.sock");
@@ -7955,7 +8038,7 @@ function startServer() {
7955
8038
  const oldPath = path20.join(path20.dirname(SOCKET_PATH2), oldFile);
7956
8039
  try {
7957
8040
  if (oldFile.endsWith(".pid")) {
7958
- const pid = parseInt(readFileSync13(oldPath, "utf8").trim(), 10);
8041
+ const pid = parseInt(readFileSync14(oldPath, "utf8").trim(), 10);
7959
8042
  if (pid > 0) try {
7960
8043
  process.kill(pid, "SIGKILL");
7961
8044
  } catch {
@@ -8379,7 +8462,7 @@ process.on("SIGTERM", () => void shutdown());
8379
8462
  function checkExistingDaemon() {
8380
8463
  try {
8381
8464
  if (!existsSync16(PID_PATH2)) return false;
8382
- const pid = parseInt(readFileSync13(PID_PATH2, "utf8").trim(), 10);
8465
+ const pid = parseInt(readFileSync14(PID_PATH2, "utf8").trim(), 10);
8383
8466
  if (!pid || isNaN(pid)) return false;
8384
8467
  process.kill(pid, 0);
8385
8468
  process.stderr.write(`[exed] Another daemon is already running (PID ${pid}). Exiting.
@@ -23,12 +23,68 @@ var init_memory = __esm({
23
23
  }
24
24
  });
25
25
 
26
+ // src/lib/db-retry.ts
27
+ function isBusyError(err) {
28
+ if (err instanceof Error) {
29
+ const msg = err.message.toLowerCase();
30
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
31
+ }
32
+ return false;
33
+ }
34
+ function delay(ms) {
35
+ return new Promise((resolve) => setTimeout(resolve, ms));
36
+ }
37
+ async function retryOnBusy(fn, label) {
38
+ let lastError;
39
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
40
+ try {
41
+ return await fn();
42
+ } catch (err) {
43
+ lastError = err;
44
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
45
+ throw err;
46
+ }
47
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
48
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
49
+ process.stderr.write(
50
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
51
+ `
52
+ );
53
+ await delay(backoff + jitter);
54
+ }
55
+ }
56
+ throw lastError;
57
+ }
58
+ function wrapWithRetry(client) {
59
+ return new Proxy(client, {
60
+ get(target, prop, receiver) {
61
+ if (prop === "execute") {
62
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
63
+ }
64
+ if (prop === "batch") {
65
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
66
+ }
67
+ return Reflect.get(target, prop, receiver);
68
+ }
69
+ });
70
+ }
71
+ var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
72
+ var init_db_retry = __esm({
73
+ "src/lib/db-retry.ts"() {
74
+ "use strict";
75
+ MAX_RETRIES = 3;
76
+ BASE_DELAY_MS = 200;
77
+ MAX_JITTER_MS = 300;
78
+ }
79
+ });
80
+
26
81
  // src/lib/database.ts
27
82
  import { createClient } from "@libsql/client";
28
83
  async function initDatabase(config) {
29
84
  if (_client) {
30
85
  _client.close();
31
86
  _client = null;
87
+ _resilientClient = null;
32
88
  }
33
89
  const opts = {
34
90
  url: `file:${config.dbPath}`
@@ -37,17 +93,24 @@ async function initDatabase(config) {
37
93
  opts.encryptionKey = config.encryptionKey;
38
94
  }
39
95
  _client = createClient(opts);
96
+ _resilientClient = wrapWithRetry(_client);
40
97
  }
41
98
  function getClient() {
99
+ if (!_resilientClient) {
100
+ throw new Error("Database client not initialized. Call initDatabase() first.");
101
+ }
102
+ return _resilientClient;
103
+ }
104
+ function getRawClient() {
42
105
  if (!_client) {
43
106
  throw new Error("Database client not initialized. Call initDatabase() first.");
44
107
  }
45
108
  return _client;
46
109
  }
47
110
  async function ensureSchema() {
48
- const client = getClient();
111
+ const client = getRawClient();
49
112
  await client.execute("PRAGMA journal_mode = WAL");
50
- await client.execute("PRAGMA busy_timeout = 5000");
113
+ await client.execute("PRAGMA busy_timeout = 30000");
51
114
  try {
52
115
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
53
116
  } catch {
@@ -840,13 +903,16 @@ async function disposeDatabase() {
840
903
  if (_client) {
841
904
  _client.close();
842
905
  _client = null;
906
+ _resilientClient = null;
843
907
  }
844
908
  }
845
- var _client, initTurso, disposeTurso;
909
+ var _client, _resilientClient, initTurso, disposeTurso;
846
910
  var init_database = __esm({
847
911
  "src/lib/database.ts"() {
848
912
  "use strict";
913
+ init_db_retry();
849
914
  _client = null;
915
+ _resilientClient = null;
850
916
  initTurso = initDatabase;
851
917
  disposeTurso = disposeDatabase;
852
918
  }
@@ -1202,7 +1268,7 @@ function listShards() {
1202
1268
  }
1203
1269
  async function ensureShardSchema(client) {
1204
1270
  await client.execute("PRAGMA journal_mode = WAL");
1205
- await client.execute("PRAGMA busy_timeout = 5000");
1271
+ await client.execute("PRAGMA busy_timeout = 30000");
1206
1272
  try {
1207
1273
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
1208
1274
  } catch {
@@ -2087,11 +2153,11 @@ async function connectEmbedDaemon() {
2087
2153
  }
2088
2154
  }
2089
2155
  const start = Date.now();
2090
- let delay = 100;
2156
+ let delay2 = 100;
2091
2157
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
2092
- await new Promise((r) => setTimeout(r, delay));
2158
+ await new Promise((r) => setTimeout(r, delay2));
2093
2159
  if (await connectToSocket()) return true;
2094
- delay = Math.min(delay * 2, 3e3);
2160
+ delay2 = Math.min(delay2 * 2, 3e3);
2095
2161
  }
2096
2162
  return false;
2097
2163
  }
@@ -2183,11 +2249,11 @@ async function embedViaClient(text, priority = "high") {
2183
2249
  `);
2184
2250
  killAndRespawnDaemon();
2185
2251
  const start = Date.now();
2186
- let delay = 200;
2252
+ let delay2 = 200;
2187
2253
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
2188
- await new Promise((r) => setTimeout(r, delay));
2254
+ await new Promise((r) => setTimeout(r, delay2));
2189
2255
  if (await connectToSocket()) break;
2190
- delay = Math.min(delay * 2, 3e3);
2256
+ delay2 = Math.min(delay2 * 2, 3e3);
2191
2257
  }
2192
2258
  if (!_connected) return null;
2193
2259
  }
@@ -2199,11 +2265,11 @@ async function embedViaClient(text, priority = "high") {
2199
2265
  `);
2200
2266
  killAndRespawnDaemon();
2201
2267
  const start = Date.now();
2202
- let delay = 200;
2268
+ let delay2 = 200;
2203
2269
  while (Date.now() - start < CONNECT_TIMEOUT_MS) {
2204
- await new Promise((r) => setTimeout(r, delay));
2270
+ await new Promise((r) => setTimeout(r, delay2));
2205
2271
  if (await connectToSocket()) break;
2206
- delay = Math.min(delay * 2, 3e3);
2272
+ delay2 = Math.min(delay2 * 2, 3e3);
2207
2273
  }
2208
2274
  if (!_connected) return null;
2209
2275
  const retry = await sendRequest([text], priority);