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