@askexenow/exe-os 0.9.102 → 0.9.104

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 (92) hide show
  1. package/dist/bin/agentic-ontology-backfill.js +334 -100
  2. package/dist/bin/agentic-reflection-backfill.js +328 -97
  3. package/dist/bin/agentic-semantic-label.js +328 -97
  4. package/dist/bin/backfill-conversations.js +332 -97
  5. package/dist/bin/backfill-responses.js +332 -97
  6. package/dist/bin/backfill-vectors.js +337 -106
  7. package/dist/bin/bulk-sync-postgres.js +335 -101
  8. package/dist/bin/cleanup-stale-review-tasks.js +356 -108
  9. package/dist/bin/cli.js +653 -405
  10. package/dist/bin/exe-agent.js +21 -3
  11. package/dist/bin/exe-assign.js +338 -94
  12. package/dist/bin/exe-boot.js +472 -239
  13. package/dist/bin/exe-call.js +22 -5
  14. package/dist/bin/exe-cloud.js +404 -158
  15. package/dist/bin/exe-dispatch.js +390 -142
  16. package/dist/bin/exe-doctor.js +349 -103
  17. package/dist/bin/exe-export-behaviors.js +351 -105
  18. package/dist/bin/exe-forget.js +352 -103
  19. package/dist/bin/exe-gateway.js +420 -172
  20. package/dist/bin/exe-heartbeat.js +361 -113
  21. package/dist/bin/exe-kill.js +344 -98
  22. package/dist/bin/exe-launch-agent.js +375 -129
  23. package/dist/bin/exe-new-employee.js +83 -67
  24. package/dist/bin/exe-pending-messages.js +356 -108
  25. package/dist/bin/exe-pending-notifications.js +358 -110
  26. package/dist/bin/exe-pending-reviews.js +359 -111
  27. package/dist/bin/exe-rename.js +354 -108
  28. package/dist/bin/exe-review.js +343 -97
  29. package/dist/bin/exe-search.js +363 -113
  30. package/dist/bin/exe-session-cleanup.js +403 -155
  31. package/dist/bin/exe-settings.js +14 -9
  32. package/dist/bin/exe-start-codex.js +365 -131
  33. package/dist/bin/exe-start-opencode.js +359 -125
  34. package/dist/bin/exe-status.js +356 -108
  35. package/dist/bin/exe-team.js +343 -97
  36. package/dist/bin/git-sweep.js +390 -142
  37. package/dist/bin/graph-backfill.js +334 -100
  38. package/dist/bin/graph-export.js +346 -100
  39. package/dist/bin/install.js +1 -0
  40. package/dist/bin/intercom-check.js +403 -155
  41. package/dist/bin/pre-publish.js +12 -0
  42. package/dist/bin/scan-tasks.js +393 -145
  43. package/dist/bin/setup.js +331 -159
  44. package/dist/bin/shard-migrate.js +328 -94
  45. package/dist/gateway/index.js +406 -158
  46. package/dist/hooks/bug-report-worker.js +396 -148
  47. package/dist/hooks/codex-stop-task-finalizer.js +374 -126
  48. package/dist/hooks/commit-complete.js +390 -142
  49. package/dist/hooks/error-recall.js +365 -115
  50. package/dist/hooks/ingest.js +357 -111
  51. package/dist/hooks/instructions-loaded.js +351 -105
  52. package/dist/hooks/notification.js +343 -97
  53. package/dist/hooks/post-compact.js +358 -110
  54. package/dist/hooks/post-tool-combined.js +384 -132
  55. package/dist/hooks/pre-compact.js +391 -143
  56. package/dist/hooks/pre-tool-use.js +362 -114
  57. package/dist/hooks/prompt-submit.js +422 -170
  58. package/dist/hooks/session-end.js +393 -145
  59. package/dist/hooks/session-start.js +390 -138
  60. package/dist/hooks/stop.js +361 -113
  61. package/dist/hooks/subagent-stop.js +354 -106
  62. package/dist/hooks/summary-worker.js +418 -185
  63. package/dist/index.js +400 -152
  64. package/dist/lib/cloud-sync.js +291 -131
  65. package/dist/lib/consolidation.js +8 -2
  66. package/dist/lib/database.js +233 -73
  67. package/dist/lib/db.js +233 -73
  68. package/dist/lib/device-registry.js +237 -77
  69. package/dist/lib/employee-templates.js +19 -1
  70. package/dist/lib/exe-daemon.js +705 -409
  71. package/dist/lib/hybrid-search.js +363 -113
  72. package/dist/lib/identity.js +9 -5
  73. package/dist/lib/messaging.js +26 -20
  74. package/dist/lib/reminders.js +5 -1
  75. package/dist/lib/schedules.js +320 -89
  76. package/dist/lib/skill-learning.js +28 -24
  77. package/dist/lib/store.js +342 -96
  78. package/dist/lib/tasks.js +82 -76
  79. package/dist/lib/tmux-routing.js +74 -68
  80. package/dist/lib/token-spend.js +5 -1
  81. package/dist/mcp/server.js +628 -355
  82. package/dist/mcp/tools/complete-reminder.js +5 -1
  83. package/dist/mcp/tools/create-reminder.js +5 -1
  84. package/dist/mcp/tools/create-task.js +89 -83
  85. package/dist/mcp/tools/deactivate-behavior.js +7 -3
  86. package/dist/mcp/tools/list-reminders.js +5 -1
  87. package/dist/mcp/tools/list-tasks.js +28 -21
  88. package/dist/mcp/tools/send-message.js +28 -22
  89. package/dist/mcp/tools/update-task.js +89 -83
  90. package/dist/runtime/index.js +390 -142
  91. package/dist/tui/App.js +437 -189
  92. package/package.json +1 -1
@@ -1970,9 +1970,79 @@ __export(database_exports, {
1970
1970
  isInitialized: () => isInitialized,
1971
1971
  setExternalClient: () => setExternalClient
1972
1972
  });
1973
- import { chmodSync as chmodSync2 } from "fs";
1973
+ import { chmodSync as chmodSync2, existsSync as existsSync8, statSync as statSync2, copyFileSync, unlinkSync as unlinkSync4, openSync as openSync2, closeSync as closeSync2, mkdirSync as mkdirSync4 } from "fs";
1974
1974
  import { createClient } from "@libsql/client";
1975
+ import { homedir } from "os";
1976
+ import { join } from "path";
1977
+ function logCatchDebug(context, err) {
1978
+ if (_debugDb) {
1979
+ process.stderr.write(
1980
+ `[database] ${context}: ${err instanceof Error ? err.message : String(err)}
1981
+ `
1982
+ );
1983
+ }
1984
+ }
1985
+ function acquireDbLock() {
1986
+ mkdirSync4(join(homedir(), ".exe-os"), { recursive: true });
1987
+ try {
1988
+ _lockFd = openSync2(DB_LOCK_PATH, "wx");
1989
+ } catch (err) {
1990
+ if (err && typeof err === "object" && "code" in err && err.code === "EEXIST") {
1991
+ try {
1992
+ const lockStat = statSync2(DB_LOCK_PATH);
1993
+ if (Date.now() - lockStat.mtimeMs > 6e4) {
1994
+ unlinkSync4(DB_LOCK_PATH);
1995
+ _lockFd = openSync2(DB_LOCK_PATH, "wx");
1996
+ return;
1997
+ }
1998
+ } catch (e) {
1999
+ logCatchDebug("stale lock check", e);
2000
+ }
2001
+ process.stderr.write(
2002
+ "[database] WARN: Another process holds db.lock \u2014 waiting briefly then proceeding.\n"
2003
+ );
2004
+ return;
2005
+ }
2006
+ throw err;
2007
+ }
2008
+ }
2009
+ function releaseDbLock() {
2010
+ if (_lockFd !== null) {
2011
+ try {
2012
+ closeSync2(_lockFd);
2013
+ } catch (e) {
2014
+ logCatchDebug("lock close", e);
2015
+ }
2016
+ _lockFd = null;
2017
+ }
2018
+ try {
2019
+ unlinkSync4(DB_LOCK_PATH);
2020
+ } catch (e) {
2021
+ logCatchDebug("lock unlink", e);
2022
+ }
2023
+ }
1975
2024
  async function initDatabase(config) {
2025
+ acquireDbLock();
2026
+ if (existsSync8(config.dbPath)) {
2027
+ const dbStat = statSync2(config.dbPath);
2028
+ if (dbStat.size === 0) {
2029
+ const walPath = config.dbPath + "-wal";
2030
+ if (existsSync8(walPath) && statSync2(walPath).size > 0) {
2031
+ const backupPath = config.dbPath + ".zeroed-" + Date.now();
2032
+ copyFileSync(config.dbPath, backupPath);
2033
+ unlinkSync4(config.dbPath);
2034
+ process.stderr.write(
2035
+ `[database] CRITICAL: DB was 0 bytes. Moved to ${backupPath}, attempting WAL recovery.
2036
+ `
2037
+ );
2038
+ } else {
2039
+ process.stderr.write(
2040
+ `[database] CRITICAL: DB is 0 bytes and no WAL available for recovery. Data may be lost. Check backups at ${config.dbPath}.bak
2041
+ `
2042
+ );
2043
+ }
2044
+ }
2045
+ }
1976
2046
  if (_walCheckpointTimer) {
1977
2047
  clearInterval(_walCheckpointTimer);
1978
2048
  _walCheckpointTimer = null;
@@ -1999,10 +2069,8 @@ async function initDatabase(config) {
1999
2069
  _client = createClient(opts);
2000
2070
  _resilientClient = wrapWithRetry(_client);
2001
2071
  _adapterClient = _resilientClient;
2002
- _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
2003
- });
2004
- _client.execute("PRAGMA journal_mode = WAL").catch(() => {
2005
- });
2072
+ await _client.execute("PRAGMA busy_timeout = 30000");
2073
+ await _client.execute("PRAGMA journal_mode = WAL");
2006
2074
  if (_walCheckpointTimer) clearInterval(_walCheckpointTimer);
2007
2075
  _walCheckpointTimer = setInterval(() => {
2008
2076
  _client?.execute("PRAGMA wal_checkpoint(PASSIVE)").catch(() => {
@@ -2017,11 +2085,16 @@ async function initDatabase(config) {
2017
2085
  for (const suffix of ["-wal", "-shm"]) {
2018
2086
  try {
2019
2087
  chmodSync2(config.dbPath + suffix, 384);
2020
- } catch {
2088
+ } catch (chmodErr) {
2089
+ process.stderr.write(`[database] chmod ${suffix} failed: ${chmodErr instanceof Error ? chmodErr.message : String(chmodErr)}
2090
+ `);
2021
2091
  }
2022
2092
  }
2023
- } catch {
2093
+ } catch (chmodErr) {
2094
+ process.stderr.write(`[database] chmod db failed: ${chmodErr instanceof Error ? chmodErr.message : String(chmodErr)}
2095
+ `);
2024
2096
  }
2097
+ releaseDbLock();
2025
2098
  }
2026
2099
  function isInitialized() {
2027
2100
  return _adapterClient !== null || _client !== null;
@@ -2076,7 +2149,8 @@ async function ensureSchema() {
2076
2149
  await client.execute("PRAGMA wal_autocheckpoint = 1000");
2077
2150
  try {
2078
2151
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
2079
- } catch {
2152
+ } catch (e) {
2153
+ logCatchDebug("migration", e);
2080
2154
  }
2081
2155
  await client.executeMultiple(`
2082
2156
  CREATE TABLE IF NOT EXISTS memories (
@@ -2141,6 +2215,23 @@ async function ensureSchema() {
2141
2215
  INSERT INTO memories_fts(memories_fts, rowid, raw_text) VALUES('delete', old.rowid, old.raw_text);
2142
2216
  END;
2143
2217
  `);
2218
+ try {
2219
+ await client.execute("SELECT COUNT(*) FROM memories_fts LIMIT 1");
2220
+ } catch (ftsErr) {
2221
+ process.stderr.write(
2222
+ `[database] WARN: memories_fts corrupted (${ftsErr instanceof Error ? ftsErr.message : String(ftsErr)}) \u2014 rebuilding FTS index.
2223
+ `
2224
+ );
2225
+ try {
2226
+ await client.execute("INSERT INTO memories_fts(memories_fts) VALUES('rebuild')");
2227
+ process.stderr.write("[database] FTS index rebuilt successfully.\n");
2228
+ } catch (rebuildErr) {
2229
+ process.stderr.write(
2230
+ `[database] ERROR: FTS rebuild failed: ${rebuildErr instanceof Error ? rebuildErr.message : String(rebuildErr)}
2231
+ `
2232
+ );
2233
+ }
2234
+ }
2144
2235
  await client.executeMultiple(`
2145
2236
  CREATE TABLE IF NOT EXISTS sync_meta (
2146
2237
  key TEXT PRIMARY KEY,
@@ -2199,35 +2290,40 @@ async function ensureSchema() {
2199
2290
  });
2200
2291
  }
2201
2292
  }
2202
- } catch {
2293
+ } catch (seedErr) {
2294
+ logCatchDebug("behavior seed", seedErr);
2203
2295
  }
2204
2296
  try {
2205
2297
  await client.execute({
2206
2298
  sql: `ALTER TABLE behaviors ADD COLUMN priority TEXT DEFAULT 'p1'`,
2207
2299
  args: []
2208
2300
  });
2209
- } catch {
2301
+ } catch (e) {
2302
+ logCatchDebug("migration", e);
2210
2303
  }
2211
2304
  try {
2212
2305
  await client.execute({
2213
2306
  sql: `ALTER TABLE behaviors ADD COLUMN vector F32_BLOB(${EMBEDDING_DIM})`,
2214
2307
  args: []
2215
2308
  });
2216
- } catch {
2309
+ } catch (e) {
2310
+ logCatchDebug("migration", e);
2217
2311
  }
2218
2312
  try {
2219
2313
  await client.execute({
2220
2314
  sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
2221
2315
  args: []
2222
2316
  });
2223
- } catch {
2317
+ } catch (e) {
2318
+ logCatchDebug("migration", e);
2224
2319
  }
2225
2320
  try {
2226
2321
  await client.execute({
2227
2322
  sql: `ALTER TABLE tasks ADD COLUMN parent_task_id TEXT`,
2228
2323
  args: []
2229
2324
  });
2230
- } catch {
2325
+ } catch (e) {
2326
+ logCatchDebug("migration", e);
2231
2327
  }
2232
2328
  try {
2233
2329
  await client.execute({
@@ -2236,98 +2332,112 @@ async function ensureSchema() {
2236
2332
  WHERE parent_task_id IS NOT NULL`,
2237
2333
  args: []
2238
2334
  });
2239
- } catch {
2335
+ } catch (e) {
2336
+ logCatchDebug("migration", e);
2240
2337
  }
2241
2338
  try {
2242
2339
  await client.execute({
2243
2340
  sql: `UPDATE tasks SET status = 'done' WHERE status = 'completed'`,
2244
2341
  args: []
2245
2342
  });
2246
- } catch {
2343
+ } catch (e) {
2344
+ logCatchDebug("migration", e);
2247
2345
  }
2248
2346
  try {
2249
2347
  await client.execute({
2250
2348
  sql: `ALTER TABLE tasks ADD COLUMN reviewer TEXT`,
2251
2349
  args: []
2252
2350
  });
2253
- } catch {
2351
+ } catch (e) {
2352
+ logCatchDebug("migration", e);
2254
2353
  }
2255
2354
  try {
2256
2355
  await client.execute({
2257
2356
  sql: `ALTER TABLE tasks ADD COLUMN context TEXT`,
2258
2357
  args: []
2259
2358
  });
2260
- } catch {
2359
+ } catch (e) {
2360
+ logCatchDebug("migration", e);
2261
2361
  }
2262
2362
  try {
2263
2363
  await client.execute({
2264
2364
  sql: `ALTER TABLE tasks ADD COLUMN result TEXT`,
2265
2365
  args: []
2266
2366
  });
2267
- } catch {
2367
+ } catch (e) {
2368
+ logCatchDebug("migration", e);
2268
2369
  }
2269
2370
  try {
2270
2371
  await client.execute({
2271
2372
  sql: `ALTER TABLE tasks ADD COLUMN assigned_tmux TEXT`,
2272
2373
  args: []
2273
2374
  });
2274
- } catch {
2375
+ } catch (e) {
2376
+ logCatchDebug("migration", e);
2275
2377
  }
2276
2378
  try {
2277
2379
  await client.execute({
2278
2380
  sql: `ALTER TABLE tasks ADD COLUMN checkpoint TEXT`,
2279
2381
  args: []
2280
2382
  });
2281
- } catch {
2383
+ } catch (e) {
2384
+ logCatchDebug("migration", e);
2282
2385
  }
2283
2386
  try {
2284
2387
  await client.execute({
2285
2388
  sql: `ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER NOT NULL DEFAULT 0`,
2286
2389
  args: []
2287
2390
  });
2288
- } catch {
2391
+ } catch (e) {
2392
+ logCatchDebug("migration", e);
2289
2393
  }
2290
2394
  try {
2291
2395
  await client.execute({
2292
2396
  sql: `ALTER TABLE tasks ADD COLUMN complexity TEXT NOT NULL DEFAULT 'standard'`,
2293
2397
  args: []
2294
2398
  });
2295
- } catch {
2399
+ } catch (e) {
2400
+ logCatchDebug("migration", e);
2296
2401
  }
2297
2402
  try {
2298
2403
  await client.execute({
2299
2404
  sql: `ALTER TABLE tasks ADD COLUMN session_scope TEXT`,
2300
2405
  args: []
2301
2406
  });
2302
- } catch {
2407
+ } catch (e) {
2408
+ logCatchDebug("migration", e);
2303
2409
  }
2304
2410
  try {
2305
2411
  await client.execute({
2306
2412
  sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
2307
2413
  args: []
2308
2414
  });
2309
- } catch {
2415
+ } catch (e) {
2416
+ logCatchDebug("migration", e);
2310
2417
  }
2311
2418
  try {
2312
2419
  await client.execute({
2313
2420
  sql: `ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0`,
2314
2421
  args: []
2315
2422
  });
2316
- } catch {
2423
+ } catch (e) {
2424
+ logCatchDebug("migration", e);
2317
2425
  }
2318
2426
  try {
2319
2427
  await client.execute({
2320
2428
  sql: `ALTER TABLE memories ADD COLUMN author_device_id TEXT`,
2321
2429
  args: []
2322
2430
  });
2323
- } catch {
2431
+ } catch (e) {
2432
+ logCatchDebug("migration", e);
2324
2433
  }
2325
2434
  try {
2326
2435
  await client.execute({
2327
2436
  sql: `ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'`,
2328
2437
  args: []
2329
2438
  });
2330
- } catch {
2439
+ } catch (e) {
2440
+ logCatchDebug("migration", e);
2331
2441
  }
2332
2442
  await client.executeMultiple(`
2333
2443
  CREATE TABLE IF NOT EXISTS consolidations (
@@ -2432,14 +2542,16 @@ async function ensureSchema() {
2432
2542
  sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
2433
2543
  args: []
2434
2544
  });
2435
- } catch {
2545
+ } catch (e) {
2546
+ logCatchDebug("migration", e);
2436
2547
  }
2437
2548
  try {
2438
2549
  await client.execute({
2439
2550
  sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
2440
2551
  args: []
2441
2552
  });
2442
- } catch {
2553
+ } catch (e) {
2554
+ logCatchDebug("migration", e);
2443
2555
  }
2444
2556
  await client.executeMultiple(`
2445
2557
  CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
@@ -2465,7 +2577,8 @@ async function ensureSchema() {
2465
2577
  sql: `UPDATE tasks SET project_name = 'exe-os' WHERE project_name = 'worker'`,
2466
2578
  args: []
2467
2579
  });
2468
- } catch {
2580
+ } catch (e) {
2581
+ logCatchDebug("migration", e);
2469
2582
  }
2470
2583
  await client.executeMultiple(`
2471
2584
  CREATE TABLE IF NOT EXISTS trajectories (
@@ -2489,7 +2602,8 @@ async function ensureSchema() {
2489
2602
  `);
2490
2603
  try {
2491
2604
  await client.execute("ALTER TABLE trajectories ADD COLUMN skill_id TEXT");
2492
- } catch {
2605
+ } catch (e) {
2606
+ logCatchDebug("migration", e);
2493
2607
  }
2494
2608
  await client.executeMultiple(`
2495
2609
  CREATE TABLE IF NOT EXISTS consolidations (
@@ -2526,63 +2640,72 @@ async function ensureSchema() {
2526
2640
  sql: `ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0`,
2527
2641
  args: []
2528
2642
  });
2529
- } catch {
2643
+ } catch (e) {
2644
+ logCatchDebug("migration", e);
2530
2645
  }
2531
2646
  try {
2532
2647
  await client.execute({
2533
2648
  sql: `ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5`,
2534
2649
  args: []
2535
2650
  });
2536
- } catch {
2651
+ } catch (e) {
2652
+ logCatchDebug("migration", e);
2537
2653
  }
2538
2654
  try {
2539
2655
  await client.execute({
2540
2656
  sql: `ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'`,
2541
2657
  args: []
2542
2658
  });
2543
- } catch {
2659
+ } catch (e) {
2660
+ logCatchDebug("migration", e);
2544
2661
  }
2545
2662
  try {
2546
2663
  await client.execute({
2547
2664
  sql: `ALTER TABLE memories ADD COLUMN deleted_at TEXT`,
2548
2665
  args: []
2549
2666
  });
2550
- } catch {
2667
+ } catch (e) {
2668
+ logCatchDebug("migration", e);
2551
2669
  }
2552
2670
  try {
2553
2671
  await client.execute({
2554
2672
  sql: `ALTER TABLE memories ADD COLUMN confidence REAL DEFAULT 0.7`,
2555
2673
  args: []
2556
2674
  });
2557
- } catch {
2675
+ } catch (e) {
2676
+ logCatchDebug("migration", e);
2558
2677
  }
2559
2678
  try {
2560
2679
  await client.execute({
2561
2680
  sql: `ALTER TABLE memories ADD COLUMN last_accessed TEXT`,
2562
2681
  args: []
2563
2682
  });
2564
- } catch {
2683
+ } catch (e) {
2684
+ logCatchDebug("migration", e);
2565
2685
  }
2566
2686
  try {
2567
2687
  await client.execute({
2568
2688
  sql: `UPDATE memories SET last_accessed = timestamp WHERE last_accessed IS NULL`,
2569
2689
  args: []
2570
2690
  });
2571
- } catch {
2691
+ } catch (e) {
2692
+ logCatchDebug("migration", e);
2572
2693
  }
2573
2694
  try {
2574
2695
  await client.execute({
2575
2696
  sql: `ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0`,
2576
2697
  args: []
2577
2698
  });
2578
- } catch {
2699
+ } catch (e) {
2700
+ logCatchDebug("migration", e);
2579
2701
  }
2580
2702
  try {
2581
2703
  await client.execute({
2582
2704
  sql: `ALTER TABLE memories ADD COLUMN graph_extracted INTEGER DEFAULT 0`,
2583
2705
  args: []
2584
2706
  });
2585
- } catch {
2707
+ } catch (e) {
2708
+ logCatchDebug("migration", e);
2586
2709
  }
2587
2710
  for (const col of [
2588
2711
  "ALTER TABLE memories ADD COLUMN content_hash TEXT",
@@ -2590,14 +2713,16 @@ async function ensureSchema() {
2590
2713
  ]) {
2591
2714
  try {
2592
2715
  await client.execute(col);
2593
- } catch {
2716
+ } catch (e) {
2717
+ logCatchDebug("migration", e);
2594
2718
  }
2595
2719
  }
2596
2720
  try {
2597
2721
  await client.execute(
2598
2722
  `CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
2599
2723
  );
2600
- } catch {
2724
+ } catch (e) {
2725
+ logCatchDebug("migration", e);
2601
2726
  }
2602
2727
  try {
2603
2728
  await client.execute(
@@ -2605,7 +2730,8 @@ async function ensureSchema() {
2605
2730
  ON memories(content_hash, agent_id, project_name, memory_type)
2606
2731
  WHERE content_hash IS NOT NULL`
2607
2732
  );
2608
- } catch {
2733
+ } catch (e) {
2734
+ logCatchDebug("migration", e);
2609
2735
  }
2610
2736
  await client.executeMultiple(`
2611
2737
  CREATE TABLE IF NOT EXISTS entities (
@@ -2681,7 +2807,8 @@ async function ensureSchema() {
2681
2807
  `);
2682
2808
  try {
2683
2809
  await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
2684
- } catch {
2810
+ } catch (e) {
2811
+ logCatchDebug("migration", e);
2685
2812
  }
2686
2813
  await client.executeMultiple(`
2687
2814
  CREATE TABLE IF NOT EXISTS entity_aliases (
@@ -2696,14 +2823,16 @@ async function ensureSchema() {
2696
2823
  ]) {
2697
2824
  try {
2698
2825
  await client.execute(col);
2699
- } catch {
2826
+ } catch (e) {
2827
+ logCatchDebug("migration", e);
2700
2828
  }
2701
2829
  }
2702
2830
  try {
2703
2831
  await client.execute(
2704
2832
  `CREATE INDEX IF NOT EXISTS idx_memories_status ON memories(status)`
2705
2833
  );
2706
- } catch {
2834
+ } catch (e) {
2835
+ logCatchDebug("migration", e);
2707
2836
  }
2708
2837
  await client.executeMultiple(`
2709
2838
  CREATE TABLE IF NOT EXISTS identity (
@@ -2802,7 +2931,8 @@ async function ensureSchema() {
2802
2931
  sql: `ALTER TABLE memories ADD COLUMN ${column}`,
2803
2932
  args: []
2804
2933
  });
2805
- } catch {
2934
+ } catch (e) {
2935
+ logCatchDebug("migration", e);
2806
2936
  }
2807
2937
  }
2808
2938
  for (const col of [
@@ -2811,7 +2941,8 @@ async function ensureSchema() {
2811
2941
  ]) {
2812
2942
  try {
2813
2943
  await client.execute(col);
2814
- } catch {
2944
+ } catch (e) {
2945
+ logCatchDebug("migration", e);
2815
2946
  }
2816
2947
  }
2817
2948
  await client.executeMultiple(`
@@ -2996,56 +3127,64 @@ async function ensureSchema() {
2996
3127
  args: []
2997
3128
  });
2998
3129
  }
2999
- } catch {
3130
+ } catch (e) {
3131
+ logCatchDebug("session_agent_map backfill", e);
3000
3132
  }
3001
3133
  try {
3002
3134
  await client.execute({
3003
3135
  sql: `ALTER TABLE session_agent_map ADD COLUMN cache_cold_count INTEGER NOT NULL DEFAULT 0`,
3004
3136
  args: []
3005
3137
  });
3006
- } catch {
3138
+ } catch (e) {
3139
+ logCatchDebug("migration", e);
3007
3140
  }
3008
3141
  try {
3009
3142
  await client.execute({
3010
3143
  sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
3011
3144
  args: []
3012
3145
  });
3013
- } catch {
3146
+ } catch (e) {
3147
+ logCatchDebug("migration", e);
3014
3148
  }
3015
3149
  try {
3016
3150
  await client.execute({
3017
3151
  sql: `ALTER TABLE tasks ADD COLUMN budget_fallback_model TEXT`,
3018
3152
  args: []
3019
3153
  });
3020
- } catch {
3154
+ } catch (e) {
3155
+ logCatchDebug("migration", e);
3021
3156
  }
3022
3157
  try {
3023
3158
  await client.execute({
3024
3159
  sql: `ALTER TABLE tasks ADD COLUMN tokens_used INTEGER DEFAULT 0`,
3025
3160
  args: []
3026
3161
  });
3027
- } catch {
3162
+ } catch (e) {
3163
+ logCatchDebug("migration", e);
3028
3164
  }
3029
3165
  try {
3030
3166
  await client.execute({
3031
3167
  sql: `ALTER TABLE tasks ADD COLUMN tokens_warned_at INTEGER`,
3032
3168
  args: []
3033
3169
  });
3034
- } catch {
3170
+ } catch (e) {
3171
+ logCatchDebug("migration", e);
3035
3172
  }
3036
3173
  try {
3037
3174
  await client.execute({
3038
3175
  sql: `ALTER TABLE tasks ADD COLUMN spawn_runtime TEXT`,
3039
3176
  args: []
3040
3177
  });
3041
- } catch {
3178
+ } catch (e) {
3179
+ logCatchDebug("migration", e);
3042
3180
  }
3043
3181
  try {
3044
3182
  await client.execute({
3045
3183
  sql: `ALTER TABLE tasks ADD COLUMN spawn_model TEXT`,
3046
3184
  args: []
3047
3185
  });
3048
- } catch {
3186
+ } catch (e) {
3187
+ logCatchDebug("migration", e);
3049
3188
  }
3050
3189
  await client.executeMultiple(`
3051
3190
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
@@ -3244,13 +3383,15 @@ async function ensureSchema() {
3244
3383
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
3245
3384
  args: []
3246
3385
  });
3247
- } catch {
3386
+ } catch (e) {
3387
+ logCatchDebug("migration", e);
3248
3388
  }
3249
3389
  try {
3250
3390
  await client.execute(
3251
3391
  `CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)`
3252
3392
  );
3253
- } catch {
3393
+ } catch (e) {
3394
+ logCatchDebug("migration", e);
3254
3395
  }
3255
3396
  try {
3256
3397
  await client.execute({
@@ -3261,20 +3402,23 @@ async function ensureSchema() {
3261
3402
  sql: `UPDATE memories SET tier = 2 WHERE tool_name IN ('store_memory', 'manual') AND importance >= 5 AND tier = 3`,
3262
3403
  args: []
3263
3404
  });
3264
- } catch {
3405
+ } catch (e) {
3406
+ logCatchDebug("migration", e);
3265
3407
  }
3266
3408
  try {
3267
3409
  await client.execute({
3268
3410
  sql: `ALTER TABLE memories ADD COLUMN supersedes_id TEXT`,
3269
3411
  args: []
3270
3412
  });
3271
- } catch {
3413
+ } catch (e) {
3414
+ logCatchDebug("migration", e);
3272
3415
  }
3273
3416
  try {
3274
3417
  await client.execute(
3275
3418
  `CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL`
3276
3419
  );
3277
- } catch {
3420
+ } catch (e) {
3421
+ logCatchDebug("migration", e);
3278
3422
  }
3279
3423
  for (const col of [
3280
3424
  "ALTER TABLE tasks ADD COLUMN checkpoint TEXT",
@@ -3282,7 +3426,8 @@ async function ensureSchema() {
3282
3426
  ]) {
3283
3427
  try {
3284
3428
  await client.execute(col);
3285
- } catch {
3429
+ } catch (e) {
3430
+ logCatchDebug("migration", e);
3286
3431
  }
3287
3432
  }
3288
3433
  try {
@@ -3290,13 +3435,15 @@ async function ensureSchema() {
3290
3435
  sql: `ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0`,
3291
3436
  args: []
3292
3437
  });
3293
- } catch {
3438
+ } catch (e) {
3439
+ logCatchDebug("migration", e);
3294
3440
  }
3295
3441
  try {
3296
3442
  await client.execute(
3297
3443
  `CREATE INDEX IF NOT EXISTS idx_memories_draft ON memories(draft) WHERE draft = 1`
3298
3444
  );
3299
- } catch {
3445
+ } catch (e) {
3446
+ logCatchDebug("migration", e);
3300
3447
  }
3301
3448
  for (const col of [
3302
3449
  "ALTER TABLE memories ADD COLUMN valid_from TEXT",
@@ -3304,7 +3451,8 @@ async function ensureSchema() {
3304
3451
  ]) {
3305
3452
  try {
3306
3453
  await client.execute(col);
3307
- } catch {
3454
+ } catch (e) {
3455
+ logCatchDebug("migration", e);
3308
3456
  }
3309
3457
  }
3310
3458
  try {
@@ -3312,27 +3460,31 @@ async function ensureSchema() {
3312
3460
  sql: `UPDATE memories SET valid_from = timestamp WHERE valid_from IS NULL`,
3313
3461
  args: []
3314
3462
  });
3315
- } catch {
3463
+ } catch (e) {
3464
+ logCatchDebug("migration", e);
3316
3465
  }
3317
3466
  try {
3318
3467
  await client.execute({
3319
3468
  sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
3320
3469
  args: []
3321
3470
  });
3322
- } catch {
3471
+ } catch (e) {
3472
+ logCatchDebug("migration", e);
3323
3473
  }
3324
3474
  try {
3325
3475
  await client.execute(
3326
3476
  `CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(memory_type)`
3327
3477
  );
3328
- } catch {
3478
+ } catch (e) {
3479
+ logCatchDebug("migration", e);
3329
3480
  }
3330
3481
  try {
3331
3482
  await client.execute({
3332
3483
  sql: `ALTER TABLE memories ADD COLUMN trajectory TEXT`,
3333
3484
  args: []
3334
3485
  });
3335
- } catch {
3486
+ } catch (e) {
3487
+ logCatchDebug("migration", e);
3336
3488
  }
3337
3489
  for (const col of [
3338
3490
  "ALTER TABLE memories ADD COLUMN intent TEXT",
@@ -3353,7 +3505,8 @@ async function ensureSchema() {
3353
3505
  ]) {
3354
3506
  try {
3355
3507
  await client.execute(col);
3356
- } catch {
3508
+ } catch (e) {
3509
+ logCatchDebug("migration", e);
3357
3510
  }
3358
3511
  }
3359
3512
  try {
@@ -3361,14 +3514,16 @@ async function ensureSchema() {
3361
3514
  sql: `ALTER TABLE memories ADD COLUMN procedure_for TEXT`,
3362
3515
  args: []
3363
3516
  });
3364
- } catch {
3517
+ } catch (e) {
3518
+ logCatchDebug("migration", e);
3365
3519
  }
3366
3520
  try {
3367
3521
  await client.execute({
3368
3522
  sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
3369
3523
  args: []
3370
3524
  });
3371
- } catch {
3525
+ } catch (e) {
3526
+ logCatchDebug("migration", e);
3372
3527
  }
3373
3528
  }
3374
3529
  async function disposeDatabase() {
@@ -3379,7 +3534,8 @@ async function disposeDatabase() {
3379
3534
  if (_client) {
3380
3535
  try {
3381
3536
  await _client.execute("PRAGMA wal_checkpoint(PASSIVE)");
3382
- } catch {
3537
+ } catch (e) {
3538
+ logCatchDebug("WAL checkpoint", e);
3383
3539
  }
3384
3540
  }
3385
3541
  if (_daemonClient) {
@@ -3395,8 +3551,9 @@ async function disposeDatabase() {
3395
3551
  _client = null;
3396
3552
  _resilientClient = null;
3397
3553
  }
3554
+ releaseDbLock();
3398
3555
  }
3399
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, SOFT_DELETE_RETENTION_DAYS, disposeTurso;
3556
+ var _debugDb, _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, _lockFd, DB_LOCK_PATH, initTurso, SOFT_DELETE_RETENTION_DAYS, disposeTurso;
3400
3557
  var init_database = __esm({
3401
3558
  "src/lib/database.ts"() {
3402
3559
  "use strict";
@@ -3404,11 +3561,14 @@ var init_database = __esm({
3404
3561
  init_employees();
3405
3562
  init_database_adapter();
3406
3563
  init_memory();
3564
+ _debugDb = process.env.EXE_DEBUG === "1";
3407
3565
  _client = null;
3408
3566
  _resilientClient = null;
3409
3567
  _walCheckpointTimer = null;
3410
3568
  _daemonClient = null;
3411
3569
  _adapterClient = null;
3570
+ _lockFd = null;
3571
+ DB_LOCK_PATH = join(homedir(), ".exe-os", "db.lock");
3412
3572
  initTurso = initDatabase;
3413
3573
  SOFT_DELETE_RETENTION_DAYS = 7;
3414
3574
  disposeTurso = disposeDatabase;
@@ -3416,7 +3576,7 @@ var init_database = __esm({
3416
3576
  });
3417
3577
 
3418
3578
  // src/lib/license.ts
3419
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
3579
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
3420
3580
  import { randomUUID as randomUUID2 } from "crypto";
3421
3581
  import { createRequire as createRequire2 } from "module";
3422
3582
  import { pathToFileURL as pathToFileURL2 } from "url";
@@ -3436,7 +3596,7 @@ var init_license = __esm({
3436
3596
  });
3437
3597
 
3438
3598
  // src/lib/plan-limits.ts
3439
- import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
3599
+ import { readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
3440
3600
  import path11 from "path";
3441
3601
  var CACHE_PATH2;
3442
3602
  var init_plan_limits = __esm({
@@ -3454,9 +3614,9 @@ var init_plan_limits = __esm({
3454
3614
  import os8 from "os";
3455
3615
  import path12 from "path";
3456
3616
  import {
3457
- existsSync as existsSync10,
3617
+ existsSync as existsSync11,
3458
3618
  lstatSync,
3459
- mkdirSync as mkdirSync5,
3619
+ mkdirSync as mkdirSync6,
3460
3620
  readlinkSync as readlinkSync2,
3461
3621
  symlinkSync as symlinkSync2
3462
3622
  } from "fs";
@@ -3468,7 +3628,7 @@ var init_agent_symlinks = __esm({
3468
3628
  });
3469
3629
 
3470
3630
  // src/lib/tmux-routing.ts
3471
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync11, appendFileSync, readdirSync as readdirSync2 } from "fs";
3631
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync2 } from "fs";
3472
3632
  import path13 from "path";
3473
3633
  import os9 from "os";
3474
3634
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -3493,11 +3653,13 @@ function getParentExe(sessionKey) {
3493
3653
  }
3494
3654
  }
3495
3655
  function resolveExeSession() {
3496
- if (process.env.EXE_SESSION_NAME) {
3497
- return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
3498
- }
3499
3656
  const mySession = getMySession();
3500
- if (!mySession) return null;
3657
+ if (!mySession) {
3658
+ if (process.env.EXE_SESSION_NAME) {
3659
+ return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
3660
+ }
3661
+ return null;
3662
+ }
3501
3663
  const fromSessionName = extractRootExe(mySession);
3502
3664
  let candidate = null;
3503
3665
  try {
@@ -3609,13 +3771,13 @@ __export(identity_exports, {
3609
3771
  listIdentities: () => listIdentities,
3610
3772
  updateIdentity: () => updateIdentity
3611
3773
  });
3612
- import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
3774
+ import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
3613
3775
  import { readdirSync as readdirSync3 } from "fs";
3614
3776
  import path14 from "path";
3615
3777
  import { createHash } from "crypto";
3616
3778
  function ensureDir() {
3617
- if (!existsSync12(IDENTITY_DIR2)) {
3618
- mkdirSync7(IDENTITY_DIR2, { recursive: true });
3779
+ if (!existsSync13(IDENTITY_DIR2)) {
3780
+ mkdirSync8(IDENTITY_DIR2, { recursive: true });
3619
3781
  }
3620
3782
  }
3621
3783
  function identityPath(agentId) {
@@ -3663,7 +3825,7 @@ function contentHash(content) {
3663
3825
  }
3664
3826
  function getIdentity(agentId) {
3665
3827
  const filePath = identityPath(agentId);
3666
- if (!existsSync12(filePath)) return null;
3828
+ if (!existsSync13(filePath)) return null;
3667
3829
  const raw = readFileSync11(filePath, "utf-8");
3668
3830
  const { frontmatter, body } = parseFrontmatter(raw);
3669
3831
  return {
@@ -3742,7 +3904,7 @@ var init_identity = __esm({
3742
3904
 
3743
3905
  // src/lib/keychain.ts
3744
3906
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3745
- import { existsSync as existsSync13, statSync as statSync2 } from "fs";
3907
+ import { existsSync as existsSync14, statSync as statSync3 } from "fs";
3746
3908
  import { execSync as execSync6 } from "child_process";
3747
3909
  import path15 from "path";
3748
3910
  import os10 from "os";
@@ -3777,7 +3939,7 @@ function isRootOnlyTrustedServerKeyFile(keyPath) {
3777
3939
  if (process.platform !== "linux") return false;
3778
3940
  try {
3779
3941
  const uid = typeof os10.userInfo().uid === "number" ? os10.userInfo().uid : -1;
3780
- const st = statSync2(keyPath);
3942
+ const st = statSync3(keyPath);
3781
3943
  if (!st.isFile() || (st.mode & 63) !== 0) return false;
3782
3944
  if (uid === 0) return true;
3783
3945
  const exeOsDir = process.env.EXE_OS_DIR;
@@ -3978,7 +4140,7 @@ async function getMasterKey() {
3978
4140
  }
3979
4141
  }
3980
4142
  const keyPath = getKeyPath();
3981
- if (!existsSync13(keyPath)) {
4143
+ if (!existsSync14(keyPath)) {
3982
4144
  process.stderr.write(
3983
4145
  `[keychain] Key not found at ${keyPath} (HOME=${os10.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
3984
4146
  `
@@ -4313,18 +4475,54 @@ __export(shard_manager_exports, {
4313
4475
  shardExists: () => shardExists
4314
4476
  });
4315
4477
  import path16 from "path";
4316
- import { existsSync as existsSync14, mkdirSync as mkdirSync8, readdirSync as readdirSync4, renameSync as renameSync4, statSync as statSync3 } from "fs";
4478
+ import { existsSync as existsSync15, mkdirSync as mkdirSync9, readdirSync as readdirSync4, renameSync as renameSync4, statSync as statSync4 } from "fs";
4317
4479
  import { createClient as createClient2 } from "@libsql/client";
4318
4480
  function initShardManager(encryptionKey) {
4319
4481
  _encryptionKey = encryptionKey;
4320
- if (!existsSync14(SHARDS_DIR)) {
4321
- mkdirSync8(SHARDS_DIR, { recursive: true });
4482
+ _keyValidated = false;
4483
+ _keyValidationPromise = null;
4484
+ if (!existsSync15(SHARDS_DIR)) {
4485
+ mkdirSync9(SHARDS_DIR, { recursive: true });
4486
+ }
4487
+ const existingShards = readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db"));
4488
+ if (existingShards.length === 0) {
4489
+ _keyValidated = true;
4322
4490
  }
4323
4491
  _shardingEnabled = true;
4324
4492
  if (_evictionTimer) clearInterval(_evictionTimer);
4325
4493
  _evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
4326
4494
  _evictionTimer.unref();
4327
4495
  }
4496
+ async function validateEncryptionKey() {
4497
+ if (_keyValidated) return true;
4498
+ if (!_encryptionKey) return false;
4499
+ const existingShards = readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db"));
4500
+ if (existingShards.length === 0) {
4501
+ _keyValidated = true;
4502
+ return true;
4503
+ }
4504
+ for (const shardFile of existingShards.slice(0, 3)) {
4505
+ const dbPath = path16.join(SHARDS_DIR, shardFile);
4506
+ const testClient = createClient2({ url: `file:${dbPath}`, encryptionKey: _encryptionKey });
4507
+ try {
4508
+ await testClient.execute("SELECT COUNT(*) FROM sqlite_schema");
4509
+ testClient.close();
4510
+ _keyValidated = true;
4511
+ return true;
4512
+ } catch {
4513
+ try {
4514
+ testClient.close();
4515
+ } catch {
4516
+ }
4517
+ }
4518
+ }
4519
+ process.stderr.write(
4520
+ `[shard-manager] WARNING: encryption key cannot read any existing shards (${existingShards.length} found). New shard creation disabled to prevent stranded files. Run /exe-doctor to audit.
4521
+ `
4522
+ );
4523
+ _shardingEnabled = false;
4524
+ return false;
4525
+ }
4328
4526
  function isShardingEnabled() {
4329
4527
  return _shardingEnabled;
4330
4528
  }
@@ -4358,13 +4556,13 @@ function getShardClient(projectName) {
4358
4556
  }
4359
4557
  function shardExists(projectName) {
4360
4558
  const safeName = safeShardName(projectName);
4361
- return existsSync14(path16.join(SHARDS_DIR, `${safeName}.db`));
4559
+ return existsSync15(path16.join(SHARDS_DIR, `${safeName}.db`));
4362
4560
  }
4363
4561
  function safeShardName(projectName) {
4364
4562
  return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
4365
4563
  }
4366
4564
  function listShards() {
4367
- if (!existsSync14(SHARDS_DIR)) return [];
4565
+ if (!existsSync15(SHARDS_DIR)) return [];
4368
4566
  return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
4369
4567
  }
4370
4568
  async function auditShardHealth(options = {}) {
@@ -4377,7 +4575,7 @@ async function auditShardHealth(options = {}) {
4377
4575
  const shards = [];
4378
4576
  for (const name of names) {
4379
4577
  const dbPath = path16.join(SHARDS_DIR, `${name}.db`);
4380
- const stat = statSync3(dbPath);
4578
+ const stat = statSync4(dbPath);
4381
4579
  const item = {
4382
4580
  name,
4383
4581
  path: dbPath,
@@ -4617,6 +4815,17 @@ async function ensureShardSchema(client) {
4617
4815
  }
4618
4816
  }
4619
4817
  async function getReadyShardClient(projectName) {
4818
+ if (!_keyValidated) {
4819
+ if (!_keyValidationPromise) {
4820
+ _keyValidationPromise = validateEncryptionKey();
4821
+ }
4822
+ const valid = await _keyValidationPromise;
4823
+ if (!valid) {
4824
+ throw new Error(
4825
+ `Shard creation blocked: encryption key mismatch with existing shards. Run /exe-doctor to audit.`
4826
+ );
4827
+ }
4828
+ }
4620
4829
  const safeName = safeShardName(projectName);
4621
4830
  let client = getShardClient(projectName);
4622
4831
  try {
@@ -4629,8 +4838,8 @@ async function getReadyShardClient(projectName) {
4629
4838
  _shards.delete(safeName);
4630
4839
  _shardLastAccess.delete(safeName);
4631
4840
  const dbPath = path16.join(SHARDS_DIR, `${safeName}.db`);
4632
- if (existsSync14(dbPath)) {
4633
- const stat = statSync3(dbPath);
4841
+ if (existsSync15(dbPath)) {
4842
+ const stat = statSync4(dbPath);
4634
4843
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4635
4844
  const archivedPath = path16.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
4636
4845
  renameSync4(dbPath, archivedPath);
@@ -4695,7 +4904,7 @@ function disposeShards() {
4695
4904
  _shardingEnabled = false;
4696
4905
  _encryptionKey = null;
4697
4906
  }
4698
- var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
4907
+ var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled, _keyValidated, _keyValidationPromise;
4699
4908
  var init_shard_manager = __esm({
4700
4909
  "src/lib/shard-manager.ts"() {
4701
4910
  "use strict";
@@ -4709,6 +4918,8 @@ var init_shard_manager = __esm({
4709
4918
  _evictionTimer = null;
4710
4919
  _encryptionKey = null;
4711
4920
  _shardingEnabled = false;
4921
+ _keyValidated = false;
4922
+ _keyValidationPromise = null;
4712
4923
  }
4713
4924
  });
4714
4925
 
@@ -4904,6 +5115,18 @@ var init_platform_procedures = __esm({
4904
5115
  priority: "p0",
4905
5116
  content: "create_task is dispatch + delivery. Task lifecycle: open \u2192 in_progress (you start) \u2192 done (update_task when finished) \u2192 needs_review (reviewer nudged) \u2192 closed (COO only via close_task). DB is the reliable delivery \u2014 intercom is just a speedup nudge. If you finish a task, self-chain: check for next task immediately (step 7). Never wait for a nudge. Never say 'standing by.'"
4906
5117
  },
5118
+ {
5119
+ title: "Review chain \u2014 managers must actively pull completed work, never wait for nudges",
5120
+ domain: "workflow",
5121
+ priority: "p0",
5122
+ content: "When you dispatch work, you OWN the review. Check list_tasks(status='needs_review') on EVERY prompt \u2014 don't wait for intercom nudges (they're unreliable). When a task shows needs_review: (1) read the deliverable (git diff in worktree, exe/output/ files, or task result summary), (2) verify it works (tsc, build, run), (3) close_task if good or create a fix task if not. Reviews sitting >30 minutes is a pipeline stall. The whole chain: worker calls update_task(done) \u2192 system flags needs_review \u2192 manager pulls and verifies \u2192 close_task \u2192 COO reviews manager's work \u2192 merge to main. Every level actively pulls \u2014 nobody waits."
5123
+ },
5124
+ {
5125
+ title: "Bug fix lifecycle \u2014 triage upstream after every verified fix so customers see the status",
5126
+ domain: "workflow",
5127
+ priority: "p0",
5128
+ content: "When a bug from support(action='list_bugs') is fixed and verified, the reviewer MUST triage it upstream: support(action='triage_bug', id='<bug-id>', notes='<what was fixed>', fixed_version='<version>', linked_commit='<hash>'). This closes the bug in the customer's view \u2014 their COO checks list_my_bugs and sees status change from open \u2192 closed with the fix version. Without triage, customers see 'open' forever even after the fix ships. Same for feature requests: support(action='triage_feature', ..., shipped_version='<version>'). Triage is part of the review gate \u2014 a fix is not done until the upstream report is closed."
5129
+ },
4907
5130
  {
4908
5131
  title: "Intercom is a speedup, not delivery \u2014 DB is the source of truth",
4909
5132
  domain: "architecture",
@@ -5501,6 +5724,20 @@ __export(store_exports, {
5501
5724
  vectorToBlob: () => vectorToBlob,
5502
5725
  writeMemory: () => writeMemory
5503
5726
  });
5727
+ function logStoreDebug(context, err) {
5728
+ if (_debugStore) {
5729
+ process.stderr.write(
5730
+ `[store] ${context}: ${err instanceof Error ? err.message : String(err)}
5731
+ `
5732
+ );
5733
+ }
5734
+ }
5735
+ function logStoreWarn(context, err) {
5736
+ process.stderr.write(
5737
+ `[store] WARN ${context}: ${err instanceof Error ? err.message : String(err)}
5738
+ `
5739
+ );
5740
+ }
5504
5741
  function isBusyError2(err) {
5505
5742
  if (err instanceof Error) {
5506
5743
  const msg = err.message.toLowerCase();
@@ -5555,13 +5792,15 @@ async function initStore(options) {
5555
5792
  try {
5556
5793
  const { initDaemonClient: initDaemonClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
5557
5794
  await initDaemonClient2();
5558
- } catch {
5795
+ } catch (e) {
5796
+ logStoreWarn("catch", e);
5559
5797
  }
5560
5798
  if (!options?.lightweight) {
5561
5799
  try {
5562
5800
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
5563
5801
  initShardManager2(hexKey);
5564
- } catch {
5802
+ } catch (e) {
5803
+ logStoreWarn("catch", e);
5565
5804
  }
5566
5805
  const client = getClient();
5567
5806
  const vResult = await retryOnBusy2(
@@ -5572,7 +5811,8 @@ async function initStore(options) {
5572
5811
  try {
5573
5812
  const { loadGlobalProcedures: loadGlobalProcedures2 } = await Promise.resolve().then(() => (init_global_procedures(), global_procedures_exports));
5574
5813
  await loadGlobalProcedures2();
5575
- } catch {
5814
+ } catch (e) {
5815
+ logStoreWarn("catch", e);
5576
5816
  }
5577
5817
  }
5578
5818
  }
@@ -5632,7 +5872,8 @@ async function writeMemory(record) {
5632
5872
  memoryType
5633
5873
  });
5634
5874
  if (existing) return;
5635
- } catch {
5875
+ } catch (e) {
5876
+ logStoreWarn("catch", e);
5636
5877
  }
5637
5878
  const dbRow = {
5638
5879
  id: record.id,
@@ -5833,12 +6074,14 @@ async function flushBatch() {
5833
6074
  try {
5834
6075
  const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
5835
6076
  await insertMemoryCardsForBatch2(batch);
5836
- } catch {
6077
+ } catch (e) {
6078
+ logStoreWarn("catch", e);
5837
6079
  }
5838
6080
  try {
5839
6081
  const { insertOntologyForBatch: insertOntologyForBatch2 } = await Promise.resolve().then(() => (init_agentic_ontology(), agentic_ontology_exports));
5840
6082
  await insertOntologyForBatch2(batch);
5841
- } catch {
6083
+ } catch (e) {
6084
+ logStoreWarn("catch", e);
5842
6085
  }
5843
6086
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
5844
6087
  _pendingRecords.splice(0, batch.length);
@@ -5877,7 +6120,8 @@ ${err.stack.split("\n").slice(1, 3).join("\n")}` : ""}` : String(err);
5877
6120
  }
5878
6121
  }
5879
6122
  }
5880
- } catch {
6123
+ } catch (e) {
6124
+ logStoreWarn("catch", e);
5881
6125
  }
5882
6126
  return batch.length;
5883
6127
  } finally {
@@ -5919,7 +6163,8 @@ async function searchMemories(queryVector, agentId, options) {
5919
6163
  } else {
5920
6164
  client = getClient();
5921
6165
  }
5922
- } catch {
6166
+ } catch (e) {
6167
+ logStoreDebug("shard routing fallback", e);
5923
6168
  client = getClient();
5924
6169
  }
5925
6170
  const limit = options?.limit ?? 10;
@@ -6035,7 +6280,8 @@ async function attachDocumentMetadata(records) {
6035
6280
  if (!record.document_id) continue;
6036
6281
  record.document_metadata = byId.get(record.document_id) ?? null;
6037
6282
  }
6038
- } catch {
6283
+ } catch (e) {
6284
+ logStoreWarn("catch", e);
6039
6285
  }
6040
6286
  return records;
6041
6287
  }
@@ -6096,11 +6342,12 @@ async function getMemoryCardinality(agentId) {
6096
6342
  args: [agentId]
6097
6343
  });
6098
6344
  return Number(result.rows[0]?.cnt) || 0;
6099
- } catch {
6345
+ } catch (e) {
6346
+ logStoreWarn("getMemoryCardinality", e);
6100
6347
  return 0;
6101
6348
  }
6102
6349
  }
6103
- var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
6350
+ var _debugStore, INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
6104
6351
  var init_store = __esm({
6105
6352
  "src/lib/store.ts"() {
6106
6353
  "use strict";
@@ -6110,6 +6357,7 @@ var init_store = __esm({
6110
6357
  init_config();
6111
6358
  init_state_bus();
6112
6359
  init_memory_write_governor();
6360
+ _debugStore = process.env.EXE_DEBUG === "1";
6113
6361
  INIT_MAX_RETRIES = 3;
6114
6362
  INIT_RETRY_DELAY_MS = 1e3;
6115
6363
  _pendingRecords = [];