@askexenow/exe-os 0.8.38 → 0.8.39

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 (91) hide show
  1. package/README.md +17 -8
  2. package/dist/bin/backfill-conversations.js +46 -10
  3. package/dist/bin/backfill-responses.js +46 -10
  4. package/dist/bin/backfill-vectors.js +42 -8
  5. package/dist/bin/cleanup-stale-review-tasks.js +37 -8
  6. package/dist/bin/cli.js +281 -154
  7. package/dist/bin/exe-agent.js +19 -4
  8. package/dist/bin/exe-assign.js +39 -5
  9. package/dist/bin/exe-boot.js +237 -111
  10. package/dist/bin/exe-call.js +11 -6
  11. package/dist/bin/exe-cloud.js +99 -28
  12. package/dist/bin/exe-dispatch.js +1 -1
  13. package/dist/bin/exe-doctor.js +37 -8
  14. package/dist/bin/exe-export-behaviors.js +39 -10
  15. package/dist/bin/exe-forget.js +38 -9
  16. package/dist/bin/exe-gateway.js +109 -42
  17. package/dist/bin/exe-heartbeat.js +49 -20
  18. package/dist/bin/exe-kill.js +39 -10
  19. package/dist/bin/exe-launch-agent.js +58 -22
  20. package/dist/bin/exe-link.js +184 -85
  21. package/dist/bin/exe-new-employee.js +21 -7
  22. package/dist/bin/exe-pending-messages.js +46 -17
  23. package/dist/bin/exe-pending-notifications.js +37 -8
  24. package/dist/bin/exe-pending-reviews.js +47 -18
  25. package/dist/bin/exe-rename.js +21 -7
  26. package/dist/bin/exe-review.js +34 -5
  27. package/dist/bin/exe-search.js +47 -10
  28. package/dist/bin/exe-session-cleanup.js +56 -19
  29. package/dist/bin/exe-settings.js +63 -2
  30. package/dist/bin/exe-status.js +34 -5
  31. package/dist/bin/exe-team.js +34 -5
  32. package/dist/bin/git-sweep.js +38 -9
  33. package/dist/bin/graph-backfill.js +37 -8
  34. package/dist/bin/graph-export.js +37 -8
  35. package/dist/bin/install.js +1 -1
  36. package/dist/bin/scan-tasks.js +40 -11
  37. package/dist/bin/setup.js +58 -24
  38. package/dist/bin/shard-migrate.js +37 -8
  39. package/dist/bin/wiki-sync.js +39 -9
  40. package/dist/gateway/index.js +102 -37
  41. package/dist/hooks/bug-report-worker.js +62 -28
  42. package/dist/hooks/commit-complete.js +38 -9
  43. package/dist/hooks/error-recall.js +49 -8
  44. package/dist/hooks/exe-heartbeat-hook.js +3 -2
  45. package/dist/hooks/ingest-worker.js +151 -37
  46. package/dist/hooks/ingest.js +74 -28
  47. package/dist/hooks/instructions-loaded.js +39 -9
  48. package/dist/hooks/notification.js +37 -7
  49. package/dist/hooks/post-compact.js +37 -7
  50. package/dist/hooks/pre-compact.js +35 -6
  51. package/dist/hooks/pre-tool-use.js +52 -14
  52. package/dist/hooks/prompt-ingest-worker.js +56 -10
  53. package/dist/hooks/prompt-submit.js +61 -23
  54. package/dist/hooks/response-ingest-worker.js +57 -11
  55. package/dist/hooks/session-end.js +43 -10
  56. package/dist/hooks/session-start.js +46 -8
  57. package/dist/hooks/stop.js +37 -7
  58. package/dist/hooks/subagent-stop.js +37 -7
  59. package/dist/hooks/summary-worker.js +317 -99
  60. package/dist/index.js +87 -22
  61. package/dist/lib/cloud-sync.js +172 -78
  62. package/dist/lib/config.js +4 -1
  63. package/dist/lib/consolidation.js +5 -4
  64. package/dist/lib/database.js +1 -0
  65. package/dist/lib/device-registry.js +2 -1
  66. package/dist/lib/embedder.js +9 -1
  67. package/dist/lib/employees.js +11 -6
  68. package/dist/lib/exe-daemon-client.js +6 -1
  69. package/dist/lib/exe-daemon.js +71 -28
  70. package/dist/lib/hybrid-search.js +47 -10
  71. package/dist/lib/identity.js +1 -1
  72. package/dist/lib/keychain.js +2 -1
  73. package/dist/lib/license.js +13 -4
  74. package/dist/lib/messaging.js +1 -1
  75. package/dist/lib/reminders.js +2 -2
  76. package/dist/lib/schedules.js +37 -8
  77. package/dist/lib/skill-learning.js +1 -1
  78. package/dist/lib/store.js +37 -8
  79. package/dist/lib/tasks.js +1 -1
  80. package/dist/lib/tmux-routing.js +1 -1
  81. package/dist/mcp/server.js +97 -43
  82. package/dist/mcp/tools/complete-reminder.js +1 -1
  83. package/dist/mcp/tools/create-task.js +14 -6
  84. package/dist/mcp/tools/deactivate-behavior.js +2 -2
  85. package/dist/mcp/tools/list-reminders.js +1 -1
  86. package/dist/mcp/tools/list-tasks.js +1 -1
  87. package/dist/mcp/tools/send-message.js +1 -1
  88. package/dist/mcp/tools/update-task.js +1 -1
  89. package/dist/runtime/index.js +35 -6
  90. package/dist/tui/App.js +177 -95
  91. package/package.json +3 -3
@@ -12,7 +12,7 @@ import path2 from "path";
12
12
  import { jwtVerify, importSPKI } from "jose";
13
13
 
14
14
  // src/lib/config.ts
15
- import { readFile, writeFile, mkdir } from "fs/promises";
15
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
16
16
  import { readFileSync, existsSync, renameSync } from "fs";
17
17
  import path from "path";
18
18
  import os from "os";
@@ -107,6 +107,15 @@ var LICENSE_PATH = path2.join(EXE_AI_DIR, "license.key");
107
107
  var CACHE_PATH = path2.join(EXE_AI_DIR, "license-cache.json");
108
108
  var DEVICE_ID_PATH = path2.join(EXE_AI_DIR, "device-id");
109
109
  var API_BASE = "https://askexe.com/cloud";
110
+ var RETRY_DELAY_MS = 500;
111
+ async function fetchRetry(url, init) {
112
+ try {
113
+ return await fetch(url, init);
114
+ } catch {
115
+ await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
116
+ return fetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
117
+ }
118
+ }
110
119
  var LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
111
120
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
112
121
  4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
@@ -159,7 +168,7 @@ function loadLicense() {
159
168
  }
160
169
  function saveLicense(apiKey) {
161
170
  mkdirSync(EXE_AI_DIR, { recursive: true });
162
- writeFileSync(LICENSE_PATH, apiKey.trim(), "utf8");
171
+ writeFileSync(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
163
172
  }
164
173
  async function verifyLicenseJwt(token) {
165
174
  try {
@@ -211,7 +220,7 @@ function cacheResponse(token) {
211
220
  async function validateLicense(apiKey, deviceId) {
212
221
  const did = deviceId ?? loadDeviceId();
213
222
  try {
214
- const res = await fetch(`${API_BASE}/auth/activate`, {
223
+ const res = await fetchRetry(`${API_BASE}/auth/activate`, {
215
224
  method: "POST",
216
225
  headers: { "Content-Type": "application/json" },
217
226
  body: JSON.stringify({ apiKey, deviceId: did }),
@@ -303,7 +312,7 @@ async function assertVpsLicense(opts) {
303
312
  let explicitRejection = false;
304
313
  let transientFailure = false;
305
314
  try {
306
- const res = await fetch(`${API_BASE}/auth/activate`, {
315
+ const res = await fetchRetry(`${API_BASE}/auth/activate`, {
307
316
  method: "POST",
308
317
  headers: { "Content-Type": "application/json" },
309
318
  body: JSON.stringify({ apiKey, deviceId }),
@@ -342,7 +342,7 @@ var init_intercom_queue = __esm({
342
342
  });
343
343
 
344
344
  // src/lib/config.ts
345
- import { readFile, writeFile, mkdir } from "fs/promises";
345
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
346
346
  import { readFileSync as readFileSync3, existsSync as existsSync3, renameSync as renameSync2 } from "fs";
347
347
  import path3 from "path";
348
348
  import os3 from "os";
@@ -24,7 +24,7 @@ async function createReminder(text, dueDate) {
24
24
  }
25
25
  async function listReminders(includeCompleted = false) {
26
26
  const client = getClient();
27
- const sql = includeCompleted ? `SELECT id, text, created_at, due_date, completed_at FROM reminders ORDER BY due_date ASC NULLS LAST` : `SELECT id, text, created_at, due_date, completed_at FROM reminders WHERE completed_at IS NULL ORDER BY due_date ASC NULLS LAST`;
27
+ const sql = includeCompleted ? `SELECT id, text, created_at, due_date, completed_at FROM reminders ORDER BY due_date ASC NULLS LAST LIMIT 500` : `SELECT id, text, created_at, due_date, completed_at FROM reminders WHERE completed_at IS NULL ORDER BY due_date ASC NULLS LAST LIMIT 500`;
28
28
  const result = await client.execute(sql);
29
29
  return result.rows.map((row) => ({
30
30
  id: String(row.id),
@@ -43,7 +43,7 @@ async function completeReminder(idOrText) {
43
43
  });
44
44
  if (result.rows.length === 0) {
45
45
  result = await client.execute({
46
- sql: `SELECT id, text FROM reminders WHERE completed_at IS NULL AND text LIKE '%' || ? || '%'`,
46
+ sql: `SELECT id, text FROM reminders WHERE completed_at IS NULL AND text LIKE '%' || ? || '%' LIMIT 1`,
47
47
  args: [idOrText]
48
48
  });
49
49
  }
@@ -9,15 +9,15 @@ var __export = (target, all) => {
9
9
  };
10
10
 
11
11
  // src/lib/config.ts
12
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
12
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
13
13
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
14
14
  import path2 from "path";
15
- import os from "os";
15
+ import os2 from "os";
16
16
  function resolveDataDir() {
17
17
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
18
18
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
19
- const newDir = path2.join(os.homedir(), ".exe-os");
20
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
19
+ const newDir = path2.join(os2.homedir(), ".exe-os");
20
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
21
21
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
22
22
  try {
23
23
  renameSync(legacyDir, newDir);
@@ -104,7 +104,7 @@ async function loadConfig() {
104
104
  normalizeAutoUpdate(migratedCfg);
105
105
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
106
106
  if (config.dbPath.startsWith("~")) {
107
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
107
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
108
108
  }
109
109
  return config;
110
110
  } catch {
@@ -532,6 +532,7 @@ async function ensureSchema() {
532
532
  const client = getRawClient();
533
533
  await client.execute("PRAGMA journal_mode = WAL");
534
534
  await client.execute("PRAGMA busy_timeout = 30000");
535
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
535
536
  try {
536
537
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
537
538
  } catch {
@@ -1325,11 +1326,12 @@ async function ensureSchema() {
1325
1326
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1326
1327
  import { existsSync } from "fs";
1327
1328
  import path from "path";
1329
+ import os from "os";
1328
1330
  import crypto from "crypto";
1329
1331
  var SERVICE = "exe-mem";
1330
1332
  var ACCOUNT = "master-key";
1331
1333
  function getKeyDir() {
1332
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
1334
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
1333
1335
  }
1334
1336
  function getKeyPath() {
1335
1337
  return path.join(getKeyDir(), "master.key");
@@ -1366,6 +1368,30 @@ async function getMasterKey() {
1366
1368
 
1367
1369
  // src/lib/store.ts
1368
1370
  init_config();
1371
+ var INIT_MAX_RETRIES = 3;
1372
+ var INIT_RETRY_DELAY_MS = 1e3;
1373
+ function isBusyError2(err) {
1374
+ if (err instanceof Error) {
1375
+ const msg = err.message.toLowerCase();
1376
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1377
+ }
1378
+ return false;
1379
+ }
1380
+ async function retryOnBusy2(fn, label) {
1381
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1382
+ try {
1383
+ return await fn();
1384
+ } catch (err) {
1385
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1386
+ process.stderr.write(
1387
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1388
+ `
1389
+ );
1390
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1391
+ }
1392
+ }
1393
+ throw new Error("unreachable");
1394
+ }
1369
1395
  var _pendingRecords = [];
1370
1396
  var _batchSize = 20;
1371
1397
  var _flushIntervalMs = 1e4;
@@ -1400,14 +1426,17 @@ async function initStore(options) {
1400
1426
  dbPath,
1401
1427
  encryptionKey: hexKey
1402
1428
  });
1403
- await ensureSchema();
1429
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1404
1430
  try {
1405
1431
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1406
1432
  initShardManager2(hexKey);
1407
1433
  } catch {
1408
1434
  }
1409
1435
  const client = getClient();
1410
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1436
+ const vResult = await retryOnBusy2(
1437
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1438
+ "version-query"
1439
+ );
1411
1440
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1412
1441
  }
1413
1442
 
@@ -26,7 +26,7 @@ async function storeBehavior(opts) {
26
26
  }
27
27
 
28
28
  // src/lib/config.ts
29
- import { readFile, writeFile, mkdir } from "fs/promises";
29
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
30
30
  import { readFileSync, existsSync, renameSync } from "fs";
31
31
  import path from "path";
32
32
  import os from "os";
package/dist/lib/store.js CHANGED
@@ -9,15 +9,15 @@ var __export = (target, all) => {
9
9
  };
10
10
 
11
11
  // src/lib/config.ts
12
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
12
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
13
13
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
14
14
  import path2 from "path";
15
- import os from "os";
15
+ import os2 from "os";
16
16
  function resolveDataDir() {
17
17
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
18
18
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
19
- const newDir = path2.join(os.homedir(), ".exe-os");
20
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
19
+ const newDir = path2.join(os2.homedir(), ".exe-os");
20
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
21
21
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
22
22
  try {
23
23
  renameSync(legacyDir, newDir);
@@ -104,7 +104,7 @@ async function loadConfig() {
104
104
  normalizeAutoUpdate(migratedCfg);
105
105
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
106
106
  if (config.dbPath.startsWith("~")) {
107
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
107
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
108
108
  }
109
109
  return config;
110
110
  } catch {
@@ -528,6 +528,7 @@ async function ensureSchema() {
528
528
  const client = getRawClient();
529
529
  await client.execute("PRAGMA journal_mode = WAL");
530
530
  await client.execute("PRAGMA busy_timeout = 30000");
531
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
531
532
  try {
532
533
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
533
534
  } catch {
@@ -1329,11 +1330,12 @@ async function disposeDatabase() {
1329
1330
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1330
1331
  import { existsSync } from "fs";
1331
1332
  import path from "path";
1333
+ import os from "os";
1332
1334
  import crypto from "crypto";
1333
1335
  var SERVICE = "exe-mem";
1334
1336
  var ACCOUNT = "master-key";
1335
1337
  function getKeyDir() {
1336
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
1338
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
1337
1339
  }
1338
1340
  function getKeyPath() {
1339
1341
  return path.join(getKeyDir(), "master.key");
@@ -1370,6 +1372,30 @@ async function getMasterKey() {
1370
1372
 
1371
1373
  // src/lib/store.ts
1372
1374
  init_config();
1375
+ var INIT_MAX_RETRIES = 3;
1376
+ var INIT_RETRY_DELAY_MS = 1e3;
1377
+ function isBusyError2(err) {
1378
+ if (err instanceof Error) {
1379
+ const msg = err.message.toLowerCase();
1380
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1381
+ }
1382
+ return false;
1383
+ }
1384
+ async function retryOnBusy2(fn, label) {
1385
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1386
+ try {
1387
+ return await fn();
1388
+ } catch (err) {
1389
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1390
+ process.stderr.write(
1391
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1392
+ `
1393
+ );
1394
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1395
+ }
1396
+ }
1397
+ throw new Error("unreachable");
1398
+ }
1373
1399
  var _pendingRecords = [];
1374
1400
  var _batchSize = 20;
1375
1401
  var _flushIntervalMs = 1e4;
@@ -1404,14 +1430,17 @@ async function initStore(options) {
1404
1430
  dbPath,
1405
1431
  encryptionKey: hexKey
1406
1432
  });
1407
- await ensureSchema();
1433
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1408
1434
  try {
1409
1435
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1410
1436
  initShardManager2(hexKey);
1411
1437
  } catch {
1412
1438
  }
1413
1439
  const client = getClient();
1414
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1440
+ const vResult = await retryOnBusy2(
1441
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1442
+ "version-query"
1443
+ );
1415
1444
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1416
1445
  }
1417
1446
  function classifyTier(record) {
package/dist/lib/tasks.js CHANGED
@@ -44,7 +44,7 @@ var init_database = __esm({
44
44
  });
45
45
 
46
46
  // src/lib/config.ts
47
- import { readFile, writeFile, mkdir } from "fs/promises";
47
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
48
48
  import { readFileSync, existsSync, renameSync } from "fs";
49
49
  import path from "path";
50
50
  import os from "os";
@@ -342,7 +342,7 @@ var init_database = __esm({
342
342
  });
343
343
 
344
344
  // src/lib/config.ts
345
- import { readFile, writeFile, mkdir } from "fs/promises";
345
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
346
346
  import { readFileSync as readFileSync3, existsSync as existsSync3, renameSync as renameSync2 } from "fs";
347
347
  import path3 from "path";
348
348
  import os3 from "os";