@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
@@ -4,15 +4,15 @@ var __esm = (fn, res) => function __init() {
4
4
  };
5
5
 
6
6
  // src/lib/config.ts
7
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
7
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
8
8
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
9
9
  import path2 from "path";
10
- import os from "os";
10
+ import os2 from "os";
11
11
  function resolveDataDir() {
12
12
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
13
13
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
14
- const newDir = path2.join(os.homedir(), ".exe-os");
15
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
14
+ const newDir = path2.join(os2.homedir(), ".exe-os");
15
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
16
16
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
17
17
  try {
18
18
  renameSync(legacyDir, newDir);
@@ -110,6 +110,7 @@ import { createClient } from "@libsql/client";
110
110
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
111
111
  import { existsSync } from "fs";
112
112
  import path from "path";
113
+ import os from "os";
113
114
  import crypto from "crypto";
114
115
 
115
116
  // src/lib/store.ts
@@ -88,6 +88,7 @@ async function ensureSchema() {
88
88
  const client = getRawClient();
89
89
  await client.execute("PRAGMA journal_mode = WAL");
90
90
  await client.execute("PRAGMA busy_timeout = 30000");
91
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
91
92
  try {
92
93
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
93
94
  } catch {
@@ -110,6 +110,7 @@ async function ensureSchema() {
110
110
  const client = getRawClient();
111
111
  await client.execute("PRAGMA journal_mode = WAL");
112
112
  await client.execute("PRAGMA busy_timeout = 30000");
113
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
113
114
  try {
114
115
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
115
116
  } catch {
@@ -924,7 +925,7 @@ import { readFileSync as readFileSync2, writeFileSync, mkdirSync, existsSync as
924
925
  import path2 from "path";
925
926
 
926
927
  // src/lib/config.ts
927
- import { readFile, writeFile, mkdir } from "fs/promises";
928
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
928
929
  import { readFileSync, existsSync, renameSync } from "fs";
929
930
  import path from "path";
930
931
  import os from "os";
@@ -24,7 +24,7 @@ __export(config_exports, {
24
24
  migrateConfig: () => migrateConfig,
25
25
  saveConfig: () => saveConfig
26
26
  });
27
- import { readFile, writeFile, mkdir } from "fs/promises";
27
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
28
28
  import { readFileSync, existsSync, renameSync } from "fs";
29
29
  import path from "path";
30
30
  import os from "os";
@@ -150,6 +150,9 @@ async function saveConfig(config) {
150
150
  await mkdir(dir, { recursive: true });
151
151
  const configPath = path.join(dir, "config.json");
152
152
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
153
+ if (config.cloud?.apiKey) {
154
+ await chmod(configPath, 384);
155
+ }
153
156
  }
154
157
  async function loadConfigFrom(configPath) {
155
158
  const raw = await readFile(configPath, "utf-8");
@@ -274,8 +277,13 @@ var _buffer = "";
274
277
  var _requestCount = 0;
275
278
  var HEALTH_CHECK_INTERVAL = 100;
276
279
  var _pending = /* @__PURE__ */ new Map();
280
+ var MAX_BUFFER = 1e7;
277
281
  function handleData(chunk) {
278
282
  _buffer += chunk.toString();
283
+ if (_buffer.length > MAX_BUFFER) {
284
+ _buffer = "";
285
+ return;
286
+ }
279
287
  let newlineIdx;
280
288
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
281
289
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -5,7 +5,7 @@ import { execSync } from "child_process";
5
5
  import path2 from "path";
6
6
 
7
7
  // src/lib/config.ts
8
- import { readFile, writeFile, mkdir } from "fs/promises";
8
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
9
9
  import { readFileSync, existsSync, renameSync } from "fs";
10
10
  import path from "path";
11
11
  import os from "os";
@@ -165,15 +165,20 @@ function addEmployee(employees, employee) {
165
165
  }
166
166
  return [...employees, normalized];
167
167
  }
168
+ function findExeBin() {
169
+ try {
170
+ return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
171
+ } catch {
172
+ return null;
173
+ }
174
+ }
168
175
  function registerBinSymlinks(name) {
169
176
  const created = [];
170
177
  const skipped = [];
171
178
  const errors = [];
172
- let exeBinPath;
173
- try {
174
- exeBinPath = execSync("which exe", { encoding: "utf-8" }).trim();
175
- } catch {
176
- errors.push("Could not find 'exe' in PATH");
179
+ const exeBinPath = findExeBin();
180
+ if (!exeBinPath) {
181
+ errors.push("Could not find 'exe-os' in PATH");
177
182
  return { created, skipped, errors };
178
183
  }
179
184
  const binDir = path2.dirname(exeBinPath);
@@ -7,7 +7,7 @@ import path2 from "path";
7
7
  import { fileURLToPath } from "url";
8
8
 
9
9
  // src/lib/config.ts
10
- import { readFile, writeFile, mkdir } from "fs/promises";
10
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
11
11
  import { readFileSync, existsSync, renameSync } from "fs";
12
12
  import path from "path";
13
13
  import os from "os";
@@ -110,8 +110,13 @@ var _buffer = "";
110
110
  var _requestCount = 0;
111
111
  var HEALTH_CHECK_INTERVAL = 100;
112
112
  var _pending = /* @__PURE__ */ new Map();
113
+ var MAX_BUFFER = 1e7;
113
114
  function handleData(chunk) {
114
115
  _buffer += chunk.toString();
116
+ if (_buffer.length > MAX_BUFFER) {
117
+ _buffer = "";
118
+ return;
119
+ }
115
120
  let newlineIdx;
116
121
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
117
122
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -41,7 +41,7 @@ __export(config_exports, {
41
41
  migrateConfig: () => migrateConfig,
42
42
  saveConfig: () => saveConfig
43
43
  });
44
- import { readFile, writeFile, mkdir } from "fs/promises";
44
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
45
45
  import { readFileSync, existsSync, renameSync } from "fs";
46
46
  import path from "path";
47
47
  import os from "os";
@@ -167,6 +167,9 @@ async function saveConfig(config) {
167
167
  await mkdir(dir, { recursive: true });
168
168
  const configPath = path.join(dir, "config.json");
169
169
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
170
+ if (config.cloud?.apiKey) {
171
+ await chmod(configPath, 384);
172
+ }
170
173
  }
171
174
  async function loadConfigFrom(configPath) {
172
175
  const raw = await readFile(configPath, "utf-8");
@@ -379,6 +382,7 @@ async function ensureSchema() {
379
382
  const client = getRawClient();
380
383
  await client.execute("PRAGMA journal_mode = WAL");
381
384
  await client.execute("PRAGMA busy_timeout = 30000");
385
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
382
386
  try {
383
387
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
384
388
  } catch {
@@ -1195,12 +1199,13 @@ __export(keychain_exports, {
1195
1199
  importMnemonic: () => importMnemonic,
1196
1200
  setMasterKey: () => setMasterKey
1197
1201
  });
1198
- import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod } from "fs/promises";
1202
+ import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
1199
1203
  import { existsSync as existsSync2 } from "fs";
1200
1204
  import path2 from "path";
1205
+ import os2 from "os";
1201
1206
  import crypto from "crypto";
1202
1207
  function getKeyDir() {
1203
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path2.join(process.env.HOME ?? "/tmp", ".exe-os");
1208
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path2.join(os2.homedir(), ".exe-os");
1204
1209
  }
1205
1210
  function getKeyPath() {
1206
1211
  return path2.join(getKeyDir(), "master.key");
@@ -1248,7 +1253,7 @@ async function setMasterKey(key) {
1248
1253
  await mkdir2(dir, { recursive: true });
1249
1254
  const keyPath = getKeyPath();
1250
1255
  await writeFile2(keyPath, b64 + "\n", "utf-8");
1251
- await chmod(keyPath, 384);
1256
+ await chmod2(keyPath, 384);
1252
1257
  }
1253
1258
  async function deleteMasterKey() {
1254
1259
  const keytar = await tryKeytar();
@@ -1590,6 +1595,28 @@ __export(store_exports, {
1590
1595
  vectorToBlob: () => vectorToBlob,
1591
1596
  writeMemory: () => writeMemory
1592
1597
  });
1598
+ function isBusyError2(err) {
1599
+ if (err instanceof Error) {
1600
+ const msg = err.message.toLowerCase();
1601
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1602
+ }
1603
+ return false;
1604
+ }
1605
+ async function retryOnBusy2(fn, label) {
1606
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1607
+ try {
1608
+ return await fn();
1609
+ } catch (err) {
1610
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1611
+ process.stderr.write(
1612
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1613
+ `
1614
+ );
1615
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1616
+ }
1617
+ }
1618
+ throw new Error("unreachable");
1619
+ }
1593
1620
  async function initStore(options) {
1594
1621
  if (_flushTimer !== null) {
1595
1622
  clearInterval(_flushTimer);
@@ -1618,14 +1645,17 @@ async function initStore(options) {
1618
1645
  dbPath,
1619
1646
  encryptionKey: hexKey
1620
1647
  });
1621
- await ensureSchema();
1648
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1622
1649
  try {
1623
1650
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1624
1651
  initShardManager2(hexKey);
1625
1652
  } catch {
1626
1653
  }
1627
1654
  const client = getClient();
1628
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1655
+ const vResult = await retryOnBusy2(
1656
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1657
+ "version-query"
1658
+ );
1629
1659
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1630
1660
  }
1631
1661
  function classifyTier(record) {
@@ -2005,7 +2035,7 @@ async function getMemoryCardinality(agentId) {
2005
2035
  return 0;
2006
2036
  }
2007
2037
  }
2008
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
2038
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
2009
2039
  var init_store = __esm({
2010
2040
  "src/lib/store.ts"() {
2011
2041
  "use strict";
@@ -2013,6 +2043,8 @@ var init_store = __esm({
2013
2043
  init_database();
2014
2044
  init_keychain();
2015
2045
  init_config();
2046
+ INIT_MAX_RETRIES = 3;
2047
+ INIT_RETRY_DELAY_MS = 1e3;
2016
2048
  _pendingRecords = [];
2017
2049
  _batchSize = 20;
2018
2050
  _flushIntervalMs = 1e4;
@@ -2032,7 +2064,7 @@ __export(session_registry_exports, {
2032
2064
  import { readFileSync as readFileSync2, writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
2033
2065
  import { execSync } from "child_process";
2034
2066
  import path4 from "path";
2035
- import os2 from "os";
2067
+ import os3 from "os";
2036
2068
  function registerSession(entry) {
2037
2069
  const dir = path4.dirname(REGISTRY_PATH);
2038
2070
  if (!existsSync4(dir)) {
@@ -2078,7 +2110,7 @@ var REGISTRY_PATH;
2078
2110
  var init_session_registry = __esm({
2079
2111
  "src/lib/session-registry.ts"() {
2080
2112
  "use strict";
2081
- REGISTRY_PATH = path4.join(os2.homedir(), ".exe-os", "session-registry.json");
2113
+ REGISTRY_PATH = path4.join(os3.homedir(), ".exe-os", "session-registry.json");
2082
2114
  }
2083
2115
  });
2084
2116
 
@@ -2315,7 +2347,7 @@ __export(intercom_queue_exports, {
2315
2347
  });
2316
2348
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync as renameSync2, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
2317
2349
  import path5 from "path";
2318
- import os3 from "os";
2350
+ import os4 from "os";
2319
2351
  function ensureDir() {
2320
2352
  const dir = path5.dirname(QUEUE_PATH);
2321
2353
  if (!existsSync5(dir)) mkdirSync3(dir, { recursive: true });
@@ -2414,10 +2446,10 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
2414
2446
  var init_intercom_queue = __esm({
2415
2447
  "src/lib/intercom-queue.ts"() {
2416
2448
  "use strict";
2417
- QUEUE_PATH = path5.join(os3.homedir(), ".exe-os", "intercom-queue.json");
2449
+ QUEUE_PATH = path5.join(os4.homedir(), ".exe-os", "intercom-queue.json");
2418
2450
  MAX_RETRIES2 = 5;
2419
2451
  TTL_MS = 60 * 60 * 1e3;
2420
- INTERCOM_LOG = path5.join(os3.homedir(), ".exe-os", "intercom.log");
2452
+ INTERCOM_LOG = path5.join(os4.homedir(), ".exe-os", "intercom.log");
2421
2453
  }
2422
2454
  });
2423
2455
 
@@ -2568,7 +2600,7 @@ var init_plan_limits = __esm({
2568
2600
  // src/lib/notifications.ts
2569
2601
  import crypto2 from "crypto";
2570
2602
  import path9 from "path";
2571
- import os4 from "os";
2603
+ import os5 from "os";
2572
2604
  import {
2573
2605
  readFileSync as readFileSync7,
2574
2606
  readdirSync as readdirSync2,
@@ -4391,7 +4423,7 @@ __export(tmux_routing_exports, {
4391
4423
  import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
4392
4424
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync } from "fs";
4393
4425
  import path15 from "path";
4394
- import os5 from "os";
4426
+ import os6 from "os";
4395
4427
  import { fileURLToPath } from "url";
4396
4428
  import { unlinkSync as unlinkSync4 } from "fs";
4397
4429
  function spawnLockPath(sessionName) {
@@ -4744,7 +4776,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4744
4776
  const transport = getTransport();
4745
4777
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
4746
4778
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
4747
- const logDir = path15.join(os5.homedir(), ".exe-os", "session-logs");
4779
+ const logDir = path15.join(os6.homedir(), ".exe-os", "session-logs");
4748
4780
  const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
4749
4781
  if (!existsSync12(logDir)) {
4750
4782
  mkdirSync6(logDir, { recursive: true });
@@ -4760,7 +4792,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4760
4792
  } catch {
4761
4793
  }
4762
4794
  try {
4763
- const claudeJsonPath = path15.join(os5.homedir(), ".claude.json");
4795
+ const claudeJsonPath = path15.join(os6.homedir(), ".claude.json");
4764
4796
  let claudeJson = {};
4765
4797
  try {
4766
4798
  claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
@@ -4775,7 +4807,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4775
4807
  } catch {
4776
4808
  }
4777
4809
  try {
4778
- const settingsDir = path15.join(os5.homedir(), ".claude", "projects");
4810
+ const settingsDir = path15.join(os6.homedir(), ".claude", "projects");
4779
4811
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
4780
4812
  const projSettingsDir = path15.join(settingsDir, normalizedKey);
4781
4813
  const settingsPath = path15.join(projSettingsDir, "settings.json");
@@ -4823,7 +4855,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4823
4855
  let legacyFallbackWarned = false;
4824
4856
  if (!useExeAgent && !useBinSymlink) {
4825
4857
  const identityPath = path15.join(
4826
- os5.homedir(),
4858
+ os6.homedir(),
4827
4859
  ".exe-os",
4828
4860
  "identity",
4829
4861
  `${employeeName}.md`
@@ -4853,7 +4885,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4853
4885
  }
4854
4886
  let sessionContextFlag = "";
4855
4887
  try {
4856
- const ctxDir = path15.join(os5.homedir(), ".exe-os", "session-cache");
4888
+ const ctxDir = path15.join(os6.homedir(), ".exe-os", "session-cache");
4857
4889
  mkdirSync6(ctxDir, { recursive: true });
4858
4890
  const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
4859
4891
  const ctxContent = [
@@ -4964,12 +4996,12 @@ var init_tmux_routing = __esm({
4964
4996
  init_provider_table();
4965
4997
  init_intercom_queue();
4966
4998
  init_plan_limits();
4967
- SPAWN_LOCK_DIR = path15.join(os5.homedir(), ".exe-os", "spawn-locks");
4968
- SESSION_CACHE = path15.join(os5.homedir(), ".exe-os", "session-cache");
4999
+ SPAWN_LOCK_DIR = path15.join(os6.homedir(), ".exe-os", "spawn-locks");
5000
+ SESSION_CACHE = path15.join(os6.homedir(), ".exe-os", "session-cache");
4969
5001
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
4970
5002
  VERIFY_PANE_LINES = 200;
4971
5003
  INTERCOM_DEBOUNCE_MS = 3e4;
4972
- INTERCOM_LOG2 = path15.join(os5.homedir(), ".exe-os", "intercom.log");
5004
+ INTERCOM_LOG2 = path15.join(os6.homedir(), ".exe-os", "intercom.log");
4973
5005
  DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
4974
5006
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
4975
5007
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
@@ -5458,7 +5490,7 @@ __export(agent_signals_exports, {
5458
5490
  hasUnreadInbox: () => hasUnreadInbox
5459
5491
  });
5460
5492
  import { readFileSync as readFileSync10, existsSync as existsSync13 } from "fs";
5461
- import os6 from "os";
5493
+ import os7 from "os";
5462
5494
  import path16 from "path";
5463
5495
  async function hasOpenTasks(client, agentId) {
5464
5496
  try {
@@ -5499,7 +5531,7 @@ async function hasUnreadInbox(client, agentId) {
5499
5531
  return CONSERVATIVE_ON_ERROR;
5500
5532
  }
5501
5533
  }
5502
- function hadRecentIntercomAck(sessionName, windowMs, nowMs = Date.now(), intercomLog = path16.join(os6.homedir(), ".exe-os", "intercom.log")) {
5534
+ function hadRecentIntercomAck(sessionName, windowMs, nowMs = Date.now(), intercomLog = path16.join(os7.homedir(), ".exe-os", "intercom.log")) {
5503
5535
  if (!existsSync13(intercomLog)) return false;
5504
5536
  try {
5505
5537
  const raw = readFileSync10(intercomLog, "utf8");
@@ -6190,6 +6222,10 @@ import path17 from "path";
6190
6222
  import { fileURLToPath as fileURLToPath2 } from "url";
6191
6223
  function handleData(chunk) {
6192
6224
  _buffer += chunk.toString();
6225
+ if (_buffer.length > MAX_BUFFER) {
6226
+ _buffer = "";
6227
+ return;
6228
+ }
6193
6229
  let newlineIdx;
6194
6230
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
6195
6231
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -6497,7 +6533,7 @@ function disconnectClient() {
6497
6533
  entry.resolve({ error: "Client disconnected" });
6498
6534
  }
6499
6535
  }
6500
- 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;
6536
+ 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;
6501
6537
  var init_exe_daemon_client = __esm({
6502
6538
  "src/lib/exe-daemon-client.ts"() {
6503
6539
  "use strict";
@@ -6514,6 +6550,7 @@ var init_exe_daemon_client = __esm({
6514
6550
  _requestCount = 0;
6515
6551
  HEALTH_CHECK_INTERVAL = 100;
6516
6552
  _pending = /* @__PURE__ */ new Map();
6553
+ MAX_BUFFER = 1e7;
6517
6554
  }
6518
6555
  });
6519
6556
 
@@ -7255,7 +7292,8 @@ async function wikiRequest(config, path21, method = "GET", body) {
7255
7292
  const response = await fetch(url, {
7256
7293
  method,
7257
7294
  headers,
7258
- body: body ? JSON.stringify(body) : void 0
7295
+ body: body ? JSON.stringify(body) : void 0,
7296
+ signal: AbortSignal.timeout(3e4)
7259
7297
  });
7260
7298
  if (!response.ok) {
7261
7299
  throw new Error(`Wiki API ${method} ${path21}: ${response.status} ${response.statusText}`);
@@ -7445,7 +7483,7 @@ __export(device_registry_exports, {
7445
7483
  setFriendlyName: () => setFriendlyName
7446
7484
  });
7447
7485
  import crypto9 from "crypto";
7448
- import os7 from "os";
7486
+ import os8 from "os";
7449
7487
  import { readFileSync as readFileSync13, writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, existsSync as existsSync15 } from "fs";
7450
7488
  import path19 from "path";
7451
7489
  function getDeviceInfo() {
@@ -7459,7 +7497,7 @@ function getDeviceInfo() {
7459
7497
  } catch {
7460
7498
  }
7461
7499
  }
7462
- const hostname = os7.hostname();
7500
+ const hostname = os8.hostname();
7463
7501
  const info = {
7464
7502
  deviceId: crypto9.randomUUID(),
7465
7503
  friendlyName: hostname.replace(/\./g, "-").toLowerCase(),
@@ -8122,8 +8160,13 @@ function startServer() {
8122
8160
  _activeConnections++;
8123
8161
  resetIdleTimer();
8124
8162
  let buffer = "";
8163
+ const MAX_BUFFER2 = 1e7;
8125
8164
  socket.on("data", (chunk) => {
8126
8165
  buffer += chunk.toString();
8166
+ if (buffer.length > MAX_BUFFER2) {
8167
+ socket.destroy(new Error("Buffer overflow \u2014 payload too large"));
8168
+ return;
8169
+ }
8127
8170
  let newlineIdx;
8128
8171
  while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
8129
8172
  const line = buffer.slice(0, newlineIdx).trim();
@@ -105,6 +105,7 @@ async function ensureSchema() {
105
105
  const client = getRawClient();
106
106
  await client.execute("PRAGMA journal_mode = WAL");
107
107
  await client.execute("PRAGMA busy_timeout = 30000");
108
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
108
109
  try {
109
110
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
110
111
  } catch {
@@ -916,9 +917,10 @@ var init_database = __esm({
916
917
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
917
918
  import { existsSync } from "fs";
918
919
  import path from "path";
920
+ import os from "os";
919
921
  import crypto from "crypto";
920
922
  function getKeyDir() {
921
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
923
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
922
924
  }
923
925
  function getKeyPath() {
924
926
  return path.join(getKeyDir(), "master.key");
@@ -977,15 +979,15 @@ __export(config_exports, {
977
979
  migrateConfig: () => migrateConfig,
978
980
  saveConfig: () => saveConfig
979
981
  });
980
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
982
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
981
983
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
982
984
  import path2 from "path";
983
- import os from "os";
985
+ import os2 from "os";
984
986
  function resolveDataDir() {
985
987
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
986
988
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
987
- const newDir = path2.join(os.homedir(), ".exe-os");
988
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
989
+ const newDir = path2.join(os2.homedir(), ".exe-os");
990
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
989
991
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
990
992
  try {
991
993
  renameSync(legacyDir, newDir);
@@ -1072,7 +1074,7 @@ async function loadConfig() {
1072
1074
  normalizeAutoUpdate(migratedCfg);
1073
1075
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
1074
1076
  if (config.dbPath.startsWith("~")) {
1075
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
1077
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
1076
1078
  }
1077
1079
  return config;
1078
1080
  } catch {
@@ -1103,6 +1105,9 @@ async function saveConfig(config) {
1103
1105
  await mkdir2(dir, { recursive: true });
1104
1106
  const configPath = path2.join(dir, "config.json");
1105
1107
  await writeFile2(configPath, JSON.stringify(config, null, 2) + "\n");
1108
+ if (config.cloud?.apiKey) {
1109
+ await chmod2(configPath, 384);
1110
+ }
1106
1111
  }
1107
1112
  async function loadConfigFrom(configPath) {
1108
1113
  const raw = await readFile2(configPath, "utf-8");
@@ -1462,6 +1467,28 @@ __export(store_exports, {
1462
1467
  vectorToBlob: () => vectorToBlob,
1463
1468
  writeMemory: () => writeMemory
1464
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
+ }
1465
1492
  async function initStore(options) {
1466
1493
  if (_flushTimer !== null) {
1467
1494
  clearInterval(_flushTimer);
@@ -1490,14 +1517,17 @@ async function initStore(options) {
1490
1517
  dbPath,
1491
1518
  encryptionKey: hexKey
1492
1519
  });
1493
- await ensureSchema();
1520
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1494
1521
  try {
1495
1522
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1496
1523
  initShardManager2(hexKey);
1497
1524
  } catch {
1498
1525
  }
1499
1526
  const client = getClient();
1500
- 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
+ );
1501
1531
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1502
1532
  }
1503
1533
  function classifyTier(record) {
@@ -1877,7 +1907,7 @@ async function getMemoryCardinality(agentId) {
1877
1907
  return 0;
1878
1908
  }
1879
1909
  }
1880
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1910
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1881
1911
  var init_store = __esm({
1882
1912
  "src/lib/store.ts"() {
1883
1913
  "use strict";
@@ -1885,6 +1915,8 @@ var init_store = __esm({
1885
1915
  init_database();
1886
1916
  init_keychain();
1887
1917
  init_config();
1918
+ INIT_MAX_RETRIES = 3;
1919
+ INIT_RETRY_DELAY_MS = 1e3;
1888
1920
  _pendingRecords = [];
1889
1921
  _batchSize = 20;
1890
1922
  _flushIntervalMs = 1e4;
@@ -1992,6 +2024,10 @@ import path4 from "path";
1992
2024
  import { fileURLToPath } from "url";
1993
2025
  function handleData(chunk) {
1994
2026
  _buffer += chunk.toString();
2027
+ if (_buffer.length > MAX_BUFFER) {
2028
+ _buffer = "";
2029
+ return;
2030
+ }
1995
2031
  let newlineIdx;
1996
2032
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
1997
2033
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -2299,7 +2335,7 @@ function disconnectClient() {
2299
2335
  entry.resolve({ error: "Client disconnected" });
2300
2336
  }
2301
2337
  }
2302
- 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;
2303
2339
  var init_exe_daemon_client = __esm({
2304
2340
  "src/lib/exe-daemon-client.ts"() {
2305
2341
  "use strict";
@@ -2316,6 +2352,7 @@ var init_exe_daemon_client = __esm({
2316
2352
  _requestCount = 0;
2317
2353
  HEALTH_CHECK_INTERVAL = 100;
2318
2354
  _pending = /* @__PURE__ */ new Map();
2355
+ MAX_BUFFER = 1e7;
2319
2356
  }
2320
2357
  });
2321
2358
 
@@ -5,7 +5,7 @@ import path2 from "path";
5
5
  import { createHash } from "crypto";
6
6
 
7
7
  // src/lib/config.ts
8
- import { readFile, writeFile, mkdir } from "fs/promises";
8
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
9
9
  import { readFileSync, existsSync, renameSync } from "fs";
10
10
  import path from "path";
11
11
  import os from "os";
@@ -9,11 +9,12 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
9
9
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
10
10
  import { existsSync } from "fs";
11
11
  import path from "path";
12
+ import os from "os";
12
13
  import crypto from "crypto";
13
14
  var SERVICE = "exe-mem";
14
15
  var ACCOUNT = "master-key";
15
16
  function getKeyDir() {
16
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
17
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
17
18
  }
18
19
  function getKeyPath() {
19
20
  return path.join(getKeyDir(), "master.key");