@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
@@ -1671,9 +1671,79 @@ __export(database_exports, {
1671
1671
  isInitialized: () => isInitialized,
1672
1672
  setExternalClient: () => setExternalClient
1673
1673
  });
1674
- import { chmodSync as chmodSync2 } from "fs";
1674
+ import { chmodSync as chmodSync2, existsSync as existsSync6, statSync as statSync2, copyFileSync, unlinkSync as unlinkSync3, openSync as openSync2, closeSync as closeSync2, mkdirSync as mkdirSync2 } from "fs";
1675
1675
  import { createClient } from "@libsql/client";
1676
+ import { homedir } from "os";
1677
+ import { join } from "path";
1678
+ function logCatchDebug(context, err) {
1679
+ if (_debugDb) {
1680
+ process.stderr.write(
1681
+ `[database] ${context}: ${err instanceof Error ? err.message : String(err)}
1682
+ `
1683
+ );
1684
+ }
1685
+ }
1686
+ function acquireDbLock() {
1687
+ mkdirSync2(join(homedir(), ".exe-os"), { recursive: true });
1688
+ try {
1689
+ _lockFd = openSync2(DB_LOCK_PATH, "wx");
1690
+ } catch (err) {
1691
+ if (err && typeof err === "object" && "code" in err && err.code === "EEXIST") {
1692
+ try {
1693
+ const lockStat = statSync2(DB_LOCK_PATH);
1694
+ if (Date.now() - lockStat.mtimeMs > 6e4) {
1695
+ unlinkSync3(DB_LOCK_PATH);
1696
+ _lockFd = openSync2(DB_LOCK_PATH, "wx");
1697
+ return;
1698
+ }
1699
+ } catch (e) {
1700
+ logCatchDebug("stale lock check", e);
1701
+ }
1702
+ process.stderr.write(
1703
+ "[database] WARN: Another process holds db.lock \u2014 waiting briefly then proceeding.\n"
1704
+ );
1705
+ return;
1706
+ }
1707
+ throw err;
1708
+ }
1709
+ }
1710
+ function releaseDbLock() {
1711
+ if (_lockFd !== null) {
1712
+ try {
1713
+ closeSync2(_lockFd);
1714
+ } catch (e) {
1715
+ logCatchDebug("lock close", e);
1716
+ }
1717
+ _lockFd = null;
1718
+ }
1719
+ try {
1720
+ unlinkSync3(DB_LOCK_PATH);
1721
+ } catch (e) {
1722
+ logCatchDebug("lock unlink", e);
1723
+ }
1724
+ }
1676
1725
  async function initDatabase(config) {
1726
+ acquireDbLock();
1727
+ if (existsSync6(config.dbPath)) {
1728
+ const dbStat = statSync2(config.dbPath);
1729
+ if (dbStat.size === 0) {
1730
+ const walPath = config.dbPath + "-wal";
1731
+ if (existsSync6(walPath) && statSync2(walPath).size > 0) {
1732
+ const backupPath = config.dbPath + ".zeroed-" + Date.now();
1733
+ copyFileSync(config.dbPath, backupPath);
1734
+ unlinkSync3(config.dbPath);
1735
+ process.stderr.write(
1736
+ `[database] CRITICAL: DB was 0 bytes. Moved to ${backupPath}, attempting WAL recovery.
1737
+ `
1738
+ );
1739
+ } else {
1740
+ process.stderr.write(
1741
+ `[database] CRITICAL: DB is 0 bytes and no WAL available for recovery. Data may be lost. Check backups at ${config.dbPath}.bak
1742
+ `
1743
+ );
1744
+ }
1745
+ }
1746
+ }
1677
1747
  if (_walCheckpointTimer) {
1678
1748
  clearInterval(_walCheckpointTimer);
1679
1749
  _walCheckpointTimer = null;
@@ -1700,10 +1770,8 @@ async function initDatabase(config) {
1700
1770
  _client = createClient(opts);
1701
1771
  _resilientClient = wrapWithRetry(_client);
1702
1772
  _adapterClient = _resilientClient;
1703
- _client.execute("PRAGMA busy_timeout = 30000").catch(() => {
1704
- });
1705
- _client.execute("PRAGMA journal_mode = WAL").catch(() => {
1706
- });
1773
+ await _client.execute("PRAGMA busy_timeout = 30000");
1774
+ await _client.execute("PRAGMA journal_mode = WAL");
1707
1775
  if (_walCheckpointTimer) clearInterval(_walCheckpointTimer);
1708
1776
  _walCheckpointTimer = setInterval(() => {
1709
1777
  _client?.execute("PRAGMA wal_checkpoint(PASSIVE)").catch(() => {
@@ -1718,11 +1786,16 @@ async function initDatabase(config) {
1718
1786
  for (const suffix of ["-wal", "-shm"]) {
1719
1787
  try {
1720
1788
  chmodSync2(config.dbPath + suffix, 384);
1721
- } catch {
1789
+ } catch (chmodErr) {
1790
+ process.stderr.write(`[database] chmod ${suffix} failed: ${chmodErr instanceof Error ? chmodErr.message : String(chmodErr)}
1791
+ `);
1722
1792
  }
1723
1793
  }
1724
- } catch {
1794
+ } catch (chmodErr) {
1795
+ process.stderr.write(`[database] chmod db failed: ${chmodErr instanceof Error ? chmodErr.message : String(chmodErr)}
1796
+ `);
1725
1797
  }
1798
+ releaseDbLock();
1726
1799
  }
1727
1800
  function isInitialized() {
1728
1801
  return _adapterClient !== null || _client !== null;
@@ -1777,7 +1850,8 @@ async function ensureSchema() {
1777
1850
  await client.execute("PRAGMA wal_autocheckpoint = 1000");
1778
1851
  try {
1779
1852
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
1780
- } catch {
1853
+ } catch (e) {
1854
+ logCatchDebug("migration", e);
1781
1855
  }
1782
1856
  await client.executeMultiple(`
1783
1857
  CREATE TABLE IF NOT EXISTS memories (
@@ -1842,6 +1916,23 @@ async function ensureSchema() {
1842
1916
  INSERT INTO memories_fts(memories_fts, rowid, raw_text) VALUES('delete', old.rowid, old.raw_text);
1843
1917
  END;
1844
1918
  `);
1919
+ try {
1920
+ await client.execute("SELECT COUNT(*) FROM memories_fts LIMIT 1");
1921
+ } catch (ftsErr) {
1922
+ process.stderr.write(
1923
+ `[database] WARN: memories_fts corrupted (${ftsErr instanceof Error ? ftsErr.message : String(ftsErr)}) \u2014 rebuilding FTS index.
1924
+ `
1925
+ );
1926
+ try {
1927
+ await client.execute("INSERT INTO memories_fts(memories_fts) VALUES('rebuild')");
1928
+ process.stderr.write("[database] FTS index rebuilt successfully.\n");
1929
+ } catch (rebuildErr) {
1930
+ process.stderr.write(
1931
+ `[database] ERROR: FTS rebuild failed: ${rebuildErr instanceof Error ? rebuildErr.message : String(rebuildErr)}
1932
+ `
1933
+ );
1934
+ }
1935
+ }
1845
1936
  await client.executeMultiple(`
1846
1937
  CREATE TABLE IF NOT EXISTS sync_meta (
1847
1938
  key TEXT PRIMARY KEY,
@@ -1900,35 +1991,40 @@ async function ensureSchema() {
1900
1991
  });
1901
1992
  }
1902
1993
  }
1903
- } catch {
1994
+ } catch (seedErr) {
1995
+ logCatchDebug("behavior seed", seedErr);
1904
1996
  }
1905
1997
  try {
1906
1998
  await client.execute({
1907
1999
  sql: `ALTER TABLE behaviors ADD COLUMN priority TEXT DEFAULT 'p1'`,
1908
2000
  args: []
1909
2001
  });
1910
- } catch {
2002
+ } catch (e) {
2003
+ logCatchDebug("migration", e);
1911
2004
  }
1912
2005
  try {
1913
2006
  await client.execute({
1914
2007
  sql: `ALTER TABLE behaviors ADD COLUMN vector F32_BLOB(${EMBEDDING_DIM})`,
1915
2008
  args: []
1916
2009
  });
1917
- } catch {
2010
+ } catch (e) {
2011
+ logCatchDebug("migration", e);
1918
2012
  }
1919
2013
  try {
1920
2014
  await client.execute({
1921
2015
  sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
1922
2016
  args: []
1923
2017
  });
1924
- } catch {
2018
+ } catch (e) {
2019
+ logCatchDebug("migration", e);
1925
2020
  }
1926
2021
  try {
1927
2022
  await client.execute({
1928
2023
  sql: `ALTER TABLE tasks ADD COLUMN parent_task_id TEXT`,
1929
2024
  args: []
1930
2025
  });
1931
- } catch {
2026
+ } catch (e) {
2027
+ logCatchDebug("migration", e);
1932
2028
  }
1933
2029
  try {
1934
2030
  await client.execute({
@@ -1937,98 +2033,112 @@ async function ensureSchema() {
1937
2033
  WHERE parent_task_id IS NOT NULL`,
1938
2034
  args: []
1939
2035
  });
1940
- } catch {
2036
+ } catch (e) {
2037
+ logCatchDebug("migration", e);
1941
2038
  }
1942
2039
  try {
1943
2040
  await client.execute({
1944
2041
  sql: `UPDATE tasks SET status = 'done' WHERE status = 'completed'`,
1945
2042
  args: []
1946
2043
  });
1947
- } catch {
2044
+ } catch (e) {
2045
+ logCatchDebug("migration", e);
1948
2046
  }
1949
2047
  try {
1950
2048
  await client.execute({
1951
2049
  sql: `ALTER TABLE tasks ADD COLUMN reviewer TEXT`,
1952
2050
  args: []
1953
2051
  });
1954
- } catch {
2052
+ } catch (e) {
2053
+ logCatchDebug("migration", e);
1955
2054
  }
1956
2055
  try {
1957
2056
  await client.execute({
1958
2057
  sql: `ALTER TABLE tasks ADD COLUMN context TEXT`,
1959
2058
  args: []
1960
2059
  });
1961
- } catch {
2060
+ } catch (e) {
2061
+ logCatchDebug("migration", e);
1962
2062
  }
1963
2063
  try {
1964
2064
  await client.execute({
1965
2065
  sql: `ALTER TABLE tasks ADD COLUMN result TEXT`,
1966
2066
  args: []
1967
2067
  });
1968
- } catch {
2068
+ } catch (e) {
2069
+ logCatchDebug("migration", e);
1969
2070
  }
1970
2071
  try {
1971
2072
  await client.execute({
1972
2073
  sql: `ALTER TABLE tasks ADD COLUMN assigned_tmux TEXT`,
1973
2074
  args: []
1974
2075
  });
1975
- } catch {
2076
+ } catch (e) {
2077
+ logCatchDebug("migration", e);
1976
2078
  }
1977
2079
  try {
1978
2080
  await client.execute({
1979
2081
  sql: `ALTER TABLE tasks ADD COLUMN checkpoint TEXT`,
1980
2082
  args: []
1981
2083
  });
1982
- } catch {
2084
+ } catch (e) {
2085
+ logCatchDebug("migration", e);
1983
2086
  }
1984
2087
  try {
1985
2088
  await client.execute({
1986
2089
  sql: `ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER NOT NULL DEFAULT 0`,
1987
2090
  args: []
1988
2091
  });
1989
- } catch {
2092
+ } catch (e) {
2093
+ logCatchDebug("migration", e);
1990
2094
  }
1991
2095
  try {
1992
2096
  await client.execute({
1993
2097
  sql: `ALTER TABLE tasks ADD COLUMN complexity TEXT NOT NULL DEFAULT 'standard'`,
1994
2098
  args: []
1995
2099
  });
1996
- } catch {
2100
+ } catch (e) {
2101
+ logCatchDebug("migration", e);
1997
2102
  }
1998
2103
  try {
1999
2104
  await client.execute({
2000
2105
  sql: `ALTER TABLE tasks ADD COLUMN session_scope TEXT`,
2001
2106
  args: []
2002
2107
  });
2003
- } catch {
2108
+ } catch (e) {
2109
+ logCatchDebug("migration", e);
2004
2110
  }
2005
2111
  try {
2006
2112
  await client.execute({
2007
2113
  sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
2008
2114
  args: []
2009
2115
  });
2010
- } catch {
2116
+ } catch (e) {
2117
+ logCatchDebug("migration", e);
2011
2118
  }
2012
2119
  try {
2013
2120
  await client.execute({
2014
2121
  sql: `ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0`,
2015
2122
  args: []
2016
2123
  });
2017
- } catch {
2124
+ } catch (e) {
2125
+ logCatchDebug("migration", e);
2018
2126
  }
2019
2127
  try {
2020
2128
  await client.execute({
2021
2129
  sql: `ALTER TABLE memories ADD COLUMN author_device_id TEXT`,
2022
2130
  args: []
2023
2131
  });
2024
- } catch {
2132
+ } catch (e) {
2133
+ logCatchDebug("migration", e);
2025
2134
  }
2026
2135
  try {
2027
2136
  await client.execute({
2028
2137
  sql: `ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'`,
2029
2138
  args: []
2030
2139
  });
2031
- } catch {
2140
+ } catch (e) {
2141
+ logCatchDebug("migration", e);
2032
2142
  }
2033
2143
  await client.executeMultiple(`
2034
2144
  CREATE TABLE IF NOT EXISTS consolidations (
@@ -2133,14 +2243,16 @@ async function ensureSchema() {
2133
2243
  sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
2134
2244
  args: []
2135
2245
  });
2136
- } catch {
2246
+ } catch (e) {
2247
+ logCatchDebug("migration", e);
2137
2248
  }
2138
2249
  try {
2139
2250
  await client.execute({
2140
2251
  sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
2141
2252
  args: []
2142
2253
  });
2143
- } catch {
2254
+ } catch (e) {
2255
+ logCatchDebug("migration", e);
2144
2256
  }
2145
2257
  await client.executeMultiple(`
2146
2258
  CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
@@ -2166,7 +2278,8 @@ async function ensureSchema() {
2166
2278
  sql: `UPDATE tasks SET project_name = 'exe-os' WHERE project_name = 'worker'`,
2167
2279
  args: []
2168
2280
  });
2169
- } catch {
2281
+ } catch (e) {
2282
+ logCatchDebug("migration", e);
2170
2283
  }
2171
2284
  await client.executeMultiple(`
2172
2285
  CREATE TABLE IF NOT EXISTS trajectories (
@@ -2190,7 +2303,8 @@ async function ensureSchema() {
2190
2303
  `);
2191
2304
  try {
2192
2305
  await client.execute("ALTER TABLE trajectories ADD COLUMN skill_id TEXT");
2193
- } catch {
2306
+ } catch (e) {
2307
+ logCatchDebug("migration", e);
2194
2308
  }
2195
2309
  await client.executeMultiple(`
2196
2310
  CREATE TABLE IF NOT EXISTS consolidations (
@@ -2227,63 +2341,72 @@ async function ensureSchema() {
2227
2341
  sql: `ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0`,
2228
2342
  args: []
2229
2343
  });
2230
- } catch {
2344
+ } catch (e) {
2345
+ logCatchDebug("migration", e);
2231
2346
  }
2232
2347
  try {
2233
2348
  await client.execute({
2234
2349
  sql: `ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5`,
2235
2350
  args: []
2236
2351
  });
2237
- } catch {
2352
+ } catch (e) {
2353
+ logCatchDebug("migration", e);
2238
2354
  }
2239
2355
  try {
2240
2356
  await client.execute({
2241
2357
  sql: `ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'`,
2242
2358
  args: []
2243
2359
  });
2244
- } catch {
2360
+ } catch (e) {
2361
+ logCatchDebug("migration", e);
2245
2362
  }
2246
2363
  try {
2247
2364
  await client.execute({
2248
2365
  sql: `ALTER TABLE memories ADD COLUMN deleted_at TEXT`,
2249
2366
  args: []
2250
2367
  });
2251
- } catch {
2368
+ } catch (e) {
2369
+ logCatchDebug("migration", e);
2252
2370
  }
2253
2371
  try {
2254
2372
  await client.execute({
2255
2373
  sql: `ALTER TABLE memories ADD COLUMN confidence REAL DEFAULT 0.7`,
2256
2374
  args: []
2257
2375
  });
2258
- } catch {
2376
+ } catch (e) {
2377
+ logCatchDebug("migration", e);
2259
2378
  }
2260
2379
  try {
2261
2380
  await client.execute({
2262
2381
  sql: `ALTER TABLE memories ADD COLUMN last_accessed TEXT`,
2263
2382
  args: []
2264
2383
  });
2265
- } catch {
2384
+ } catch (e) {
2385
+ logCatchDebug("migration", e);
2266
2386
  }
2267
2387
  try {
2268
2388
  await client.execute({
2269
2389
  sql: `UPDATE memories SET last_accessed = timestamp WHERE last_accessed IS NULL`,
2270
2390
  args: []
2271
2391
  });
2272
- } catch {
2392
+ } catch (e) {
2393
+ logCatchDebug("migration", e);
2273
2394
  }
2274
2395
  try {
2275
2396
  await client.execute({
2276
2397
  sql: `ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0`,
2277
2398
  args: []
2278
2399
  });
2279
- } catch {
2400
+ } catch (e) {
2401
+ logCatchDebug("migration", e);
2280
2402
  }
2281
2403
  try {
2282
2404
  await client.execute({
2283
2405
  sql: `ALTER TABLE memories ADD COLUMN graph_extracted INTEGER DEFAULT 0`,
2284
2406
  args: []
2285
2407
  });
2286
- } catch {
2408
+ } catch (e) {
2409
+ logCatchDebug("migration", e);
2287
2410
  }
2288
2411
  for (const col of [
2289
2412
  "ALTER TABLE memories ADD COLUMN content_hash TEXT",
@@ -2291,14 +2414,16 @@ async function ensureSchema() {
2291
2414
  ]) {
2292
2415
  try {
2293
2416
  await client.execute(col);
2294
- } catch {
2417
+ } catch (e) {
2418
+ logCatchDebug("migration", e);
2295
2419
  }
2296
2420
  }
2297
2421
  try {
2298
2422
  await client.execute(
2299
2423
  `CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
2300
2424
  );
2301
- } catch {
2425
+ } catch (e) {
2426
+ logCatchDebug("migration", e);
2302
2427
  }
2303
2428
  try {
2304
2429
  await client.execute(
@@ -2306,7 +2431,8 @@ async function ensureSchema() {
2306
2431
  ON memories(content_hash, agent_id, project_name, memory_type)
2307
2432
  WHERE content_hash IS NOT NULL`
2308
2433
  );
2309
- } catch {
2434
+ } catch (e) {
2435
+ logCatchDebug("migration", e);
2310
2436
  }
2311
2437
  await client.executeMultiple(`
2312
2438
  CREATE TABLE IF NOT EXISTS entities (
@@ -2382,7 +2508,8 @@ async function ensureSchema() {
2382
2508
  `);
2383
2509
  try {
2384
2510
  await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
2385
- } catch {
2511
+ } catch (e) {
2512
+ logCatchDebug("migration", e);
2386
2513
  }
2387
2514
  await client.executeMultiple(`
2388
2515
  CREATE TABLE IF NOT EXISTS entity_aliases (
@@ -2397,14 +2524,16 @@ async function ensureSchema() {
2397
2524
  ]) {
2398
2525
  try {
2399
2526
  await client.execute(col);
2400
- } catch {
2527
+ } catch (e) {
2528
+ logCatchDebug("migration", e);
2401
2529
  }
2402
2530
  }
2403
2531
  try {
2404
2532
  await client.execute(
2405
2533
  `CREATE INDEX IF NOT EXISTS idx_memories_status ON memories(status)`
2406
2534
  );
2407
- } catch {
2535
+ } catch (e) {
2536
+ logCatchDebug("migration", e);
2408
2537
  }
2409
2538
  await client.executeMultiple(`
2410
2539
  CREATE TABLE IF NOT EXISTS identity (
@@ -2503,7 +2632,8 @@ async function ensureSchema() {
2503
2632
  sql: `ALTER TABLE memories ADD COLUMN ${column}`,
2504
2633
  args: []
2505
2634
  });
2506
- } catch {
2635
+ } catch (e) {
2636
+ logCatchDebug("migration", e);
2507
2637
  }
2508
2638
  }
2509
2639
  for (const col of [
@@ -2512,7 +2642,8 @@ async function ensureSchema() {
2512
2642
  ]) {
2513
2643
  try {
2514
2644
  await client.execute(col);
2515
- } catch {
2645
+ } catch (e) {
2646
+ logCatchDebug("migration", e);
2516
2647
  }
2517
2648
  }
2518
2649
  await client.executeMultiple(`
@@ -2697,56 +2828,64 @@ async function ensureSchema() {
2697
2828
  args: []
2698
2829
  });
2699
2830
  }
2700
- } catch {
2831
+ } catch (e) {
2832
+ logCatchDebug("session_agent_map backfill", e);
2701
2833
  }
2702
2834
  try {
2703
2835
  await client.execute({
2704
2836
  sql: `ALTER TABLE session_agent_map ADD COLUMN cache_cold_count INTEGER NOT NULL DEFAULT 0`,
2705
2837
  args: []
2706
2838
  });
2707
- } catch {
2839
+ } catch (e) {
2840
+ logCatchDebug("migration", e);
2708
2841
  }
2709
2842
  try {
2710
2843
  await client.execute({
2711
2844
  sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
2712
2845
  args: []
2713
2846
  });
2714
- } catch {
2847
+ } catch (e) {
2848
+ logCatchDebug("migration", e);
2715
2849
  }
2716
2850
  try {
2717
2851
  await client.execute({
2718
2852
  sql: `ALTER TABLE tasks ADD COLUMN budget_fallback_model TEXT`,
2719
2853
  args: []
2720
2854
  });
2721
- } catch {
2855
+ } catch (e) {
2856
+ logCatchDebug("migration", e);
2722
2857
  }
2723
2858
  try {
2724
2859
  await client.execute({
2725
2860
  sql: `ALTER TABLE tasks ADD COLUMN tokens_used INTEGER DEFAULT 0`,
2726
2861
  args: []
2727
2862
  });
2728
- } catch {
2863
+ } catch (e) {
2864
+ logCatchDebug("migration", e);
2729
2865
  }
2730
2866
  try {
2731
2867
  await client.execute({
2732
2868
  sql: `ALTER TABLE tasks ADD COLUMN tokens_warned_at INTEGER`,
2733
2869
  args: []
2734
2870
  });
2735
- } catch {
2871
+ } catch (e) {
2872
+ logCatchDebug("migration", e);
2736
2873
  }
2737
2874
  try {
2738
2875
  await client.execute({
2739
2876
  sql: `ALTER TABLE tasks ADD COLUMN spawn_runtime TEXT`,
2740
2877
  args: []
2741
2878
  });
2742
- } catch {
2879
+ } catch (e) {
2880
+ logCatchDebug("migration", e);
2743
2881
  }
2744
2882
  try {
2745
2883
  await client.execute({
2746
2884
  sql: `ALTER TABLE tasks ADD COLUMN spawn_model TEXT`,
2747
2885
  args: []
2748
2886
  });
2749
- } catch {
2887
+ } catch (e) {
2888
+ logCatchDebug("migration", e);
2750
2889
  }
2751
2890
  await client.executeMultiple(`
2752
2891
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
@@ -2945,13 +3084,15 @@ async function ensureSchema() {
2945
3084
  sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
2946
3085
  args: []
2947
3086
  });
2948
- } catch {
3087
+ } catch (e) {
3088
+ logCatchDebug("migration", e);
2949
3089
  }
2950
3090
  try {
2951
3091
  await client.execute(
2952
3092
  `CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)`
2953
3093
  );
2954
- } catch {
3094
+ } catch (e) {
3095
+ logCatchDebug("migration", e);
2955
3096
  }
2956
3097
  try {
2957
3098
  await client.execute({
@@ -2962,20 +3103,23 @@ async function ensureSchema() {
2962
3103
  sql: `UPDATE memories SET tier = 2 WHERE tool_name IN ('store_memory', 'manual') AND importance >= 5 AND tier = 3`,
2963
3104
  args: []
2964
3105
  });
2965
- } catch {
3106
+ } catch (e) {
3107
+ logCatchDebug("migration", e);
2966
3108
  }
2967
3109
  try {
2968
3110
  await client.execute({
2969
3111
  sql: `ALTER TABLE memories ADD COLUMN supersedes_id TEXT`,
2970
3112
  args: []
2971
3113
  });
2972
- } catch {
3114
+ } catch (e) {
3115
+ logCatchDebug("migration", e);
2973
3116
  }
2974
3117
  try {
2975
3118
  await client.execute(
2976
3119
  `CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL`
2977
3120
  );
2978
- } catch {
3121
+ } catch (e) {
3122
+ logCatchDebug("migration", e);
2979
3123
  }
2980
3124
  for (const col of [
2981
3125
  "ALTER TABLE tasks ADD COLUMN checkpoint TEXT",
@@ -2983,7 +3127,8 @@ async function ensureSchema() {
2983
3127
  ]) {
2984
3128
  try {
2985
3129
  await client.execute(col);
2986
- } catch {
3130
+ } catch (e) {
3131
+ logCatchDebug("migration", e);
2987
3132
  }
2988
3133
  }
2989
3134
  try {
@@ -2991,13 +3136,15 @@ async function ensureSchema() {
2991
3136
  sql: `ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0`,
2992
3137
  args: []
2993
3138
  });
2994
- } catch {
3139
+ } catch (e) {
3140
+ logCatchDebug("migration", e);
2995
3141
  }
2996
3142
  try {
2997
3143
  await client.execute(
2998
3144
  `CREATE INDEX IF NOT EXISTS idx_memories_draft ON memories(draft) WHERE draft = 1`
2999
3145
  );
3000
- } catch {
3146
+ } catch (e) {
3147
+ logCatchDebug("migration", e);
3001
3148
  }
3002
3149
  for (const col of [
3003
3150
  "ALTER TABLE memories ADD COLUMN valid_from TEXT",
@@ -3005,7 +3152,8 @@ async function ensureSchema() {
3005
3152
  ]) {
3006
3153
  try {
3007
3154
  await client.execute(col);
3008
- } catch {
3155
+ } catch (e) {
3156
+ logCatchDebug("migration", e);
3009
3157
  }
3010
3158
  }
3011
3159
  try {
@@ -3013,27 +3161,31 @@ async function ensureSchema() {
3013
3161
  sql: `UPDATE memories SET valid_from = timestamp WHERE valid_from IS NULL`,
3014
3162
  args: []
3015
3163
  });
3016
- } catch {
3164
+ } catch (e) {
3165
+ logCatchDebug("migration", e);
3017
3166
  }
3018
3167
  try {
3019
3168
  await client.execute({
3020
3169
  sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
3021
3170
  args: []
3022
3171
  });
3023
- } catch {
3172
+ } catch (e) {
3173
+ logCatchDebug("migration", e);
3024
3174
  }
3025
3175
  try {
3026
3176
  await client.execute(
3027
3177
  `CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(memory_type)`
3028
3178
  );
3029
- } catch {
3179
+ } catch (e) {
3180
+ logCatchDebug("migration", e);
3030
3181
  }
3031
3182
  try {
3032
3183
  await client.execute({
3033
3184
  sql: `ALTER TABLE memories ADD COLUMN trajectory TEXT`,
3034
3185
  args: []
3035
3186
  });
3036
- } catch {
3187
+ } catch (e) {
3188
+ logCatchDebug("migration", e);
3037
3189
  }
3038
3190
  for (const col of [
3039
3191
  "ALTER TABLE memories ADD COLUMN intent TEXT",
@@ -3054,7 +3206,8 @@ async function ensureSchema() {
3054
3206
  ]) {
3055
3207
  try {
3056
3208
  await client.execute(col);
3057
- } catch {
3209
+ } catch (e) {
3210
+ logCatchDebug("migration", e);
3058
3211
  }
3059
3212
  }
3060
3213
  try {
@@ -3062,14 +3215,16 @@ async function ensureSchema() {
3062
3215
  sql: `ALTER TABLE memories ADD COLUMN procedure_for TEXT`,
3063
3216
  args: []
3064
3217
  });
3065
- } catch {
3218
+ } catch (e) {
3219
+ logCatchDebug("migration", e);
3066
3220
  }
3067
3221
  try {
3068
3222
  await client.execute({
3069
3223
  sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
3070
3224
  args: []
3071
3225
  });
3072
- } catch {
3226
+ } catch (e) {
3227
+ logCatchDebug("migration", e);
3073
3228
  }
3074
3229
  }
3075
3230
  async function disposeDatabase() {
@@ -3080,7 +3235,8 @@ async function disposeDatabase() {
3080
3235
  if (_client) {
3081
3236
  try {
3082
3237
  await _client.execute("PRAGMA wal_checkpoint(PASSIVE)");
3083
- } catch {
3238
+ } catch (e) {
3239
+ logCatchDebug("WAL checkpoint", e);
3084
3240
  }
3085
3241
  }
3086
3242
  if (_daemonClient) {
@@ -3096,8 +3252,9 @@ async function disposeDatabase() {
3096
3252
  _client = null;
3097
3253
  _resilientClient = null;
3098
3254
  }
3255
+ releaseDbLock();
3099
3256
  }
3100
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, SOFT_DELETE_RETENTION_DAYS, disposeTurso;
3257
+ var _debugDb, _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, _lockFd, DB_LOCK_PATH, initTurso, SOFT_DELETE_RETENTION_DAYS, disposeTurso;
3101
3258
  var init_database = __esm({
3102
3259
  "src/lib/database.ts"() {
3103
3260
  "use strict";
@@ -3105,11 +3262,14 @@ var init_database = __esm({
3105
3262
  init_employees();
3106
3263
  init_database_adapter();
3107
3264
  init_memory();
3265
+ _debugDb = process.env.EXE_DEBUG === "1";
3108
3266
  _client = null;
3109
3267
  _resilientClient = null;
3110
3268
  _walCheckpointTimer = null;
3111
3269
  _daemonClient = null;
3112
3270
  _adapterClient = null;
3271
+ _lockFd = null;
3272
+ DB_LOCK_PATH = join(homedir(), ".exe-os", "db.lock");
3113
3273
  initTurso = initDatabase;
3114
3274
  SOFT_DELETE_RETENTION_DAYS = 7;
3115
3275
  disposeTurso = disposeDatabase;
@@ -3118,7 +3278,7 @@ var init_database = __esm({
3118
3278
 
3119
3279
  // src/lib/keychain.ts
3120
3280
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3121
- import { existsSync as existsSync6, statSync as statSync2 } from "fs";
3281
+ import { existsSync as existsSync7, statSync as statSync3 } from "fs";
3122
3282
  import { execSync as execSync3 } from "child_process";
3123
3283
  import path6 from "path";
3124
3284
  import os5 from "os";
@@ -3153,7 +3313,7 @@ function isRootOnlyTrustedServerKeyFile(keyPath) {
3153
3313
  if (process.platform !== "linux") return false;
3154
3314
  try {
3155
3315
  const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
3156
- const st = statSync2(keyPath);
3316
+ const st = statSync3(keyPath);
3157
3317
  if (!st.isFile() || (st.mode & 63) !== 0) return false;
3158
3318
  if (uid === 0) return true;
3159
3319
  const exeOsDir = process.env.EXE_OS_DIR;
@@ -3354,7 +3514,7 @@ async function getMasterKey() {
3354
3514
  }
3355
3515
  }
3356
3516
  const keyPath = getKeyPath();
3357
- if (!existsSync6(keyPath)) {
3517
+ if (!existsSync7(keyPath)) {
3358
3518
  process.stderr.write(
3359
3519
  `[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
3360
3520
  `
@@ -3689,18 +3849,54 @@ __export(shard_manager_exports, {
3689
3849
  shardExists: () => shardExists
3690
3850
  });
3691
3851
  import path7 from "path";
3692
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync, renameSync as renameSync3, statSync as statSync3 } from "fs";
3852
+ import { existsSync as existsSync8, mkdirSync as mkdirSync3, readdirSync, renameSync as renameSync3, statSync as statSync4 } from "fs";
3693
3853
  import { createClient as createClient2 } from "@libsql/client";
3694
3854
  function initShardManager(encryptionKey) {
3695
3855
  _encryptionKey = encryptionKey;
3696
- if (!existsSync7(SHARDS_DIR)) {
3697
- mkdirSync2(SHARDS_DIR, { recursive: true });
3856
+ _keyValidated = false;
3857
+ _keyValidationPromise = null;
3858
+ if (!existsSync8(SHARDS_DIR)) {
3859
+ mkdirSync3(SHARDS_DIR, { recursive: true });
3860
+ }
3861
+ const existingShards = readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db"));
3862
+ if (existingShards.length === 0) {
3863
+ _keyValidated = true;
3698
3864
  }
3699
3865
  _shardingEnabled = true;
3700
3866
  if (_evictionTimer) clearInterval(_evictionTimer);
3701
3867
  _evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
3702
3868
  _evictionTimer.unref();
3703
3869
  }
3870
+ async function validateEncryptionKey() {
3871
+ if (_keyValidated) return true;
3872
+ if (!_encryptionKey) return false;
3873
+ const existingShards = readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db"));
3874
+ if (existingShards.length === 0) {
3875
+ _keyValidated = true;
3876
+ return true;
3877
+ }
3878
+ for (const shardFile of existingShards.slice(0, 3)) {
3879
+ const dbPath = path7.join(SHARDS_DIR, shardFile);
3880
+ const testClient = createClient2({ url: `file:${dbPath}`, encryptionKey: _encryptionKey });
3881
+ try {
3882
+ await testClient.execute("SELECT COUNT(*) FROM sqlite_schema");
3883
+ testClient.close();
3884
+ _keyValidated = true;
3885
+ return true;
3886
+ } catch {
3887
+ try {
3888
+ testClient.close();
3889
+ } catch {
3890
+ }
3891
+ }
3892
+ }
3893
+ process.stderr.write(
3894
+ `[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.
3895
+ `
3896
+ );
3897
+ _shardingEnabled = false;
3898
+ return false;
3899
+ }
3704
3900
  function isShardingEnabled() {
3705
3901
  return _shardingEnabled;
3706
3902
  }
@@ -3734,13 +3930,13 @@ function getShardClient(projectName) {
3734
3930
  }
3735
3931
  function shardExists(projectName) {
3736
3932
  const safeName = safeShardName(projectName);
3737
- return existsSync7(path7.join(SHARDS_DIR, `${safeName}.db`));
3933
+ return existsSync8(path7.join(SHARDS_DIR, `${safeName}.db`));
3738
3934
  }
3739
3935
  function safeShardName(projectName) {
3740
3936
  return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
3741
3937
  }
3742
3938
  function listShards() {
3743
- if (!existsSync7(SHARDS_DIR)) return [];
3939
+ if (!existsSync8(SHARDS_DIR)) return [];
3744
3940
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
3745
3941
  }
3746
3942
  async function auditShardHealth(options = {}) {
@@ -3753,7 +3949,7 @@ async function auditShardHealth(options = {}) {
3753
3949
  const shards = [];
3754
3950
  for (const name of names) {
3755
3951
  const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
3756
- const stat = statSync3(dbPath);
3952
+ const stat = statSync4(dbPath);
3757
3953
  const item = {
3758
3954
  name,
3759
3955
  path: dbPath,
@@ -3993,6 +4189,17 @@ async function ensureShardSchema(client) {
3993
4189
  }
3994
4190
  }
3995
4191
  async function getReadyShardClient(projectName) {
4192
+ if (!_keyValidated) {
4193
+ if (!_keyValidationPromise) {
4194
+ _keyValidationPromise = validateEncryptionKey();
4195
+ }
4196
+ const valid = await _keyValidationPromise;
4197
+ if (!valid) {
4198
+ throw new Error(
4199
+ `Shard creation blocked: encryption key mismatch with existing shards. Run /exe-doctor to audit.`
4200
+ );
4201
+ }
4202
+ }
3996
4203
  const safeName = safeShardName(projectName);
3997
4204
  let client = getShardClient(projectName);
3998
4205
  try {
@@ -4005,8 +4212,8 @@ async function getReadyShardClient(projectName) {
4005
4212
  _shards.delete(safeName);
4006
4213
  _shardLastAccess.delete(safeName);
4007
4214
  const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
4008
- if (existsSync7(dbPath)) {
4009
- const stat = statSync3(dbPath);
4215
+ if (existsSync8(dbPath)) {
4216
+ const stat = statSync4(dbPath);
4010
4217
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4011
4218
  const archivedPath = path7.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
4012
4219
  renameSync3(dbPath, archivedPath);
@@ -4071,7 +4278,7 @@ function disposeShards() {
4071
4278
  _shardingEnabled = false;
4072
4279
  _encryptionKey = null;
4073
4280
  }
4074
- var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
4281
+ var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled, _keyValidated, _keyValidationPromise;
4075
4282
  var init_shard_manager = __esm({
4076
4283
  "src/lib/shard-manager.ts"() {
4077
4284
  "use strict";
@@ -4085,6 +4292,8 @@ var init_shard_manager = __esm({
4085
4292
  _evictionTimer = null;
4086
4293
  _encryptionKey = null;
4087
4294
  _shardingEnabled = false;
4295
+ _keyValidated = false;
4296
+ _keyValidationPromise = null;
4088
4297
  }
4089
4298
  });
4090
4299
 
@@ -4280,6 +4489,18 @@ var init_platform_procedures = __esm({
4280
4489
  priority: "p0",
4281
4490
  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.'"
4282
4491
  },
4492
+ {
4493
+ title: "Review chain \u2014 managers must actively pull completed work, never wait for nudges",
4494
+ domain: "workflow",
4495
+ priority: "p0",
4496
+ 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."
4497
+ },
4498
+ {
4499
+ title: "Bug fix lifecycle \u2014 triage upstream after every verified fix so customers see the status",
4500
+ domain: "workflow",
4501
+ priority: "p0",
4502
+ 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."
4503
+ },
4283
4504
  {
4284
4505
  title: "Intercom is a speedup, not delivery \u2014 DB is the source of truth",
4285
4506
  domain: "architecture",
@@ -4877,6 +5098,20 @@ __export(store_exports, {
4877
5098
  vectorToBlob: () => vectorToBlob,
4878
5099
  writeMemory: () => writeMemory
4879
5100
  });
5101
+ function logStoreDebug(context, err) {
5102
+ if (_debugStore) {
5103
+ process.stderr.write(
5104
+ `[store] ${context}: ${err instanceof Error ? err.message : String(err)}
5105
+ `
5106
+ );
5107
+ }
5108
+ }
5109
+ function logStoreWarn(context, err) {
5110
+ process.stderr.write(
5111
+ `[store] WARN ${context}: ${err instanceof Error ? err.message : String(err)}
5112
+ `
5113
+ );
5114
+ }
4880
5115
  function isBusyError2(err) {
4881
5116
  if (err instanceof Error) {
4882
5117
  const msg = err.message.toLowerCase();
@@ -4931,13 +5166,15 @@ async function initStore(options) {
4931
5166
  try {
4932
5167
  const { initDaemonClient: initDaemonClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4933
5168
  await initDaemonClient2();
4934
- } catch {
5169
+ } catch (e) {
5170
+ logStoreWarn("catch", e);
4935
5171
  }
4936
5172
  if (!options?.lightweight) {
4937
5173
  try {
4938
5174
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
4939
5175
  initShardManager2(hexKey);
4940
- } catch {
5176
+ } catch (e) {
5177
+ logStoreWarn("catch", e);
4941
5178
  }
4942
5179
  const client = getClient();
4943
5180
  const vResult = await retryOnBusy2(
@@ -4948,7 +5185,8 @@ async function initStore(options) {
4948
5185
  try {
4949
5186
  const { loadGlobalProcedures: loadGlobalProcedures2 } = await Promise.resolve().then(() => (init_global_procedures(), global_procedures_exports));
4950
5187
  await loadGlobalProcedures2();
4951
- } catch {
5188
+ } catch (e) {
5189
+ logStoreWarn("catch", e);
4952
5190
  }
4953
5191
  }
4954
5192
  }
@@ -5008,7 +5246,8 @@ async function writeMemory(record) {
5008
5246
  memoryType
5009
5247
  });
5010
5248
  if (existing) return;
5011
- } catch {
5249
+ } catch (e) {
5250
+ logStoreWarn("catch", e);
5012
5251
  }
5013
5252
  const dbRow = {
5014
5253
  id: record.id,
@@ -5209,12 +5448,14 @@ async function flushBatch() {
5209
5448
  try {
5210
5449
  const { insertMemoryCardsForBatch: insertMemoryCardsForBatch2 } = await Promise.resolve().then(() => (init_memory_cards(), memory_cards_exports));
5211
5450
  await insertMemoryCardsForBatch2(batch);
5212
- } catch {
5451
+ } catch (e) {
5452
+ logStoreWarn("catch", e);
5213
5453
  }
5214
5454
  try {
5215
5455
  const { insertOntologyForBatch: insertOntologyForBatch2 } = await Promise.resolve().then(() => (init_agentic_ontology(), agentic_ontology_exports));
5216
5456
  await insertOntologyForBatch2(batch);
5217
- } catch {
5457
+ } catch (e) {
5458
+ logStoreWarn("catch", e);
5218
5459
  }
5219
5460
  schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
5220
5461
  _pendingRecords.splice(0, batch.length);
@@ -5253,7 +5494,8 @@ ${err.stack.split("\n").slice(1, 3).join("\n")}` : ""}` : String(err);
5253
5494
  }
5254
5495
  }
5255
5496
  }
5256
- } catch {
5497
+ } catch (e) {
5498
+ logStoreWarn("catch", e);
5257
5499
  }
5258
5500
  return batch.length;
5259
5501
  } finally {
@@ -5295,7 +5537,8 @@ async function searchMemories(queryVector, agentId, options) {
5295
5537
  } else {
5296
5538
  client = getClient();
5297
5539
  }
5298
- } catch {
5540
+ } catch (e) {
5541
+ logStoreDebug("shard routing fallback", e);
5299
5542
  client = getClient();
5300
5543
  }
5301
5544
  const limit = options?.limit ?? 10;
@@ -5411,7 +5654,8 @@ async function attachDocumentMetadata(records) {
5411
5654
  if (!record.document_id) continue;
5412
5655
  record.document_metadata = byId.get(record.document_id) ?? null;
5413
5656
  }
5414
- } catch {
5657
+ } catch (e) {
5658
+ logStoreWarn("catch", e);
5415
5659
  }
5416
5660
  return records;
5417
5661
  }
@@ -5472,11 +5716,12 @@ async function getMemoryCardinality(agentId) {
5472
5716
  args: [agentId]
5473
5717
  });
5474
5718
  return Number(result.rows[0]?.cnt) || 0;
5475
- } catch {
5719
+ } catch (e) {
5720
+ logStoreWarn("getMemoryCardinality", e);
5476
5721
  return 0;
5477
5722
  }
5478
5723
  }
5479
- var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
5724
+ var _debugStore, INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
5480
5725
  var init_store = __esm({
5481
5726
  "src/lib/store.ts"() {
5482
5727
  "use strict";
@@ -5486,6 +5731,7 @@ var init_store = __esm({
5486
5731
  init_config();
5487
5732
  init_state_bus();
5488
5733
  init_memory_write_governor();
5734
+ _debugStore = process.env.EXE_DEBUG === "1";
5489
5735
  INIT_MAX_RETRIES = 3;
5490
5736
  INIT_RETRY_DELAY_MS = 1e3;
5491
5737
  _pendingRecords = [];
@@ -5597,7 +5843,7 @@ __export(reranker_exports, {
5597
5843
  rerankWithScores: () => rerankWithScores
5598
5844
  });
5599
5845
  import path8 from "path";
5600
- import { existsSync as existsSync8 } from "fs";
5846
+ import { existsSync as existsSync9 } from "fs";
5601
5847
  function resetIdleTimer() {
5602
5848
  if (_idleTimer) clearTimeout(_idleTimer);
5603
5849
  _idleTimer = setTimeout(() => {
@@ -5608,7 +5854,7 @@ function resetIdleTimer() {
5608
5854
  }
5609
5855
  }
5610
5856
  function isRerankerAvailable() {
5611
- return existsSync8(path8.join(MODELS_DIR, RERANKER_MODEL_FILE));
5857
+ return existsSync9(path8.join(MODELS_DIR, RERANKER_MODEL_FILE));
5612
5858
  }
5613
5859
  function getRerankerModelPath() {
5614
5860
  return path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
@@ -5619,7 +5865,7 @@ async function ensureLoaded() {
5619
5865
  return;
5620
5866
  }
5621
5867
  const modelPath = path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
5622
- if (!existsSync8(modelPath)) {
5868
+ if (!existsSync9(modelPath)) {
5623
5869
  throw new Error(
5624
5870
  `Reranker model not found at ${modelPath}. Run /exe-setup to download it.`
5625
5871
  );
@@ -5752,10 +5998,10 @@ async function disposeEmbedder() {
5752
5998
  async function embedDirect(text) {
5753
5999
  const llamaCpp = await import("node-llama-cpp");
5754
6000
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
5755
- const { existsSync: existsSync10 } = await import("fs");
6001
+ const { existsSync: existsSync11 } = await import("fs");
5756
6002
  const path11 = await import("path");
5757
6003
  const modelPath = path11.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
5758
- if (!existsSync10(modelPath)) {
6004
+ if (!existsSync11(modelPath)) {
5759
6005
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
5760
6006
  }
5761
6007
  const llama = await llamaCpp.getLlama();
@@ -5840,7 +6086,7 @@ __export(file_grep_exports, {
5840
6086
  grepProjectFiles: () => grepProjectFiles
5841
6087
  });
5842
6088
  import { execSync as execSync5 } from "child_process";
5843
- import { readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync4, existsSync as existsSync9 } from "fs";
6089
+ import { readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync5, existsSync as existsSync10 } from "fs";
5844
6090
  import path10 from "path";
5845
6091
  import crypto2 from "crypto";
5846
6092
  function hasRipgrep() {
@@ -5957,7 +6203,7 @@ function grepWithNodeFs(pattern, projectRoot, patterns) {
5957
6203
  for (const filePath of files.slice(0, MAX_FILES)) {
5958
6204
  const absPath = path10.join(projectRoot, filePath);
5959
6205
  try {
5960
- const stat = statSync4(absPath);
6206
+ const stat = statSync5(absPath);
5961
6207
  if (stat.size > MAX_FILE_SIZE) continue;
5962
6208
  const content = readFileSync5(absPath, "utf8");
5963
6209
  const lines = content.split("\n");
@@ -6031,7 +6277,7 @@ function matchGlob(filePath, pattern) {
6031
6277
  function buildSnippet(hit, projectRoot) {
6032
6278
  try {
6033
6279
  const absPath = path10.join(projectRoot, hit.filePath);
6034
- if (!existsSync9(absPath)) return hit.matchLine;
6280
+ if (!existsSync10(absPath)) return hit.matchLine;
6035
6281
  const lines = readFileSync5(absPath, "utf8").split("\n");
6036
6282
  const start = Math.max(0, hit.lineNumber - 3);
6037
6283
  const end = Math.min(lines.length, hit.lineNumber + 2);
@@ -6956,7 +7202,8 @@ function rrfMergeMulti(lists, limit, k = RRF_K, weights) {
6956
7202
  const confidence = sourceConfidence(e.record);
6957
7203
  const frecency = frecencyBoost(e.record.last_accessed, e.record.timestamp);
6958
7204
  const baseScore = e.rrfScore * 0.45 + recency * 0.24 + importance * 0.21 + confidence * 0.1;
6959
- const finalScore = baseScore * (1 + 0.3 * frecency);
7205
+ const procedureBoost = e.record.memory_type === "procedure" ? 0.1 : 0;
7206
+ const finalScore = (baseScore + procedureBoost) * (1 + 0.3 * frecency);
6960
7207
  return { score: finalScore, record: e.record };
6961
7208
  });
6962
7209
  return entries.sort((a, b) => b.score - a.score).slice(0, limit).map((e) => e.record);
@@ -7008,7 +7255,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
7008
7255
  m.importance, m.status, m.confidence, m.last_accessed,
7009
7256
  m.workspace_id, m.document_id, m.user_id,
7010
7257
  m.char_offset, m.page_number,
7011
- m.source_path, m.source_type
7258
+ m.source_path, m.source_type, m.memory_type
7012
7259
  FROM memories m
7013
7260
  JOIN memories_fts fts ON m.rowid = fts.rowid
7014
7261
  WHERE memories_fts MATCH ?
@@ -7066,7 +7313,8 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
7066
7313
  char_offset: row.char_offset ?? null,
7067
7314
  page_number: row.page_number ?? null,
7068
7315
  source_path: row.source_path ?? null,
7069
- source_type: row.source_type ?? null
7316
+ source_type: row.source_type ?? null,
7317
+ memory_type: row.memory_type ?? void 0
7070
7318
  }));
7071
7319
  }
7072
7320
  async function recentRecords(agentId, options, limit, textFilter) {
@@ -7091,7 +7339,7 @@ async function recentRecords(agentId, options, limit, textFilter) {
7091
7339
  importance, status, confidence, last_accessed,
7092
7340
  workspace_id, document_id, user_id,
7093
7341
  char_offset, page_number,
7094
- source_path, source_type
7342
+ source_path, source_type, memory_type
7095
7343
  FROM memories
7096
7344
  WHERE agent_id = ?
7097
7345
  AND timestamp >= ? AND timestamp <= ?
@@ -7125,7 +7373,7 @@ async function recentRecords(agentId, options, limit, textFilter) {
7125
7373
  importance, status, confidence, last_accessed,
7126
7374
  workspace_id, document_id, user_id,
7127
7375
  char_offset, page_number,
7128
- source_path, source_type
7376
+ source_path, source_type, memory_type
7129
7377
  FROM memories
7130
7378
  WHERE agent_id = ?${statusFilter}${draftFilter}
7131
7379
  AND COALESCE(confidence, 0.7) >= 0.3`;
@@ -7200,7 +7448,8 @@ function rowToMemoryRecord(row) {
7200
7448
  char_offset: row.char_offset ?? null,
7201
7449
  page_number: row.page_number ?? null,
7202
7450
  source_path: row.source_path ?? null,
7203
- source_type: row.source_type ?? null
7451
+ source_type: row.source_type ?? null,
7452
+ memory_type: row.memory_type ?? void 0
7204
7453
  };
7205
7454
  }
7206
7455
  var KNOWN_TOOLS = /* @__PURE__ */ new Set([
@@ -7239,7 +7488,7 @@ async function trajectoryBypass(queryText, agentId, options, limit) {
7239
7488
  importance, status, confidence, last_accessed,
7240
7489
  workspace_id, document_id, user_id,
7241
7490
  char_offset, page_number,
7242
- source_path, source_type
7491
+ source_path, source_type, memory_type
7243
7492
  FROM memories
7244
7493
  WHERE trajectory IS NOT NULL
7245
7494
  AND json_extract(trajectory, '$.tool') = ?
@@ -7285,7 +7534,8 @@ async function trajectoryBypass(queryText, agentId, options, limit) {
7285
7534
  char_offset: row.char_offset ?? null,
7286
7535
  page_number: row.page_number ?? null,
7287
7536
  source_path: row.source_path ?? null,
7288
- source_type: row.source_type ?? null
7537
+ source_type: row.source_type ?? null,
7538
+ memory_type: row.memory_type ?? void 0
7289
7539
  }));
7290
7540
  } catch {
7291
7541
  return null;