@askexenow/exe-os 0.8.33 → 0.8.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/bin/backfill-conversations.js +341 -349
  2. package/dist/bin/backfill-responses.js +81 -13
  3. package/dist/bin/backfill-vectors.js +72 -12
  4. package/dist/bin/cleanup-stale-review-tasks.js +63 -3
  5. package/dist/bin/cli.js +1737 -1117
  6. package/dist/bin/exe-assign.js +89 -19
  7. package/dist/bin/exe-boot.js +951 -101
  8. package/dist/bin/exe-call.js +61 -2
  9. package/dist/bin/exe-dispatch.js +61 -13
  10. package/dist/bin/exe-doctor.js +63 -3
  11. package/dist/bin/exe-export-behaviors.js +71 -3
  12. package/dist/bin/exe-forget.js +69 -4
  13. package/dist/bin/exe-gateway.js +178 -45
  14. package/dist/bin/exe-heartbeat.js +79 -14
  15. package/dist/bin/exe-kill.js +71 -3
  16. package/dist/bin/exe-launch-agent.js +148 -14
  17. package/dist/bin/exe-link.js +1437 -0
  18. package/dist/bin/exe-new-employee.js +98 -13
  19. package/dist/bin/exe-pending-messages.js +74 -8
  20. package/dist/bin/exe-pending-notifications.js +63 -3
  21. package/dist/bin/exe-pending-reviews.js +77 -11
  22. package/dist/bin/exe-rename.js +1287 -0
  23. package/dist/bin/exe-review.js +73 -5
  24. package/dist/bin/exe-search.js +88 -14
  25. package/dist/bin/exe-session-cleanup.js +102 -28
  26. package/dist/bin/exe-status.js +64 -4
  27. package/dist/bin/exe-team.js +64 -4
  28. package/dist/bin/git-sweep.js +80 -5
  29. package/dist/bin/graph-backfill.js +71 -3
  30. package/dist/bin/graph-export.js +71 -3
  31. package/dist/bin/install.js +38 -8
  32. package/dist/bin/scan-tasks.js +80 -5
  33. package/dist/bin/setup.js +128 -10
  34. package/dist/bin/shard-migrate.js +71 -3
  35. package/dist/bin/wiki-sync.js +71 -3
  36. package/dist/gateway/index.js +179 -46
  37. package/dist/hooks/bug-report-worker.js +254 -28
  38. package/dist/hooks/commit-complete.js +80 -5
  39. package/dist/hooks/error-recall.js +89 -15
  40. package/dist/hooks/exe-heartbeat-hook.js +1 -1
  41. package/dist/hooks/ingest-worker.js +185 -51
  42. package/dist/hooks/ingest.js +1 -1
  43. package/dist/hooks/instructions-loaded.js +81 -6
  44. package/dist/hooks/notification.js +81 -6
  45. package/dist/hooks/post-compact.js +81 -6
  46. package/dist/hooks/pre-compact.js +81 -6
  47. package/dist/hooks/pre-tool-use.js +423 -196
  48. package/dist/hooks/prompt-ingest-worker.js +91 -23
  49. package/dist/hooks/prompt-submit.js +159 -45
  50. package/dist/hooks/response-ingest-worker.js +96 -23
  51. package/dist/hooks/session-end.js +81 -6
  52. package/dist/hooks/session-start.js +89 -15
  53. package/dist/hooks/stop.js +81 -6
  54. package/dist/hooks/subagent-stop.js +81 -6
  55. package/dist/hooks/summary-worker.js +807 -55
  56. package/dist/index.js +198 -60
  57. package/dist/lib/cloud-sync.js +703 -18
  58. package/dist/lib/consolidation.js +4 -4
  59. package/dist/lib/database.js +64 -2
  60. package/dist/lib/device-registry.js +70 -3
  61. package/dist/lib/employee-templates.js +26 -0
  62. package/dist/lib/employees.js +34 -1
  63. package/dist/lib/exe-daemon.js +207 -74
  64. package/dist/lib/hybrid-search.js +88 -14
  65. package/dist/lib/identity-templates.js +51 -0
  66. package/dist/lib/identity.js +3 -3
  67. package/dist/lib/messaging.js +65 -17
  68. package/dist/lib/reminders.js +3 -3
  69. package/dist/lib/schedules.js +63 -3
  70. package/dist/lib/skill-learning.js +3 -3
  71. package/dist/lib/status-brief.js +63 -5
  72. package/dist/lib/store.js +73 -4
  73. package/dist/lib/task-router.js +4 -2
  74. package/dist/lib/tasks.js +95 -28
  75. package/dist/lib/tmux-routing.js +92 -23
  76. package/dist/mcp/server.js +800 -74
  77. package/dist/mcp/tools/complete-reminder.js +3 -3
  78. package/dist/mcp/tools/create-reminder.js +3 -3
  79. package/dist/mcp/tools/create-task.js +198 -31
  80. package/dist/mcp/tools/deactivate-behavior.js +4 -4
  81. package/dist/mcp/tools/list-reminders.js +3 -3
  82. package/dist/mcp/tools/list-tasks.js +19 -9
  83. package/dist/mcp/tools/send-message.js +69 -21
  84. package/dist/mcp/tools/update-task.js +28 -18
  85. package/dist/runtime/index.js +166 -28
  86. package/dist/tui/App.js +193 -40
  87. package/package.json +7 -3
  88. package/src/commands/exe/afk.md +116 -0
  89. package/src/commands/exe/rename.md +12 -0
@@ -231,6 +231,61 @@ var init_memory = __esm({
231
231
  }
232
232
  });
233
233
 
234
+ // src/lib/db-retry.ts
235
+ function isBusyError(err) {
236
+ if (err instanceof Error) {
237
+ const msg = err.message.toLowerCase();
238
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
239
+ }
240
+ return false;
241
+ }
242
+ function delay(ms) {
243
+ return new Promise((resolve) => setTimeout(resolve, ms));
244
+ }
245
+ async function retryOnBusy(fn, label) {
246
+ let lastError;
247
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
248
+ try {
249
+ return await fn();
250
+ } catch (err) {
251
+ lastError = err;
252
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
253
+ throw err;
254
+ }
255
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
256
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
257
+ process.stderr.write(
258
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
259
+ `
260
+ );
261
+ await delay(backoff + jitter);
262
+ }
263
+ }
264
+ throw lastError;
265
+ }
266
+ function wrapWithRetry(client) {
267
+ return new Proxy(client, {
268
+ get(target, prop, receiver) {
269
+ if (prop === "execute") {
270
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
271
+ }
272
+ if (prop === "batch") {
273
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
274
+ }
275
+ return Reflect.get(target, prop, receiver);
276
+ }
277
+ });
278
+ }
279
+ var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
280
+ var init_db_retry = __esm({
281
+ "src/lib/db-retry.ts"() {
282
+ "use strict";
283
+ MAX_RETRIES = 3;
284
+ BASE_DELAY_MS = 200;
285
+ MAX_JITTER_MS = 300;
286
+ }
287
+ });
288
+
234
289
  // src/lib/database.ts
235
290
  var database_exports = {};
236
291
  __export(database_exports, {
@@ -238,6 +293,7 @@ __export(database_exports, {
238
293
  disposeTurso: () => disposeTurso,
239
294
  ensureSchema: () => ensureSchema,
240
295
  getClient: () => getClient,
296
+ getRawClient: () => getRawClient,
241
297
  initDatabase: () => initDatabase,
242
298
  initTurso: () => initTurso,
243
299
  isInitialized: () => isInitialized
@@ -247,6 +303,7 @@ async function initDatabase(config) {
247
303
  if (_client) {
248
304
  _client.close();
249
305
  _client = null;
306
+ _resilientClient = null;
250
307
  }
251
308
  const opts = {
252
309
  url: `file:${config.dbPath}`
@@ -255,20 +312,27 @@ async function initDatabase(config) {
255
312
  opts.encryptionKey = config.encryptionKey;
256
313
  }
257
314
  _client = createClient(opts);
315
+ _resilientClient = wrapWithRetry(_client);
258
316
  }
259
317
  function isInitialized() {
260
318
  return _client !== null;
261
319
  }
262
320
  function getClient() {
321
+ if (!_resilientClient) {
322
+ throw new Error("Database client not initialized. Call initDatabase() first.");
323
+ }
324
+ return _resilientClient;
325
+ }
326
+ function getRawClient() {
263
327
  if (!_client) {
264
328
  throw new Error("Database client not initialized. Call initDatabase() first.");
265
329
  }
266
330
  return _client;
267
331
  }
268
332
  async function ensureSchema() {
269
- const client = getClient();
333
+ const client = getRawClient();
270
334
  await client.execute("PRAGMA journal_mode = WAL");
271
- await client.execute("PRAGMA busy_timeout = 5000");
335
+ await client.execute("PRAGMA busy_timeout = 30000");
272
336
  try {
273
337
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
274
338
  } catch {
@@ -1061,13 +1125,16 @@ async function disposeDatabase() {
1061
1125
  if (_client) {
1062
1126
  _client.close();
1063
1127
  _client = null;
1128
+ _resilientClient = null;
1064
1129
  }
1065
1130
  }
1066
- var _client, initTurso, disposeTurso;
1131
+ var _client, _resilientClient, initTurso, disposeTurso;
1067
1132
  var init_database = __esm({
1068
1133
  "src/lib/database.ts"() {
1069
1134
  "use strict";
1135
+ init_db_retry();
1070
1136
  _client = null;
1137
+ _resilientClient = null;
1071
1138
  initTurso = initDatabase;
1072
1139
  disposeTurso = disposeDatabase;
1073
1140
  }
@@ -1180,7 +1247,7 @@ function listShards() {
1180
1247
  }
1181
1248
  async function ensureShardSchema(client) {
1182
1249
  await client.execute("PRAGMA journal_mode = WAL");
1183
- await client.execute("PRAGMA busy_timeout = 5000");
1250
+ await client.execute("PRAGMA busy_timeout = 30000");
1184
1251
  try {
1185
1252
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
1186
1253
  } catch {
@@ -1441,7 +1508,8 @@ async function writeMemory(record) {
1441
1508
  has_error: record.has_error ? 1 : 0,
1442
1509
  raw_text: record.raw_text,
1443
1510
  vector: record.vector,
1444
- version: _nextVersion++,
1511
+ version: 0,
1512
+ // Placeholder — assigned atomically at flush time
1445
1513
  task_id: record.task_id ?? null,
1446
1514
  importance: record.importance ?? 5,
1447
1515
  status: record.status ?? "active",
@@ -1475,6 +1543,13 @@ async function flushBatch() {
1475
1543
  _flushing = true;
1476
1544
  try {
1477
1545
  const batch = _pendingRecords.slice(0);
1546
+ const client = getClient();
1547
+ const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1548
+ let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1549
+ for (const row of batch) {
1550
+ row.version = baseVersion++;
1551
+ }
1552
+ _nextVersion = baseVersion;
1478
1553
  const buildStmt = (row) => {
1479
1554
  const hasVector = row.vector !== null;
1480
1555
  const taskId = row.task_id ?? null;
@@ -1881,7 +1956,7 @@ function getActiveAgent() {
1881
1956
  "tmux display-message -p '#{session_name}' 2>/dev/null",
1882
1957
  { encoding: "utf8", timeout: 2e3 }
1883
1958
  ).trim();
1884
- const empMatch = sessionName.match(/^(\w+)-exe\d+$/);
1959
+ const empMatch = sessionName.match(/^([a-zA-Z]+)\d*-exe\d+$/);
1885
1960
  if (empMatch && empMatch[1] !== "exe") {
1886
1961
  return { agentId: empMatch[1], agentRole: "employee" };
1887
1962
  }
@@ -212,6 +212,61 @@ var init_memory = __esm({
212
212
  }
213
213
  });
214
214
 
215
+ // src/lib/db-retry.ts
216
+ function isBusyError(err) {
217
+ if (err instanceof Error) {
218
+ const msg = err.message.toLowerCase();
219
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
220
+ }
221
+ return false;
222
+ }
223
+ function delay(ms) {
224
+ return new Promise((resolve) => setTimeout(resolve, ms));
225
+ }
226
+ async function retryOnBusy(fn, label) {
227
+ let lastError;
228
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
229
+ try {
230
+ return await fn();
231
+ } catch (err) {
232
+ lastError = err;
233
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
234
+ throw err;
235
+ }
236
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
237
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
238
+ process.stderr.write(
239
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
240
+ `
241
+ );
242
+ await delay(backoff + jitter);
243
+ }
244
+ }
245
+ throw lastError;
246
+ }
247
+ function wrapWithRetry(client) {
248
+ return new Proxy(client, {
249
+ get(target, prop, receiver) {
250
+ if (prop === "execute") {
251
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
252
+ }
253
+ if (prop === "batch") {
254
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
255
+ }
256
+ return Reflect.get(target, prop, receiver);
257
+ }
258
+ });
259
+ }
260
+ var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
261
+ var init_db_retry = __esm({
262
+ "src/lib/db-retry.ts"() {
263
+ "use strict";
264
+ MAX_RETRIES = 3;
265
+ BASE_DELAY_MS = 200;
266
+ MAX_JITTER_MS = 300;
267
+ }
268
+ });
269
+
215
270
  // src/lib/database.ts
216
271
  var database_exports = {};
217
272
  __export(database_exports, {
@@ -219,6 +274,7 @@ __export(database_exports, {
219
274
  disposeTurso: () => disposeTurso,
220
275
  ensureSchema: () => ensureSchema,
221
276
  getClient: () => getClient,
277
+ getRawClient: () => getRawClient,
222
278
  initDatabase: () => initDatabase,
223
279
  initTurso: () => initTurso,
224
280
  isInitialized: () => isInitialized
@@ -228,6 +284,7 @@ async function initDatabase(config) {
228
284
  if (_client) {
229
285
  _client.close();
230
286
  _client = null;
287
+ _resilientClient = null;
231
288
  }
232
289
  const opts = {
233
290
  url: `file:${config.dbPath}`
@@ -236,20 +293,27 @@ async function initDatabase(config) {
236
293
  opts.encryptionKey = config.encryptionKey;
237
294
  }
238
295
  _client = createClient(opts);
296
+ _resilientClient = wrapWithRetry(_client);
239
297
  }
240
298
  function isInitialized() {
241
299
  return _client !== null;
242
300
  }
243
301
  function getClient() {
302
+ if (!_resilientClient) {
303
+ throw new Error("Database client not initialized. Call initDatabase() first.");
304
+ }
305
+ return _resilientClient;
306
+ }
307
+ function getRawClient() {
244
308
  if (!_client) {
245
309
  throw new Error("Database client not initialized. Call initDatabase() first.");
246
310
  }
247
311
  return _client;
248
312
  }
249
313
  async function ensureSchema() {
250
- const client = getClient();
314
+ const client = getRawClient();
251
315
  await client.execute("PRAGMA journal_mode = WAL");
252
- await client.execute("PRAGMA busy_timeout = 5000");
316
+ await client.execute("PRAGMA busy_timeout = 30000");
253
317
  try {
254
318
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
255
319
  } catch {
@@ -1042,13 +1106,16 @@ async function disposeDatabase() {
1042
1106
  if (_client) {
1043
1107
  _client.close();
1044
1108
  _client = null;
1109
+ _resilientClient = null;
1045
1110
  }
1046
1111
  }
1047
- var _client, initTurso, disposeTurso;
1112
+ var _client, _resilientClient, initTurso, disposeTurso;
1048
1113
  var init_database = __esm({
1049
1114
  "src/lib/database.ts"() {
1050
1115
  "use strict";
1116
+ init_db_retry();
1051
1117
  _client = null;
1118
+ _resilientClient = null;
1052
1119
  initTurso = initDatabase;
1053
1120
  disposeTurso = disposeDatabase;
1054
1121
  }
@@ -1161,7 +1228,7 @@ function listShards() {
1161
1228
  }
1162
1229
  async function ensureShardSchema(client) {
1163
1230
  await client.execute("PRAGMA journal_mode = WAL");
1164
- await client.execute("PRAGMA busy_timeout = 5000");
1231
+ await client.execute("PRAGMA busy_timeout = 30000");
1165
1232
  try {
1166
1233
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
1167
1234
  } catch {
@@ -1422,7 +1489,8 @@ async function writeMemory(record) {
1422
1489
  has_error: record.has_error ? 1 : 0,
1423
1490
  raw_text: record.raw_text,
1424
1491
  vector: record.vector,
1425
- version: _nextVersion++,
1492
+ version: 0,
1493
+ // Placeholder — assigned atomically at flush time
1426
1494
  task_id: record.task_id ?? null,
1427
1495
  importance: record.importance ?? 5,
1428
1496
  status: record.status ?? "active",
@@ -1456,6 +1524,13 @@ async function flushBatch() {
1456
1524
  _flushing = true;
1457
1525
  try {
1458
1526
  const batch = _pendingRecords.slice(0);
1527
+ const client = getClient();
1528
+ const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1529
+ let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1530
+ for (const row of batch) {
1531
+ row.version = baseVersion++;
1532
+ }
1533
+ _nextVersion = baseVersion;
1459
1534
  const buildStmt = (row) => {
1460
1535
  const hasVector = row.vector !== null;
1461
1536
  const taskId = row.task_id ?? null;
@@ -1854,7 +1929,7 @@ function getActiveAgent() {
1854
1929
  "tmux display-message -p '#{session_name}' 2>/dev/null",
1855
1930
  { encoding: "utf8", timeout: 2e3 }
1856
1931
  ).trim();
1857
- const empMatch = sessionName.match(/^(\w+)-exe\d+$/);
1932
+ const empMatch = sessionName.match(/^([a-zA-Z]+)\d*-exe\d+$/);
1858
1933
  if (empMatch && empMatch[1] !== "exe") {
1859
1934
  return { agentId: empMatch[1], agentRole: "employee" };
1860
1935
  }