@askexenow/exe-os 0.8.37 → 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 (93) hide show
  1. package/README.md +17 -8
  2. package/dist/bin/backfill-conversations.js +112 -70
  3. package/dist/bin/backfill-responses.js +53 -18
  4. package/dist/bin/backfill-vectors.js +43 -16
  5. package/dist/bin/cleanup-stale-review-tasks.js +38 -16
  6. package/dist/bin/cli.js +790 -468
  7. package/dist/bin/exe-agent.js +19 -4
  8. package/dist/bin/exe-assign.js +46 -13
  9. package/dist/bin/exe-boot.js +288 -129
  10. package/dist/bin/exe-call.js +20 -10
  11. package/dist/bin/exe-cloud.js +135 -30
  12. package/dist/bin/exe-dispatch.js +1 -1
  13. package/dist/bin/exe-doctor.js +38 -16
  14. package/dist/bin/exe-export-behaviors.js +43 -21
  15. package/dist/bin/exe-forget.js +39 -17
  16. package/dist/bin/exe-gateway.js +159 -50
  17. package/dist/bin/exe-heartbeat.js +53 -31
  18. package/dist/bin/exe-kill.js +40 -18
  19. package/dist/bin/exe-launch-agent.js +109 -36
  20. package/dist/bin/exe-link.js +196 -87
  21. package/dist/bin/exe-new-employee.js +56 -17
  22. package/dist/bin/exe-pending-messages.js +47 -25
  23. package/dist/bin/exe-pending-notifications.js +38 -16
  24. package/dist/bin/exe-pending-reviews.js +51 -29
  25. package/dist/bin/exe-rename.js +21 -7
  26. package/dist/bin/exe-review.js +41 -13
  27. package/dist/bin/exe-search.js +57 -21
  28. package/dist/bin/exe-session-cleanup.js +67 -31
  29. package/dist/bin/exe-settings.js +63 -2
  30. package/dist/bin/exe-status.js +35 -13
  31. package/dist/bin/exe-team.js +35 -13
  32. package/dist/bin/git-sweep.js +45 -17
  33. package/dist/bin/graph-backfill.js +38 -16
  34. package/dist/bin/graph-export.js +38 -16
  35. package/dist/bin/install.js +10 -1
  36. package/dist/bin/scan-tasks.js +47 -19
  37. package/dist/bin/setup.js +444 -259
  38. package/dist/bin/shard-migrate.js +38 -16
  39. package/dist/bin/wiki-sync.js +40 -17
  40. package/dist/gateway/index.js +113 -48
  41. package/dist/hooks/bug-report-worker.js +66 -39
  42. package/dist/hooks/commit-complete.js +45 -17
  43. package/dist/hooks/error-recall.js +60 -20
  44. package/dist/hooks/exe-heartbeat-hook.js +3 -2
  45. package/dist/hooks/ingest-worker.js +174 -45
  46. package/dist/hooks/ingest.js +74 -28
  47. package/dist/hooks/instructions-loaded.js +46 -17
  48. package/dist/hooks/notification.js +44 -15
  49. package/dist/hooks/post-compact.js +44 -15
  50. package/dist/hooks/pre-compact.js +42 -14
  51. package/dist/hooks/pre-tool-use.js +59 -22
  52. package/dist/hooks/prompt-ingest-worker.js +75 -14
  53. package/dist/hooks/prompt-submit.js +75 -32
  54. package/dist/hooks/response-ingest-worker.js +76 -15
  55. package/dist/hooks/session-end.js +54 -22
  56. package/dist/hooks/session-start.js +57 -20
  57. package/dist/hooks/stop.js +44 -15
  58. package/dist/hooks/subagent-stop.js +44 -15
  59. package/dist/hooks/summary-worker.js +339 -106
  60. package/dist/index.js +94 -23
  61. package/dist/lib/cloud-sync.js +191 -80
  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/employee-templates.js +5 -0
  68. package/dist/lib/employees.js +11 -6
  69. package/dist/lib/exe-daemon-client.js +6 -1
  70. package/dist/lib/exe-daemon.js +95 -36
  71. package/dist/lib/hybrid-search.js +57 -21
  72. package/dist/lib/identity-templates.js +16 -7
  73. package/dist/lib/identity.js +1 -1
  74. package/dist/lib/keychain.js +2 -1
  75. package/dist/lib/license.js +56 -6
  76. package/dist/lib/messaging.js +1 -1
  77. package/dist/lib/reminders.js +2 -2
  78. package/dist/lib/schedules.js +38 -16
  79. package/dist/lib/skill-learning.js +1 -1
  80. package/dist/lib/store.js +44 -16
  81. package/dist/lib/tasks.js +1 -1
  82. package/dist/lib/tmux-routing.js +1 -1
  83. package/dist/mcp/server.js +280 -155
  84. package/dist/mcp/tools/complete-reminder.js +1 -1
  85. package/dist/mcp/tools/create-task.js +14 -6
  86. package/dist/mcp/tools/deactivate-behavior.js +2 -2
  87. package/dist/mcp/tools/list-reminders.js +1 -1
  88. package/dist/mcp/tools/list-tasks.js +36 -28
  89. package/dist/mcp/tools/send-message.js +1 -1
  90. package/dist/mcp/tools/update-task.js +1 -1
  91. package/dist/runtime/index.js +42 -8
  92. package/dist/tui/App.js +220 -99
  93. package/package.json +5 -3
@@ -114,6 +114,7 @@ async function ensureSchema() {
114
114
  const client = getRawClient();
115
115
  await client.execute("PRAGMA journal_mode = WAL");
116
116
  await client.execute("PRAGMA busy_timeout = 30000");
117
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
117
118
  try {
118
119
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
119
120
  } catch {
@@ -929,15 +930,15 @@ __export(config_exports, {
929
930
  migrateConfig: () => migrateConfig,
930
931
  saveConfig: () => saveConfig
931
932
  });
932
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
933
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
933
934
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
934
935
  import path3 from "path";
935
- import os from "os";
936
+ import os2 from "os";
936
937
  function resolveDataDir() {
937
938
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
938
939
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
939
- const newDir = path3.join(os.homedir(), ".exe-os");
940
- const legacyDir = path3.join(os.homedir(), ".exe-mem");
940
+ const newDir = path3.join(os2.homedir(), ".exe-os");
941
+ const legacyDir = path3.join(os2.homedir(), ".exe-mem");
941
942
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
942
943
  try {
943
944
  renameSync(legacyDir, newDir);
@@ -1024,7 +1025,7 @@ async function loadConfig() {
1024
1025
  normalizeAutoUpdate(migratedCfg);
1025
1026
  const config = { ...DEFAULT_CONFIG, dbPath: path3.join(dir, "memories.db"), ...migratedCfg };
1026
1027
  if (config.dbPath.startsWith("~")) {
1027
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
1028
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
1028
1029
  }
1029
1030
  return config;
1030
1031
  } catch {
@@ -1055,6 +1056,9 @@ async function saveConfig(config) {
1055
1056
  await mkdir2(dir, { recursive: true });
1056
1057
  const configPath = path3.join(dir, "config.json");
1057
1058
  await writeFile2(configPath, JSON.stringify(config, null, 2) + "\n");
1059
+ if (config.cloud?.apiKey) {
1060
+ await chmod2(configPath, 384);
1061
+ }
1058
1062
  }
1059
1063
  async function loadConfigFrom(configPath) {
1060
1064
  const raw = await readFile2(configPath, "utf-8");
@@ -1170,7 +1174,7 @@ __export(shard_manager_exports, {
1170
1174
  shardExists: () => shardExists
1171
1175
  });
1172
1176
  import path4 from "path";
1173
- import { existsSync as existsSync3, mkdirSync } from "fs";
1177
+ import { existsSync as existsSync3, mkdirSync, readdirSync } from "fs";
1174
1178
  import { createClient as createClient2 } from "@libsql/client";
1175
1179
  function initShardManager(encryptionKey) {
1176
1180
  _encryptionKey = encryptionKey;
@@ -1209,7 +1213,6 @@ function shardExists(projectName) {
1209
1213
  }
1210
1214
  function listShards() {
1211
1215
  if (!existsSync3(SHARDS_DIR)) return [];
1212
- const { readdirSync } = __require("fs");
1213
1216
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1214
1217
  }
1215
1218
  async function ensureShardSchema(client) {
@@ -1407,6 +1410,10 @@ import path5 from "path";
1407
1410
  import { fileURLToPath } from "url";
1408
1411
  function handleData(chunk) {
1409
1412
  _buffer += chunk.toString();
1413
+ if (_buffer.length > MAX_BUFFER) {
1414
+ _buffer = "";
1415
+ return;
1416
+ }
1410
1417
  let newlineIdx;
1411
1418
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
1412
1419
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -1714,7 +1721,7 @@ function disconnectClient() {
1714
1721
  entry.resolve({ error: "Client disconnected" });
1715
1722
  }
1716
1723
  }
1717
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending;
1724
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
1718
1725
  var init_exe_daemon_client = __esm({
1719
1726
  "src/lib/exe-daemon-client.ts"() {
1720
1727
  "use strict";
@@ -1731,6 +1738,7 @@ var init_exe_daemon_client = __esm({
1731
1738
  _requestCount = 0;
1732
1739
  HEALTH_CHECK_INTERVAL = 100;
1733
1740
  _pending = /* @__PURE__ */ new Map();
1741
+ MAX_BUFFER = 1e7;
1734
1742
  }
1735
1743
  });
1736
1744
 
@@ -1832,6 +1840,14 @@ import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync6
1832
1840
  import { randomUUID as randomUUID2 } from "crypto";
1833
1841
  import path7 from "path";
1834
1842
  import { jwtVerify, importSPKI } from "jose";
1843
+ async function fetchRetry(url, init) {
1844
+ try {
1845
+ return await fetch(url, init);
1846
+ } catch {
1847
+ await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
1848
+ return fetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
1849
+ }
1850
+ }
1835
1851
  function loadDeviceId() {
1836
1852
  const deviceJsonPath = path7.join(EXE_AI_DIR, "device.json");
1837
1853
  try {
@@ -1902,7 +1918,7 @@ function cacheResponse(token) {
1902
1918
  async function validateLicense(apiKey, deviceId) {
1903
1919
  const did = deviceId ?? loadDeviceId();
1904
1920
  try {
1905
- const res = await fetch(`${API_BASE}/auth/activate`, {
1921
+ const res = await fetchRetry(`${API_BASE}/auth/activate`, {
1906
1922
  method: "POST",
1907
1923
  headers: { "Content-Type": "application/json" },
1908
1924
  body: JSON.stringify({ apiKey, deviceId: did }),
@@ -1937,14 +1953,23 @@ async function validateLicense(apiKey, deviceId) {
1937
1953
  } catch {
1938
1954
  const cached = await getCachedLicense();
1939
1955
  if (cached) return cached;
1940
- return FREE_LICENSE;
1956
+ return { ...FREE_LICENSE, valid: false, error: "offline" };
1957
+ }
1958
+ }
1959
+ function getCacheAgeMs() {
1960
+ try {
1961
+ const { statSync: statSync2 } = __require("fs");
1962
+ const s = statSync2(CACHE_PATH);
1963
+ return Date.now() - s.mtimeMs;
1964
+ } catch {
1965
+ return Infinity;
1941
1966
  }
1942
1967
  }
1943
1968
  async function checkLicense() {
1944
1969
  const key = loadLicense();
1945
1970
  if (!key) return FREE_LICENSE;
1946
1971
  const cached = await getCachedLicense();
1947
- if (cached) return cached;
1972
+ if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
1948
1973
  const deviceId = loadDeviceId();
1949
1974
  return validateLicense(key, deviceId);
1950
1975
  }
@@ -1958,7 +1983,7 @@ function isFeatureAllowed(license, feature) {
1958
1983
  return license.plan === "team" || license.plan === "agency" || license.plan === "enterprise";
1959
1984
  }
1960
1985
  }
1961
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE;
1986
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS;
1962
1987
  var init_license = __esm({
1963
1988
  "src/lib/license.ts"() {
1964
1989
  "use strict";
@@ -1967,6 +1992,7 @@ var init_license = __esm({
1967
1992
  CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
1968
1993
  DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
1969
1994
  API_BASE = "https://askexe.com/cloud";
1995
+ RETRY_DELAY_MS = 500;
1970
1996
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
1971
1997
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
1972
1998
  4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
@@ -1988,6 +2014,7 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
1988
2014
  employeeLimit: 1,
1989
2015
  memoryLimit: 5e3
1990
2016
  };
2017
+ CACHE_MAX_AGE_MS = 36e5;
1991
2018
  }
1992
2019
  });
1993
2020
 
@@ -2165,11 +2192,12 @@ init_database();
2165
2192
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
2166
2193
  import { existsSync } from "fs";
2167
2194
  import path2 from "path";
2195
+ import os from "os";
2168
2196
  import crypto from "crypto";
2169
2197
  var SERVICE = "exe-mem";
2170
2198
  var ACCOUNT = "master-key";
2171
2199
  function getKeyDir() {
2172
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path2.join(process.env.HOME ?? "/tmp", ".exe-os");
2200
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path2.join(os.homedir(), ".exe-os");
2173
2201
  }
2174
2202
  function getKeyPath() {
2175
2203
  return path2.join(getKeyDir(), "master.key");
@@ -2206,6 +2234,30 @@ async function getMasterKey() {
2206
2234
 
2207
2235
  // src/lib/store.ts
2208
2236
  init_config();
2237
+ var INIT_MAX_RETRIES = 3;
2238
+ var INIT_RETRY_DELAY_MS = 1e3;
2239
+ function isBusyError2(err) {
2240
+ if (err instanceof Error) {
2241
+ const msg = err.message.toLowerCase();
2242
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
2243
+ }
2244
+ return false;
2245
+ }
2246
+ async function retryOnBusy2(fn, label) {
2247
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
2248
+ try {
2249
+ return await fn();
2250
+ } catch (err) {
2251
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
2252
+ process.stderr.write(
2253
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
2254
+ `
2255
+ );
2256
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
2257
+ }
2258
+ }
2259
+ throw new Error("unreachable");
2260
+ }
2209
2261
  var _pendingRecords = [];
2210
2262
  var _batchSize = 20;
2211
2263
  var _flushIntervalMs = 1e4;
@@ -2240,14 +2292,17 @@ async function initStore(options) {
2240
2292
  dbPath,
2241
2293
  encryptionKey: hexKey
2242
2294
  });
2243
- await ensureSchema();
2295
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
2244
2296
  try {
2245
2297
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
2246
2298
  initShardManager2(hexKey);
2247
2299
  } catch {
2248
2300
  }
2249
2301
  const client = getClient();
2250
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
2302
+ const vResult = await retryOnBusy2(
2303
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
2304
+ "version-query"
2305
+ );
2251
2306
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
2252
2307
  }
2253
2308
  function classifyTier(record) {
@@ -2290,6 +2345,12 @@ async function writeMemory(record) {
2290
2345
  supersedes_id: record.supersedes_id ?? null
2291
2346
  };
2292
2347
  _pendingRecords.push(dbRow);
2348
+ const MAX_PENDING = 1e3;
2349
+ if (_pendingRecords.length > MAX_PENDING) {
2350
+ const dropped = _pendingRecords.length - MAX_PENDING;
2351
+ _pendingRecords = _pendingRecords.slice(-MAX_PENDING);
2352
+ console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
2353
+ }
2293
2354
  if (_flushTimer === null) {
2294
2355
  _flushTimer = setInterval(() => {
2295
2356
  void flushBatch();
@@ -1,11 +1,5 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
- }) : x)(function(x) {
6
- if (typeof require !== "undefined") return require.apply(this, arguments);
7
- throw Error('Dynamic require of "' + x + '" is not supported');
8
- });
9
3
  var __esm = (fn, res) => function __init() {
10
4
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
5
  };
@@ -15,7 +9,7 @@ var __export = (target, all) => {
15
9
  };
16
10
 
17
11
  // src/lib/config.ts
18
- import { readFile, writeFile, mkdir } from "fs/promises";
12
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
19
13
  import { readFileSync, existsSync, renameSync } from "fs";
20
14
  import path from "path";
21
15
  import os from "os";
@@ -314,6 +308,7 @@ async function ensureSchema() {
314
308
  const client = getRawClient();
315
309
  await client.execute("PRAGMA journal_mode = WAL");
316
310
  await client.execute("PRAGMA busy_timeout = 30000");
311
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
317
312
  try {
318
313
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
319
314
  } catch {
@@ -1122,12 +1117,13 @@ var init_database = __esm({
1122
1117
  });
1123
1118
 
1124
1119
  // src/lib/keychain.ts
1125
- import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod } from "fs/promises";
1120
+ import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
1126
1121
  import { existsSync as existsSync2 } from "fs";
1127
1122
  import path3 from "path";
1123
+ import os2 from "os";
1128
1124
  import crypto from "crypto";
1129
1125
  function getKeyDir() {
1130
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(process.env.HOME ?? "/tmp", ".exe-os");
1126
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os2.homedir(), ".exe-os");
1131
1127
  }
1132
1128
  function getKeyPath() {
1133
1129
  return path3.join(getKeyDir(), "master.key");
@@ -1184,7 +1180,7 @@ __export(shard_manager_exports, {
1184
1180
  shardExists: () => shardExists
1185
1181
  });
1186
1182
  import path4 from "path";
1187
- import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
1183
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync as readdirSync2 } from "fs";
1188
1184
  import { createClient as createClient2 } from "@libsql/client";
1189
1185
  function initShardManager(encryptionKey) {
1190
1186
  _encryptionKey = encryptionKey;
@@ -1223,8 +1219,7 @@ function shardExists(projectName) {
1223
1219
  }
1224
1220
  function listShards() {
1225
1221
  if (!existsSync3(SHARDS_DIR)) return [];
1226
- const { readdirSync: readdirSync3 } = __require("fs");
1227
- return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1222
+ return readdirSync2(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1228
1223
  }
1229
1224
  async function ensureShardSchema(client) {
1230
1225
  await client.execute("PRAGMA journal_mode = WAL");
@@ -1429,6 +1424,28 @@ __export(store_exports, {
1429
1424
  vectorToBlob: () => vectorToBlob,
1430
1425
  writeMemory: () => writeMemory
1431
1426
  });
1427
+ function isBusyError2(err) {
1428
+ if (err instanceof Error) {
1429
+ const msg = err.message.toLowerCase();
1430
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1431
+ }
1432
+ return false;
1433
+ }
1434
+ async function retryOnBusy2(fn, label) {
1435
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1436
+ try {
1437
+ return await fn();
1438
+ } catch (err) {
1439
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1440
+ process.stderr.write(
1441
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1442
+ `
1443
+ );
1444
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1445
+ }
1446
+ }
1447
+ throw new Error("unreachable");
1448
+ }
1432
1449
  async function initStore(options) {
1433
1450
  if (_flushTimer !== null) {
1434
1451
  clearInterval(_flushTimer);
@@ -1457,14 +1474,17 @@ async function initStore(options) {
1457
1474
  dbPath,
1458
1475
  encryptionKey: hexKey
1459
1476
  });
1460
- await ensureSchema();
1477
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1461
1478
  try {
1462
1479
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1463
1480
  initShardManager2(hexKey);
1464
1481
  } catch {
1465
1482
  }
1466
1483
  const client = getClient();
1467
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1484
+ const vResult = await retryOnBusy2(
1485
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1486
+ "version-query"
1487
+ );
1468
1488
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1469
1489
  }
1470
1490
  function classifyTier(record) {
@@ -1507,6 +1527,12 @@ async function writeMemory(record) {
1507
1527
  supersedes_id: record.supersedes_id ?? null
1508
1528
  };
1509
1529
  _pendingRecords.push(dbRow);
1530
+ const MAX_PENDING = 1e3;
1531
+ if (_pendingRecords.length > MAX_PENDING) {
1532
+ const dropped = _pendingRecords.length - MAX_PENDING;
1533
+ _pendingRecords = _pendingRecords.slice(-MAX_PENDING);
1534
+ console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
1535
+ }
1510
1536
  if (_flushTimer === null) {
1511
1537
  _flushTimer = setInterval(() => {
1512
1538
  void flushBatch();
@@ -1838,7 +1864,7 @@ async function getMemoryCardinality(agentId) {
1838
1864
  return 0;
1839
1865
  }
1840
1866
  }
1841
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1867
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1842
1868
  var init_store = __esm({
1843
1869
  "src/lib/store.ts"() {
1844
1870
  "use strict";
@@ -1846,6 +1872,8 @@ var init_store = __esm({
1846
1872
  init_database();
1847
1873
  init_keychain();
1848
1874
  init_config();
1875
+ INIT_MAX_RETRIES = 3;
1876
+ INIT_RETRY_DELAY_MS = 1e3;
1849
1877
  _pendingRecords = [];
1850
1878
  _batchSize = 20;
1851
1879
  _flushIntervalMs = 1e4;
@@ -1869,10 +1897,10 @@ __export(notifications_exports, {
1869
1897
  });
1870
1898
  import crypto2 from "crypto";
1871
1899
  import path5 from "path";
1872
- import os2 from "os";
1900
+ import os3 from "os";
1873
1901
  import {
1874
1902
  readFileSync as readFileSync3,
1875
- readdirSync as readdirSync2,
1903
+ readdirSync as readdirSync3,
1876
1904
  unlinkSync as unlinkSync2,
1877
1905
  existsSync as existsSync4,
1878
1906
  rmdirSync
@@ -2010,12 +2038,12 @@ function formatNotifications(notifications) {
2010
2038
  return lines.join("\n");
2011
2039
  }
2012
2040
  async function migrateJsonNotifications() {
2013
- const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path5.join(os2.homedir(), ".exe-os");
2041
+ const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path5.join(os3.homedir(), ".exe-os");
2014
2042
  const notifDir = path5.join(base, "notifications");
2015
2043
  if (!existsSync4(notifDir)) return 0;
2016
2044
  let migrated = 0;
2017
2045
  try {
2018
- const files = readdirSync2(notifDir).filter((f) => f.endsWith(".json"));
2046
+ const files = readdirSync3(notifDir).filter((f) => f.endsWith(".json"));
2019
2047
  if (files.length === 0) return 0;
2020
2048
  const client = getClient();
2021
2049
  for (const file of files) {
@@ -2043,7 +2071,7 @@ async function migrateJsonNotifications() {
2043
2071
  }
2044
2072
  }
2045
2073
  try {
2046
- const remaining = readdirSync2(notifDir);
2074
+ const remaining = readdirSync3(notifDir);
2047
2075
  if (remaining.length === 0) {
2048
2076
  rmdirSync(notifDir);
2049
2077
  }
@@ -2194,13 +2222,17 @@ var timeout = setTimeout(() => {
2194
2222
  process.exit(0);
2195
2223
  }, 5e3);
2196
2224
  timeout.unref();
2225
+ var MAX_INPUT_SIZE = 1e6;
2197
2226
  var input = "";
2198
2227
  process.stdin.setEncoding("utf8");
2199
2228
  process.stdin.on("data", (chunk) => {
2200
- input += chunk;
2229
+ if (input.length < MAX_INPUT_SIZE) input += chunk;
2201
2230
  });
2202
2231
  process.stdin.on("end", async () => {
2203
2232
  try {
2233
+ if (input.length >= MAX_INPUT_SIZE) {
2234
+ process.exit(0);
2235
+ }
2204
2236
  const data = JSON.parse(input);
2205
2237
  const agent = getActiveAgent();
2206
2238
  if (agent.agentId !== "default") {
@@ -2230,7 +2262,7 @@ Orphaned tasks at session end: ${orphanResult.rows.map((r) => `"${String(r.title
2230
2262
  vector: null
2231
2263
  });
2232
2264
  await flushBatch2();
2233
- if (agent.agentId !== "exe") {
2265
+ if (agent.agentRole !== "COO") {
2234
2266
  const inProgress = orphanResult.rows.filter((r) => String(r.status) === "in_progress");
2235
2267
  if (inProgress.length > 0) {
2236
2268
  const titles = inProgress.map((r) => `"${String(r.title)}"`).join(", ");
@@ -1,11 +1,5 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
- }) : x)(function(x) {
6
- if (typeof require !== "undefined") return require.apply(this, arguments);
7
- throw Error('Dynamic require of "' + x + '" is not supported');
8
- });
9
3
  var __esm = (fn, res) => function __init() {
10
4
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
5
  };
@@ -30,7 +24,7 @@ __export(config_exports, {
30
24
  migrateConfig: () => migrateConfig,
31
25
  saveConfig: () => saveConfig
32
26
  });
33
- import { readFile, writeFile, mkdir } from "fs/promises";
27
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
34
28
  import { readFileSync, existsSync, renameSync } from "fs";
35
29
  import path from "path";
36
30
  import os from "os";
@@ -156,6 +150,9 @@ async function saveConfig(config) {
156
150
  await mkdir(dir, { recursive: true });
157
151
  const configPath = path.join(dir, "config.json");
158
152
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
153
+ if (config.cloud?.apiKey) {
154
+ await chmod(configPath, 384);
155
+ }
159
156
  }
160
157
  async function loadConfigFrom(configPath) {
161
158
  const raw = await readFile(configPath, "utf-8");
@@ -354,6 +351,7 @@ async function ensureSchema() {
354
351
  const client = getRawClient();
355
352
  await client.execute("PRAGMA journal_mode = WAL");
356
353
  await client.execute("PRAGMA busy_timeout = 30000");
354
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
357
355
  try {
358
356
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
359
357
  } catch {
@@ -1162,12 +1160,13 @@ var init_database = __esm({
1162
1160
  });
1163
1161
 
1164
1162
  // src/lib/keychain.ts
1165
- import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod } from "fs/promises";
1163
+ import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
1166
1164
  import { existsSync as existsSync2 } from "fs";
1167
1165
  import path2 from "path";
1166
+ import os2 from "os";
1168
1167
  import crypto from "crypto";
1169
1168
  function getKeyDir() {
1170
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path2.join(process.env.HOME ?? "/tmp", ".exe-os");
1169
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path2.join(os2.homedir(), ".exe-os");
1171
1170
  }
1172
1171
  function getKeyPath() {
1173
1172
  return path2.join(getKeyDir(), "master.key");
@@ -1224,7 +1223,7 @@ __export(shard_manager_exports, {
1224
1223
  shardExists: () => shardExists
1225
1224
  });
1226
1225
  import path3 from "path";
1227
- import { existsSync as existsSync3, mkdirSync } from "fs";
1226
+ import { existsSync as existsSync3, mkdirSync, readdirSync } from "fs";
1228
1227
  import { createClient as createClient2 } from "@libsql/client";
1229
1228
  function initShardManager(encryptionKey) {
1230
1229
  _encryptionKey = encryptionKey;
@@ -1263,8 +1262,7 @@ function shardExists(projectName) {
1263
1262
  }
1264
1263
  function listShards() {
1265
1264
  if (!existsSync3(SHARDS_DIR)) return [];
1266
- const { readdirSync: readdirSync3 } = __require("fs");
1267
- return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1265
+ return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1268
1266
  }
1269
1267
  async function ensureShardSchema(client) {
1270
1268
  await client.execute("PRAGMA journal_mode = WAL");
@@ -1469,6 +1467,28 @@ __export(store_exports, {
1469
1467
  vectorToBlob: () => vectorToBlob,
1470
1468
  writeMemory: () => writeMemory
1471
1469
  });
1470
+ function isBusyError2(err) {
1471
+ if (err instanceof Error) {
1472
+ const msg = err.message.toLowerCase();
1473
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1474
+ }
1475
+ return false;
1476
+ }
1477
+ async function retryOnBusy2(fn, label) {
1478
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1479
+ try {
1480
+ return await fn();
1481
+ } catch (err) {
1482
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1483
+ process.stderr.write(
1484
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1485
+ `
1486
+ );
1487
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1488
+ }
1489
+ }
1490
+ throw new Error("unreachable");
1491
+ }
1472
1492
  async function initStore(options) {
1473
1493
  if (_flushTimer !== null) {
1474
1494
  clearInterval(_flushTimer);
@@ -1497,14 +1517,17 @@ async function initStore(options) {
1497
1517
  dbPath,
1498
1518
  encryptionKey: hexKey
1499
1519
  });
1500
- await ensureSchema();
1520
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1501
1521
  try {
1502
1522
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1503
1523
  initShardManager2(hexKey);
1504
1524
  } catch {
1505
1525
  }
1506
1526
  const client = getClient();
1507
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1527
+ const vResult = await retryOnBusy2(
1528
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1529
+ "version-query"
1530
+ );
1508
1531
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1509
1532
  }
1510
1533
  function classifyTier(record) {
@@ -1547,6 +1570,12 @@ async function writeMemory(record) {
1547
1570
  supersedes_id: record.supersedes_id ?? null
1548
1571
  };
1549
1572
  _pendingRecords.push(dbRow);
1573
+ const MAX_PENDING = 1e3;
1574
+ if (_pendingRecords.length > MAX_PENDING) {
1575
+ const dropped = _pendingRecords.length - MAX_PENDING;
1576
+ _pendingRecords = _pendingRecords.slice(-MAX_PENDING);
1577
+ console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
1578
+ }
1550
1579
  if (_flushTimer === null) {
1551
1580
  _flushTimer = setInterval(() => {
1552
1581
  void flushBatch();
@@ -1878,7 +1907,7 @@ async function getMemoryCardinality(agentId) {
1878
1907
  return 0;
1879
1908
  }
1880
1909
  }
1881
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1910
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1882
1911
  var init_store = __esm({
1883
1912
  "src/lib/store.ts"() {
1884
1913
  "use strict";
@@ -1886,6 +1915,8 @@ var init_store = __esm({
1886
1915
  init_database();
1887
1916
  init_keychain();
1888
1917
  init_config();
1918
+ INIT_MAX_RETRIES = 3;
1919
+ INIT_RETRY_DELAY_MS = 1e3;
1889
1920
  _pendingRecords = [];
1890
1921
  _batchSize = 20;
1891
1922
  _flushIntervalMs = 1e4;
@@ -1993,6 +2024,10 @@ import path4 from "path";
1993
2024
  import { fileURLToPath } from "url";
1994
2025
  function handleData(chunk) {
1995
2026
  _buffer += chunk.toString();
2027
+ if (_buffer.length > MAX_BUFFER) {
2028
+ _buffer = "";
2029
+ return;
2030
+ }
1996
2031
  let newlineIdx;
1997
2032
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
1998
2033
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -2300,7 +2335,7 @@ function disconnectClient() {
2300
2335
  entry.resolve({ error: "Client disconnected" });
2301
2336
  }
2302
2337
  }
2303
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending;
2338
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
2304
2339
  var init_exe_daemon_client = __esm({
2305
2340
  "src/lib/exe-daemon-client.ts"() {
2306
2341
  "use strict";
@@ -2317,6 +2352,7 @@ var init_exe_daemon_client = __esm({
2317
2352
  _requestCount = 0;
2318
2353
  HEALTH_CHECK_INTERVAL = 100;
2319
2354
  _pending = /* @__PURE__ */ new Map();
2355
+ MAX_BUFFER = 1e7;
2320
2356
  }
2321
2357
  });
2322
2358
 
@@ -2445,7 +2481,7 @@ __export(file_grep_exports, {
2445
2481
  grepProjectFiles: () => grepProjectFiles
2446
2482
  });
2447
2483
  import { execSync as execSync2 } from "child_process";
2448
- import { readFileSync as readFileSync3, readdirSync, statSync as statSync2, existsSync as existsSync5 } from "fs";
2484
+ import { readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync5 } from "fs";
2449
2485
  import path6 from "path";
2450
2486
  import crypto2 from "crypto";
2451
2487
  function hasRipgrep() {
@@ -2590,7 +2626,7 @@ function collectFiles(root, patterns) {
2590
2626
  const basename = path6.basename(dir);
2591
2627
  if (EXCLUDE_DIRS.includes(basename)) return;
2592
2628
  try {
2593
- const entries = readdirSync(dir, { withFileTypes: true });
2629
+ const entries = readdirSync2(dir, { withFileTypes: true });
2594
2630
  for (const entry of entries) {
2595
2631
  if (files.length >= MAX_FILES) return;
2596
2632
  const rel = path6.join(relative, entry.name);
@@ -3137,7 +3173,7 @@ async function recentRecords(agentId, options, limit) {
3137
3173
 
3138
3174
  // src/adapters/claude/active-agent.ts
3139
3175
  init_config();
3140
- import { readFileSync as readFileSync4, writeFileSync, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync as readdirSync2 } from "fs";
3176
+ import { readFileSync as readFileSync4, writeFileSync, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync as readdirSync3 } from "fs";
3141
3177
  import { execSync as execSync4 } from "child_process";
3142
3178
  import path8 from "path";
3143
3179
 
@@ -3237,10 +3273,11 @@ var timeout = setTimeout(() => {
3237
3273
  process.exit(0);
3238
3274
  }, 5e3);
3239
3275
  timeout.unref();
3276
+ var MAX_INPUT_SIZE = 1e6;
3240
3277
  var input = "";
3241
3278
  process.stdin.setEncoding("utf8");
3242
3279
  process.stdin.on("data", (chunk) => {
3243
- input += chunk;
3280
+ if (input.length < MAX_INPUT_SIZE) input += chunk;
3244
3281
  });
3245
3282
  process.stdin.on("end", async () => {
3246
3283
  try {