@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
@@ -1,4 +1,12 @@
1
1
  // src/lib/identity-templates.ts
2
+ var PLAN_MODE_COMPAT = `
3
+ ## Plan Mode Compatibility
4
+ If tool execution is unavailable (e.g., CC plan mode), switch to planning:
5
+ - Reason about the task and create a written plan
6
+ - Document what tools you would call and with what parameters
7
+ - Output structured text that can be acted on when tools become available
8
+ Do not repeatedly attempt tool calls that fail \u2014 switch to planning mode.
9
+ `;
2
10
  var POST_WORK_CHECKLIST = `
3
11
  5. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
4
12
  6. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
@@ -78,7 +86,7 @@ Never say "I have no memories" without first searching broadly. Your memory may
78
86
  - **update_identity** \u2014 rewrite any agent's identity when role/responsibilities change (exe/founder only)
79
87
  - **get_identity** \u2014 read any agent's identity for coordination
80
88
  - **send_message** \u2014 direct intercom to employees
81
-
89
+ ${PLAN_MODE_COMPAT}
82
90
  ## Completion Workflow
83
91
 
84
92
  1. Read the task file and verify the deliverable matches the brief
@@ -149,7 +157,7 @@ You are \${agent_id}. CTO. You hold deep context on the entire codebase, archite
149
157
  - **store_behavior** \u2014 record corrections for engineers (p0 = always injected)
150
158
  - **get_identity** \u2014 read any agent's identity for review context
151
159
  - **query_relationships** \u2014 GraphRAG entity connections for architecture analysis
152
-
160
+ ${PLAN_MODE_COMPAT}
153
161
  ## Completion Workflow
154
162
 
155
163
  1. Read ARCHITECTURE.md before starting work on any repo
@@ -216,7 +224,7 @@ You are \${agent_id}. CMO. You hold deep context on design, branding, storytelli
216
224
  - **update_task** \u2014 mark tasks done with result summary
217
225
  - **store_memory** \u2014 report completions with brand alignment notes, SEO considerations
218
226
  - **get_identity** \u2014 read team identities for brand-consistent communication
219
-
227
+ ${PLAN_MODE_COMPAT}
220
228
  ## Completion Workflow
221
229
 
222
230
  1. Read the task file and understand the brief \u2014 tone, format, channel requirements
@@ -283,7 +291,7 @@ You are a principal engineer. You write production-grade code with zero shortcut
283
291
  - **recall_my_memory** \u2014 check past work, patterns, gotchas in this project
284
292
  - **store_memory** \u2014 report completions for org visibility
285
293
  - **ask_team_memory** \u2014 pull context from colleagues when specs reference their work
286
-
294
+ ${PLAN_MODE_COMPAT}
287
295
  ## Completion Workflow
288
296
 
289
297
  1. Read ARCHITECTURE.md if it exists \u2014 understand architecture before changing anything
@@ -343,7 +351,7 @@ You are the content production specialist. You turn scripts and creative briefs
343
351
  - **update_task** \u2014 mark tasks done with result summary
344
352
  - **recall_my_memory** \u2014 check past work: which models worked, which prompts produced good results
345
353
  - **store_memory** \u2014 report completions with production decisions for future reference
346
-
354
+ ${PLAN_MODE_COMPAT}
347
355
  ## Completion Workflow
348
356
 
349
357
  1. Read the task file \u2014 understand the brief, check budget constraints
@@ -415,7 +423,7 @@ You are the AI Product Lead \u2014 the competitive intelligence engine. You stud
415
423
  - **update_task** \u2014 mark tasks done with analysis results
416
424
  - **store_memory** \u2014 persist competitive analyses, evaluations, recommendations
417
425
  - **create_task** \u2014 when a feature is worth building, spec it for the CTO
418
-
426
+ ${PLAN_MODE_COMPAT}
419
427
  ## Completion Workflow
420
428
 
421
429
  1. Read the task \u2014 understand what capability is needed
@@ -478,7 +486,7 @@ You are \${agent_id}. Staff Code Reviewer and System Auditor. Last line of defen
478
486
  - **store_behavior** \u2014 record new patterns
479
487
  - **update_task** \u2014 mark reviews done with structured findings
480
488
  - **create_task** \u2014 assign fixes to the CTO
481
-
489
+ ${PLAN_MODE_COMPAT}
482
490
  ## Completion Workflow
483
491
 
484
492
  1. Read the task brief and understand the audit scope
@@ -507,6 +515,7 @@ function getTemplateForTitle(title) {
507
515
  }
508
516
  export {
509
517
  IDENTITY_TEMPLATES,
518
+ PLAN_MODE_COMPAT,
510
519
  POST_WORK_CHECKLIST,
511
520
  getTemplate,
512
521
  getTemplateForTitle
@@ -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");
@@ -1,3 +1,10 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
1
8
  // src/lib/license.ts
2
9
  import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
3
10
  import { randomUUID } from "crypto";
@@ -5,7 +12,7 @@ import path2 from "path";
5
12
  import { jwtVerify, importSPKI } from "jose";
6
13
 
7
14
  // src/lib/config.ts
8
- import { readFile, writeFile, mkdir } from "fs/promises";
15
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
9
16
  import { readFileSync, existsSync, renameSync } from "fs";
10
17
  import path from "path";
11
18
  import os from "os";
@@ -100,6 +107,15 @@ var LICENSE_PATH = path2.join(EXE_AI_DIR, "license.key");
100
107
  var CACHE_PATH = path2.join(EXE_AI_DIR, "license-cache.json");
101
108
  var DEVICE_ID_PATH = path2.join(EXE_AI_DIR, "device-id");
102
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
+ }
103
119
  var LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
104
120
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
105
121
  4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
@@ -152,7 +168,7 @@ function loadLicense() {
152
168
  }
153
169
  function saveLicense(apiKey) {
154
170
  mkdirSync(EXE_AI_DIR, { recursive: true });
155
- writeFileSync(LICENSE_PATH, apiKey.trim(), "utf8");
171
+ writeFileSync(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
156
172
  }
157
173
  async function verifyLicenseJwt(token) {
158
174
  try {
@@ -204,7 +220,7 @@ function cacheResponse(token) {
204
220
  async function validateLicense(apiKey, deviceId) {
205
221
  const did = deviceId ?? loadDeviceId();
206
222
  try {
207
- const res = await fetch(`${API_BASE}/auth/activate`, {
223
+ const res = await fetchRetry(`${API_BASE}/auth/activate`, {
208
224
  method: "POST",
209
225
  headers: { "Content-Type": "application/json" },
210
226
  body: JSON.stringify({ apiKey, deviceId: did }),
@@ -239,14 +255,24 @@ async function validateLicense(apiKey, deviceId) {
239
255
  } catch {
240
256
  const cached = await getCachedLicense();
241
257
  if (cached) return cached;
242
- return FREE_LICENSE;
258
+ return { ...FREE_LICENSE, valid: false, error: "offline" };
259
+ }
260
+ }
261
+ var CACHE_MAX_AGE_MS = 36e5;
262
+ function getCacheAgeMs() {
263
+ try {
264
+ const { statSync } = __require("fs");
265
+ const s = statSync(CACHE_PATH);
266
+ return Date.now() - s.mtimeMs;
267
+ } catch {
268
+ return Infinity;
243
269
  }
244
270
  }
245
271
  async function checkLicense() {
246
272
  const key = loadLicense();
247
273
  if (!key) return FREE_LICENSE;
248
274
  const cached = await getCachedLicense();
249
- if (cached) return cached;
275
+ if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
250
276
  const deviceId = loadDeviceId();
251
277
  return validateLicense(key, deviceId);
252
278
  }
@@ -286,7 +312,7 @@ async function assertVpsLicense(opts) {
286
312
  let explicitRejection = false;
287
313
  let transientFailure = false;
288
314
  try {
289
- const res = await fetch(`${API_BASE}/auth/activate`, {
315
+ const res = await fetchRetry(`${API_BASE}/auth/activate`, {
290
316
  method: "POST",
291
317
  headers: { "Content-Type": "application/json" },
292
318
  body: JSON.stringify({ apiKey, deviceId }),
@@ -367,6 +393,28 @@ async function assertVpsLicense(opts) {
367
393
  `License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com/cloud and retry. This VPS image refuses to boot after the offline grace window.`
368
394
  );
369
395
  }
396
+ var _revalTimer = null;
397
+ function startLicenseRevalidation(intervalMs = 36e5) {
398
+ if (_revalTimer) return;
399
+ _revalTimer = setInterval(async () => {
400
+ try {
401
+ const license = await checkLicense();
402
+ if (!license.valid) {
403
+ process.stderr.write("[exe-os] License expired or invalid \u2014 features may be restricted\n");
404
+ }
405
+ } catch {
406
+ }
407
+ }, intervalMs);
408
+ if (_revalTimer && typeof _revalTimer === "object" && "unref" in _revalTimer) {
409
+ _revalTimer.unref();
410
+ }
411
+ }
412
+ function stopLicenseRevalidation() {
413
+ if (_revalTimer) {
414
+ clearInterval(_revalTimer);
415
+ _revalTimer = null;
416
+ }
417
+ }
370
418
  export {
371
419
  LICENSE_PUBLIC_KEY_PEM,
372
420
  PLAN_LIMITS,
@@ -378,5 +426,7 @@ export {
378
426
  loadLicense,
379
427
  mirrorLicenseKey,
380
428
  saveLicense,
429
+ startLicenseRevalidation,
430
+ stopLicenseRevalidation,
381
431
  validateLicense
382
432
  };
@@ -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
  }
@@ -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,15 +9,15 @@ var __export = (target, all) => {
15
9
  };
16
10
 
17
11
  // src/lib/config.ts
18
- 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";
19
13
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
20
14
  import path2 from "path";
21
- import os from "os";
15
+ import os2 from "os";
22
16
  function resolveDataDir() {
23
17
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
24
18
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
25
- const newDir = path2.join(os.homedir(), ".exe-os");
26
- 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");
27
21
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
28
22
  try {
29
23
  renameSync(legacyDir, newDir);
@@ -110,7 +104,7 @@ async function loadConfig() {
110
104
  normalizeAutoUpdate(migratedCfg);
111
105
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
112
106
  if (config.dbPath.startsWith("~")) {
113
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
107
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
114
108
  }
115
109
  return config;
116
110
  } catch {
@@ -217,7 +211,7 @@ __export(shard_manager_exports, {
217
211
  shardExists: () => shardExists
218
212
  });
219
213
  import path3 from "path";
220
- import { existsSync as existsSync3, mkdirSync } from "fs";
214
+ import { existsSync as existsSync3, mkdirSync, readdirSync } from "fs";
221
215
  import { createClient as createClient2 } from "@libsql/client";
222
216
  function initShardManager(encryptionKey) {
223
217
  _encryptionKey = encryptionKey;
@@ -256,7 +250,6 @@ function shardExists(projectName) {
256
250
  }
257
251
  function listShards() {
258
252
  if (!existsSync3(SHARDS_DIR)) return [];
259
- const { readdirSync } = __require("fs");
260
253
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
261
254
  }
262
255
  async function ensureShardSchema(client) {
@@ -539,6 +532,7 @@ async function ensureSchema() {
539
532
  const client = getRawClient();
540
533
  await client.execute("PRAGMA journal_mode = WAL");
541
534
  await client.execute("PRAGMA busy_timeout = 30000");
535
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
542
536
  try {
543
537
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
544
538
  } catch {
@@ -1332,11 +1326,12 @@ async function ensureSchema() {
1332
1326
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1333
1327
  import { existsSync } from "fs";
1334
1328
  import path from "path";
1329
+ import os from "os";
1335
1330
  import crypto from "crypto";
1336
1331
  var SERVICE = "exe-mem";
1337
1332
  var ACCOUNT = "master-key";
1338
1333
  function getKeyDir() {
1339
- 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");
1340
1335
  }
1341
1336
  function getKeyPath() {
1342
1337
  return path.join(getKeyDir(), "master.key");
@@ -1373,6 +1368,30 @@ async function getMasterKey() {
1373
1368
 
1374
1369
  // src/lib/store.ts
1375
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
+ }
1376
1395
  var _pendingRecords = [];
1377
1396
  var _batchSize = 20;
1378
1397
  var _flushIntervalMs = 1e4;
@@ -1407,14 +1426,17 @@ async function initStore(options) {
1407
1426
  dbPath,
1408
1427
  encryptionKey: hexKey
1409
1428
  });
1410
- await ensureSchema();
1429
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1411
1430
  try {
1412
1431
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1413
1432
  initShardManager2(hexKey);
1414
1433
  } catch {
1415
1434
  }
1416
1435
  const client = getClient();
1417
- 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
+ );
1418
1440
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1419
1441
  }
1420
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
@@ -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,15 +9,15 @@ var __export = (target, all) => {
15
9
  };
16
10
 
17
11
  // src/lib/config.ts
18
- 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";
19
13
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
20
14
  import path2 from "path";
21
- import os from "os";
15
+ import os2 from "os";
22
16
  function resolveDataDir() {
23
17
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
24
18
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
25
- const newDir = path2.join(os.homedir(), ".exe-os");
26
- 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");
27
21
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
28
22
  try {
29
23
  renameSync(legacyDir, newDir);
@@ -110,7 +104,7 @@ async function loadConfig() {
110
104
  normalizeAutoUpdate(migratedCfg);
111
105
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
112
106
  if (config.dbPath.startsWith("~")) {
113
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
107
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
114
108
  }
115
109
  return config;
116
110
  } catch {
@@ -217,7 +211,7 @@ __export(shard_manager_exports, {
217
211
  shardExists: () => shardExists
218
212
  });
219
213
  import path3 from "path";
220
- import { existsSync as existsSync3, mkdirSync } from "fs";
214
+ import { existsSync as existsSync3, mkdirSync, readdirSync } from "fs";
221
215
  import { createClient as createClient2 } from "@libsql/client";
222
216
  function initShardManager(encryptionKey) {
223
217
  _encryptionKey = encryptionKey;
@@ -256,7 +250,6 @@ function shardExists(projectName) {
256
250
  }
257
251
  function listShards() {
258
252
  if (!existsSync3(SHARDS_DIR)) return [];
259
- const { readdirSync } = __require("fs");
260
253
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
261
254
  }
262
255
  async function ensureShardSchema(client) {
@@ -535,6 +528,7 @@ async function ensureSchema() {
535
528
  const client = getRawClient();
536
529
  await client.execute("PRAGMA journal_mode = WAL");
537
530
  await client.execute("PRAGMA busy_timeout = 30000");
531
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
538
532
  try {
539
533
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
540
534
  } catch {
@@ -1336,11 +1330,12 @@ async function disposeDatabase() {
1336
1330
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1337
1331
  import { existsSync } from "fs";
1338
1332
  import path from "path";
1333
+ import os from "os";
1339
1334
  import crypto from "crypto";
1340
1335
  var SERVICE = "exe-mem";
1341
1336
  var ACCOUNT = "master-key";
1342
1337
  function getKeyDir() {
1343
- 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");
1344
1339
  }
1345
1340
  function getKeyPath() {
1346
1341
  return path.join(getKeyDir(), "master.key");
@@ -1377,6 +1372,30 @@ async function getMasterKey() {
1377
1372
 
1378
1373
  // src/lib/store.ts
1379
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
+ }
1380
1399
  var _pendingRecords = [];
1381
1400
  var _batchSize = 20;
1382
1401
  var _flushIntervalMs = 1e4;
@@ -1411,14 +1430,17 @@ async function initStore(options) {
1411
1430
  dbPath,
1412
1431
  encryptionKey: hexKey
1413
1432
  });
1414
- await ensureSchema();
1433
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1415
1434
  try {
1416
1435
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1417
1436
  initShardManager2(hexKey);
1418
1437
  } catch {
1419
1438
  }
1420
1439
  const client = getClient();
1421
- 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
+ );
1422
1444
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1423
1445
  }
1424
1446
  function classifyTier(record) {
@@ -1461,6 +1483,12 @@ async function writeMemory(record) {
1461
1483
  supersedes_id: record.supersedes_id ?? null
1462
1484
  };
1463
1485
  _pendingRecords.push(dbRow);
1486
+ const MAX_PENDING = 1e3;
1487
+ if (_pendingRecords.length > MAX_PENDING) {
1488
+ const dropped = _pendingRecords.length - MAX_PENDING;
1489
+ _pendingRecords = _pendingRecords.slice(-MAX_PENDING);
1490
+ console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
1491
+ }
1464
1492
  if (_flushTimer === null) {
1465
1493
  _flushTimer = setInterval(() => {
1466
1494
  void flushBatch();
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";