@askexenow/exe-os 0.9.245 → 0.9.246

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 (146) hide show
  1. package/dist/{backfill-metadata-DFTIGPXP.js → backfill-metadata-YPCH2VDT.js} +1 -1
  2. package/dist/bin/agentic-ontology-backfill.js +1 -1
  3. package/dist/bin/agentic-reflection-backfill.js +1 -1
  4. package/dist/bin/agentic-semantic-label.js +1 -1
  5. package/dist/bin/backfill-conversations.js +1 -1
  6. package/dist/bin/backfill-responses.js +1 -1
  7. package/dist/bin/backfill-vectors.js +2 -2
  8. package/dist/bin/bulk-sync-postgres.js +1 -1
  9. package/dist/bin/cleanup-stale-review-tasks.js +2 -2
  10. package/dist/bin/cli.js +5 -5
  11. package/dist/bin/exe-assign.js +1 -1
  12. package/dist/bin/exe-boot.js +3 -3
  13. package/dist/bin/exe-dispatch.js +2 -2
  14. package/dist/bin/exe-doctor.js +1 -1
  15. package/dist/bin/exe-export-behaviors.js +2 -2
  16. package/dist/bin/exe-forget.js +3 -3
  17. package/dist/bin/exe-gateway.js +5 -5
  18. package/dist/bin/exe-heartbeat.js +2 -2
  19. package/dist/bin/exe-kill.js +3 -3
  20. package/dist/bin/exe-launch-agent.js +3 -3
  21. package/dist/bin/exe-pending-messages.js +3 -3
  22. package/dist/bin/exe-pending-notifications.js +2 -2
  23. package/dist/bin/exe-pending-reviews.js +2 -2
  24. package/dist/bin/exe-review.js +3 -3
  25. package/dist/bin/exe-search.js +2 -2
  26. package/dist/bin/exe-session-cleanup.js +5 -5
  27. package/dist/bin/exe-start-codex.js +1 -1
  28. package/dist/bin/exe-start-opencode.js +1 -1
  29. package/dist/bin/exe-status.js +3 -3
  30. package/dist/bin/exe-team.js +1 -1
  31. package/dist/bin/git-sweep.js +2 -2
  32. package/dist/bin/graph-backfill.js +1 -1
  33. package/dist/bin/graph-export.js +2 -2
  34. package/dist/bin/import-history.js +2 -2
  35. package/dist/bin/intercom-check.js +3 -3
  36. package/dist/bin/mcp-sessions.js +2 -2
  37. package/dist/bin/orchestration-metrics.js +1 -1
  38. package/dist/bin/pre-publish.js +5 -1
  39. package/dist/bin/scan-tasks.js +2 -2
  40. package/dist/bin/shard-migrate.js +1 -1
  41. package/dist/bin/stack-update.js +11 -3
  42. package/dist/bin/vps-health-gate.js +1 -1
  43. package/dist/{capacity-monitor-4CSBC7AP.js → capacity-monitor-MX24P5JT.js} +2 -2
  44. package/dist/{catchup-brief-KTDI32BO.js → catchup-brief-Z6Z32E6A.js} +3 -3
  45. package/dist/{chunk-KEZXW3RP.js → chunk-4LJIFVLX.js} +1 -1
  46. package/dist/{chunk-F7JLZXHC.js → chunk-66AQSKG7.js} +14 -4
  47. package/dist/{chunk-DGBGIXCC.js → chunk-6BRFIYYW.js} +1 -1
  48. package/dist/{chunk-NBY6R37W.js → chunk-A7UWQXYD.js} +4 -4
  49. package/dist/{chunk-HOGTZLVU.js → chunk-AJAEUU76.js} +2 -2
  50. package/dist/{chunk-LAOB5BKV.js → chunk-AYPCH26U.js} +1 -1
  51. package/dist/{chunk-NEYQAEYU.js → chunk-CC4RTOVL.js} +2 -2
  52. package/dist/{chunk-GHT4REOS.js → chunk-EK3VNK35.js} +1 -1
  53. package/dist/{chunk-6JAGJN77.js → chunk-FHQSMTU4.js} +1 -1
  54. package/dist/{chunk-V2GZMY6O.js → chunk-GO2O5J42.js} +1 -1
  55. package/dist/{chunk-KLQI7QY4.js → chunk-HBIO3RKF.js} +2 -2
  56. package/dist/{chunk-DR5BGWFR.js → chunk-IPWA6HOZ.js} +1 -1
  57. package/dist/{chunk-ZYRMTXBF.js → chunk-JFVITKXI.js} +1 -1
  58. package/dist/{chunk-VQAP35DA.js → chunk-JHR3MR3S.js} +3 -3
  59. package/dist/{chunk-WAO67AYG.js → chunk-JO6EILLF.js} +31 -31
  60. package/dist/{chunk-LSFHEMVI.js → chunk-K423J5RE.js} +3 -3
  61. package/dist/{chunk-OCLP7WP4.js → chunk-KOCQAMAM.js} +1 -1
  62. package/dist/{chunk-CPXGLSIL.js → chunk-LROJFFSZ.js} +1 -1
  63. package/dist/{chunk-HANG6NLF.js → chunk-LZR2MD6I.js} +1 -1
  64. package/dist/{chunk-LQSFP2BV.js → chunk-P7GRGLNV.js} +1 -1
  65. package/dist/{chunk-PI6V23GF.js → chunk-QVJ5FNBI.js} +1 -1
  66. package/dist/{chunk-RYDHEWYY.js → chunk-QZIOX5KX.js} +2 -2
  67. package/dist/{chunk-OWQ3CCYJ.js → chunk-RCDV3PYU.js} +1 -1
  68. package/dist/{chunk-NRVV4Y5V.js → chunk-RY3RVDBK.js} +2 -2
  69. package/dist/{chunk-3V53HH5T.js → chunk-UWGJ3ZLP.js} +2 -2
  70. package/dist/{chunk-QUNKPR6Y.js → chunk-VNBYL5HB.js} +3 -3
  71. package/dist/{chunk-J7V7LPPX.js → chunk-VUTIP75I.js} +2 -2
  72. package/dist/{chunk-2MNLSUC6.js → chunk-XGJ5QYBU.js} +4 -4
  73. package/dist/{chunk-CWITU7DW.js → chunk-ZGTV62EE.js} +1 -1
  74. package/dist/{crm-webhook-WK3PYJJK.js → crm-webhook-YUFMGYQ4.js} +2 -2
  75. package/dist/{cto-delegation-gate-5JZORQIT.js → cto-delegation-gate-R7Y6DK4U.js} +1 -1
  76. package/dist/{daemon-orchestration-BJ3T5MMF.js → daemon-orchestration-FR3LV4G3.js} +4 -4
  77. package/dist/{dreaming-BOSBDRI3.js → dreaming-2KKKDADN.js} +2 -2
  78. package/dist/{exe-export-7N5PBCMK.js → exe-export-M2VOFX25.js} +1 -1
  79. package/dist/{exe-import-YOOE7S3H.js → exe-import-2SKHXCJX.js} +1 -1
  80. package/dist/{exe-snapshot-ODUCFW7G.js → exe-snapshot-YSVZSTRN.js} +3 -3
  81. package/dist/{fast-db-init-YSR7RMVZ.js → fast-db-init-ODXXUTSD.js} +1 -1
  82. package/dist/gateway/index.js +6 -6
  83. package/dist/{git-task-sweep-L3U3T5HM.js → git-task-sweep-XTWQO64K.js} +2 -2
  84. package/dist/hooks/bug-report-worker.js +4 -4
  85. package/dist/hooks/codex-stop-task-finalizer.js +4 -4
  86. package/dist/hooks/commit-complete.js +4 -4
  87. package/dist/hooks/error-recall.js +2 -2
  88. package/dist/hooks/ingest.js +2 -2
  89. package/dist/hooks/instructions-loaded.js +1 -1
  90. package/dist/hooks/manifest.json +18 -18
  91. package/dist/hooks/notification.js +1 -1
  92. package/dist/hooks/post-compact.js +2 -2
  93. package/dist/hooks/post-tool-combined.js +2 -2
  94. package/dist/hooks/pre-compact.js +3 -3
  95. package/dist/hooks/pre-tool-use.js +6 -6
  96. package/dist/hooks/prompt-submit.js +8 -8
  97. package/dist/hooks/session-end.js +5 -5
  98. package/dist/hooks/session-start.js +5 -5
  99. package/dist/hooks/stop.js +5 -5
  100. package/dist/hooks/subagent-stop.js +2 -2
  101. package/dist/hooks/summary-worker.js +5 -5
  102. package/dist/index.js +9 -9
  103. package/dist/lib/consolidation.js +2 -2
  104. package/dist/lib/exe-daemon.js +189 -103
  105. package/dist/lib/hybrid-search.js +2 -2
  106. package/dist/lib/messaging.js +2 -2
  107. package/dist/lib/schedules.js +2 -2
  108. package/dist/lib/store.js +1 -1
  109. package/dist/lib/tasks.js +3 -3
  110. package/dist/lib/tmux-routing.js +1 -1
  111. package/dist/mcp/register-tools.js +24 -24
  112. package/dist/mcp/server.js +25 -25
  113. package/dist/mcp/tools/create-task.js +4 -4
  114. package/dist/mcp/tools/list-tasks.js +4 -4
  115. package/dist/mcp/tools/send-message.js +3 -3
  116. package/dist/mcp/tools/update-task.js +4 -4
  117. package/dist/{notifications-P3XQZDTH.js → notifications-Q7ZBG2CR.js} +1 -1
  118. package/dist/{orchestrator-VIXTY4E4.js → orchestrator-PZ322OZT.js} +2 -2
  119. package/dist/{pipeline-router-S5PE5U6B.js → pipeline-router-TUMTOJUC.js} +1 -1
  120. package/dist/{reranker-XHONNVBD.js → reranker-VEXZ2QP4.js} +1 -1
  121. package/dist/{review-polling-P2MWEXLR.js → review-polling-PHS23C5B.js} +2 -2
  122. package/dist/runtime/index.js +3 -3
  123. package/dist/{session-events-5CD66R6U.js → session-events-XVWTYBYG.js} +2 -2
  124. package/dist/{session-scope-VMIPAZU7.js → session-scope-PUMBI74U.js} +1 -1
  125. package/dist/{stack-update-ZBK34WTB.js → stack-update-F4CQWMGV.js} +1 -1
  126. package/dist/{task-enforcement-XQL77PZH.js → task-enforcement-WAMGSZ4S.js} +1 -1
  127. package/dist/{task-scope-R3XKBIHL.js → task-scope-AE6U7XGY.js} +1 -1
  128. package/dist/{tasks-crud-DQOG2NPG.js → tasks-crud-6GYPBGJP.js} +1 -1
  129. package/dist/{tasks-notify-4EQYG52H.js → tasks-notify-PMDNAHGC.js} +2 -2
  130. package/dist/{tasks-review-XPFJ4DSJ.js → tasks-review-KRZ3UF5X.js} +1 -1
  131. package/dist/{telemetry-upload-OT5B5HUY.js → telemetry-upload-NJ33742S.js} +1 -1
  132. package/dist/tui/App.js +7 -7
  133. package/dist/{tui-data-UEV2QOR3.js → tui-data-WTWJLP57.js} +1 -1
  134. package/dist/{worker-gate-PRCKA23W.js → worker-gate-MSTTXGOE.js} +1 -1
  135. package/dist/{workflow-engine-7X6LLH3M.js → workflow-engine-BRQHDTN7.js} +2 -2
  136. package/package.json +1 -1
  137. package/release-notes.json +101 -17
  138. /package/dist/{chunk-222SI7QC.js → chunk-5IQBMR2V.js} +0 -0
  139. /package/dist/{chunk-NWBHL5PI.js → chunk-EVBXUKAU.js} +0 -0
  140. /package/dist/{chunk-AQBEG33D.js → chunk-HSWOCMNX.js} +0 -0
  141. /package/dist/{chunk-A4K2ZT6N.js → chunk-MG6JXDAT.js} +0 -0
  142. /package/dist/{chunk-QHVOFVSL.js → chunk-PXK2SQRS.js} +0 -0
  143. /package/dist/{chunk-B7JGEDVE.js → chunk-UED2CGZ3.js} +0 -0
  144. /package/dist/{core-memory-4KAIKQRQ.js → core-memory-TXVA3CVH.js} +0 -0
  145. /package/dist/{exe-key-GXJSTCX2.js → exe-key-LKNPA5SQ.js} +0 -0
  146. /package/dist/{skill-refinement-NRG4WWRW.js → skill-refinement-D3ZYCYS4.js} +0 -0
@@ -1218,7 +1218,7 @@ async function handleHealthCheck(socket, requestId) {
1218
1218
  let pipelineStatus = "unknown";
1219
1219
  if (dbConnected) {
1220
1220
  try {
1221
- await import("../pipeline-router-S5PE5U6B.js");
1221
+ await import("../pipeline-router-TUMTOJUC.js");
1222
1222
  pipelineStatus = "ok";
1223
1223
  } catch {
1224
1224
  pipelineStatus = "unavailable";
@@ -1846,6 +1846,11 @@ async function startMcpHttpServer() {
1846
1846
  transports.delete(sid);
1847
1847
  sessionLastSeen.delete(sid);
1848
1848
  sessionOwnerKeys.delete(sid);
1849
+ for (const [staleSid, alias] of staleSessionRecoveries.entries()) {
1850
+ if (staleSid === sid || alias.recoveredSessionId === sid) {
1851
+ staleSessionRecoveries.delete(staleSid);
1852
+ }
1853
+ }
1849
1854
  const details = sessionDetails.get(sid);
1850
1855
  process.stderr.write(`[exed] MCP session closed: ${sid.slice(0, 8)}\u2026 agent=${details?.agentId ?? "?"} reason=${reason} remaining=${transports.size}
1851
1856
  `);
@@ -1889,6 +1894,23 @@ async function startMcpHttpServer() {
1889
1894
  }
1890
1895
  }
1891
1896
  }
1897
+ }, pruneStaleRecoveryAliases2 = function(now = Date.now()) {
1898
+ for (const [staleSid, alias] of staleSessionRecoveries.entries()) {
1899
+ if (now - alias.createdAt > MCP_RECOVERY_ALIAS_TTL_MS || !transports.has(alias.recoveredSessionId)) {
1900
+ staleSessionRecoveries.delete(staleSid);
1901
+ }
1902
+ }
1903
+ }, getRecoveredMcpSession2 = function(staleSessionId) {
1904
+ pruneStaleRecoveryAliases2();
1905
+ const alias = staleSessionRecoveries.get(staleSessionId);
1906
+ if (!alias) return null;
1907
+ const recoveredTransport = transports.get(alias.recoveredSessionId);
1908
+ if (!recoveredTransport) {
1909
+ staleSessionRecoveries.delete(staleSessionId);
1910
+ return null;
1911
+ }
1912
+ sessionLastSeen.set(alias.recoveredSessionId, Date.now());
1913
+ return { sessionId: alias.recoveredSessionId, transport: recoveredTransport };
1892
1914
  }, sweepStaleMcpSessions2 = function() {
1893
1915
  const now = Date.now();
1894
1916
  if (MCP_SESSION_TTL_MS > 0) {
@@ -1909,7 +1931,7 @@ async function startMcpHttpServer() {
1909
1931
  const rssPressure = mem.rss > RSS_RESTART_BYTES * 0.8;
1910
1932
  const heapPressure = heapLimit > 0 && mem.heapUsed > heapLimit * 0.7;
1911
1933
  if ((rssPressure || heapPressure) && transports.size > 1) {
1912
- const sorted = [...sessionLastSeen.entries()].sort((a, b) => a[1] - b[1]);
1934
+ const sorted = [...sessionLastSeen.entries()].filter(([, lastSeen]) => now - lastSeen >= MCP_PRESSURE_EVICT_MIN_IDLE_MS).sort((a, b) => a[1] - b[1]);
1913
1935
  if (sorted.length > 0) {
1914
1936
  closeMcpSession2(sorted[0][0], rssPressure ? "rss_pressure_evict" : "heap_pressure_evict");
1915
1937
  process.stderr.write(`[exed] MCP pressure eviction: closed 1 session ${sorted[0][0].slice(0, 8)} (${transports.size} remaining; rss=${Math.round(mem.rss / 1024 / 1024)}MB heap=${Math.round(mem.heapUsed / 1024 / 1024)}MB)
@@ -1932,7 +1954,7 @@ async function startMcpHttpServer() {
1932
1954
  }));
1933
1955
  recordMcpHttpEvent({ level: "warn", message, status, ...extra });
1934
1956
  };
1935
- var parseDurationMs = parseDurationMs2, closeMcpSession = closeMcpSession2, mcpSessionOwnerKey = mcpSessionOwnerKey2, evictDuplicateMcpSessions = evictDuplicateMcpSessions2, sweepStaleMcpSessions = sweepStaleMcpSessions2, getJsonRpcId = getJsonRpcId2, sendJsonRpcError = sendJsonRpcError2;
1957
+ var parseDurationMs = parseDurationMs2, closeMcpSession = closeMcpSession2, mcpSessionOwnerKey = mcpSessionOwnerKey2, evictDuplicateMcpSessions = evictDuplicateMcpSessions2, pruneStaleRecoveryAliases = pruneStaleRecoveryAliases2, getRecoveredMcpSession = getRecoveredMcpSession2, sweepStaleMcpSessions = sweepStaleMcpSessions2, getJsonRpcId = getJsonRpcId2, sendJsonRpcError = sendJsonRpcError2;
1936
1958
  process.stderr.write("[exed] MCP HTTP: importing SDK modules...\n");
1937
1959
  const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
1938
1960
  const { StreamableHTTPServerTransport } = await import("@modelcontextprotocol/sdk/server/streamableHttp.js");
@@ -1994,8 +2016,12 @@ async function startMcpHttpServer() {
1994
2016
  const MCP_SESSION_TTL_MS = parseDurationMs2(process.env.EXE_MCP_SESSION_TTL_MS, 0, { allowZero: true });
1995
2017
  const MAX_MCP_SESSIONS = Number(process.env.EXE_MAX_MCP_SESSIONS) || 50;
1996
2018
  const MCP_SESSION_SWEEP_MS = parseDurationMs2(process.env.EXE_MCP_SESSION_SWEEP_MS, 60 * 1e3);
2019
+ const MCP_RECOVERY_ALIAS_TTL_MS = parseDurationMs2(process.env.EXE_MCP_RECOVERY_ALIAS_TTL_MS, 30 * 60 * 1e3);
2020
+ const MCP_PRESSURE_EVICT_MIN_IDLE_MS = parseDurationMs2(process.env.EXE_MCP_PRESSURE_EVICT_MIN_IDLE_MS, 30 * 60 * 1e3);
1997
2021
  const sessionLastSeen = /* @__PURE__ */ new Map();
1998
2022
  const sessionDetails = /* @__PURE__ */ new Map();
2023
+ const staleSessionRecoveries = /* @__PURE__ */ new Map();
2024
+ const pendingStaleSessionRecoveries = /* @__PURE__ */ new Map();
1999
2025
  _mcpSessionDetailsRef = sessionDetails;
2000
2026
  try {
2001
2027
  const sessPath = path3.join(EXE_AI_DIR, "mcp-sessions-persist.json");
@@ -2036,6 +2062,135 @@ async function startMcpHttpServer() {
2036
2062
  const EVICTION_LOG_WINDOW_MS = 6e4;
2037
2063
  const recentEvictions = /* @__PURE__ */ new Map();
2038
2064
  const EVICTION_COOLDOWN_MS = 3e4;
2065
+ async function createRecoveredMcpSession(staleSessionId, details) {
2066
+ const existingRecovered = getRecoveredMcpSession2(staleSessionId);
2067
+ if (existingRecovered) return existingRecovered;
2068
+ const pending = pendingStaleSessionRecoveries.get(staleSessionId);
2069
+ if (pending) {
2070
+ try {
2071
+ const recoveredSessionId2 = await pending;
2072
+ const recoveredTransport = transports.get(recoveredSessionId2);
2073
+ if (recoveredTransport) {
2074
+ sessionLastSeen.set(recoveredSessionId2, Date.now());
2075
+ recordMcpHttpEvent({
2076
+ level: "info",
2077
+ message: "session_recovery_joined",
2078
+ sessionId: recoveredSessionId2,
2079
+ agentId: details.agentId,
2080
+ agentRole: details.agentRole,
2081
+ runtime: details.runtime,
2082
+ activeSessions: transports.size
2083
+ });
2084
+ return { sessionId: recoveredSessionId2, transport: recoveredTransport };
2085
+ }
2086
+ } catch (err) {
2087
+ process.stderr.write(`[exed] MCP session ${staleSessionId.slice(0, 8)}\u2026 pending recovery failed: ${err instanceof Error ? err.message : String(err)}
2088
+ `);
2089
+ }
2090
+ pendingStaleSessionRecoveries.delete(staleSessionId);
2091
+ staleSessionRecoveries.delete(staleSessionId);
2092
+ }
2093
+ let recoveredSessionId = "";
2094
+ let _resolveRecovery;
2095
+ let _rejectRecovery;
2096
+ const recoveryPromise = new Promise((resolve, reject) => {
2097
+ _resolveRecovery = resolve;
2098
+ _rejectRecovery = reject;
2099
+ });
2100
+ pendingStaleSessionRecoveries.set(staleSessionId, recoveryPromise);
2101
+ void (async () => {
2102
+ process.stderr.write(
2103
+ `[exed] MCP session ${staleSessionId.slice(0, 8)}\u2026 stale after daemon restart \u2014 transparently recovering
2104
+ `
2105
+ );
2106
+ recordMcpHttpEvent({
2107
+ level: "info",
2108
+ message: "session_stale_recovering",
2109
+ sessionId: staleSessionId,
2110
+ agentId: details.agentId,
2111
+ agentRole: details.agentRole,
2112
+ runtime: details.runtime,
2113
+ activeSessions: transports.size
2114
+ });
2115
+ try {
2116
+ const { initDatabase, isInitialized } = await import("./database.js");
2117
+ if (!isInitialized()) {
2118
+ const { loadConfig } = await import("./config.js");
2119
+ const { getMasterKey } = await import("./keychain.js");
2120
+ const config = await loadConfig();
2121
+ const masterKey = await getMasterKey();
2122
+ if (masterKey) {
2123
+ await initDatabase({ dbPath: config.dbPath, encryptionKey: masterKey.toString("hex") });
2124
+ process.stderr.write("[exed] DB initialized during stale-session recovery\n");
2125
+ }
2126
+ }
2127
+ } catch (dbErr) {
2128
+ process.stderr.write(`[exed] DB init during stale-session recovery: ${dbErr instanceof Error ? dbErr.message : String(dbErr)}
2129
+ `);
2130
+ }
2131
+ recoveredSessionId = randomUUID();
2132
+ const recoveredTransport = new StreamableHTTPServerTransport({
2133
+ sessionIdGenerator: () => recoveredSessionId,
2134
+ enableJsonResponse: true
2135
+ });
2136
+ recoveredTransport.onclose = () => {
2137
+ const sid = Array.from(transports.entries()).find(([, t]) => t === recoveredTransport)?.[0];
2138
+ if (sid) closeMcpSession2(sid, "session_closed");
2139
+ };
2140
+ const webTransport = recoveredTransport._webStandardTransport;
2141
+ if (!webTransport) {
2142
+ throw new Error("MCP recovery failed: missing underlying web transport");
2143
+ }
2144
+ webTransport.sessionId = recoveredSessionId;
2145
+ webTransport._initialized = true;
2146
+ if (transports.size >= MAX_MCP_SESSIONS) {
2147
+ const oldest = [...sessionLastSeen.entries()].sort((a, b) => a[1] - b[1])[0];
2148
+ if (oldest) {
2149
+ process.stderr.write(`[exed] Session cap (${MAX_MCP_SESSIONS}) reached during recovery \u2014 evicting oldest ${oldest[0].slice(0, 8)}
2150
+ `);
2151
+ closeMcpSession2(oldest[0], "session_cap_eviction");
2152
+ }
2153
+ }
2154
+ const ownerKey = mcpSessionOwnerKey2(details);
2155
+ evictDuplicateMcpSessions2(ownerKey, recoveredSessionId);
2156
+ transports.set(recoveredSessionId, recoveredTransport);
2157
+ sessionLastSeen.set(recoveredSessionId, Date.now());
2158
+ sessionDetails.set(recoveredSessionId, details);
2159
+ sessionOwnerKeys.set(recoveredSessionId, ownerKey);
2160
+ staleSessionRecoveries.set(staleSessionId, { recoveredSessionId, createdAt: Date.now() });
2161
+ recordMcpHttpEvent({
2162
+ level: "info",
2163
+ message: "session_recovered",
2164
+ sessionId: recoveredSessionId,
2165
+ agentId: details.agentId,
2166
+ agentRole: details.agentRole,
2167
+ runtime: details.runtime,
2168
+ activeSessions: transports.size
2169
+ });
2170
+ const sessionMcp = new McpServer({ name: "exe-os", version: "1.3.0" });
2171
+ sessionServers.set(recoveredSessionId, sessionMcp);
2172
+ const gateState = getCachedLicenseGate();
2173
+ const wrapTelemetry = await getWrapTelemetry();
2174
+ const wrappedMcp = wrapTelemetry(sessionMcp);
2175
+ registerAllTools(wrappedMcp, gateState.license, gateState.hasKey);
2176
+ await sessionMcp.connect(recoveredTransport);
2177
+ _resolveRecovery(recoveredSessionId);
2178
+ })().catch((err) => {
2179
+ _rejectRecovery(err);
2180
+ });
2181
+ try {
2182
+ const sid = await recoveryPromise;
2183
+ const recoveredTransport = transports.get(sid);
2184
+ if (!recoveredTransport) throw new Error(`MCP recovery failed: recovered session ${sid} was closed`);
2185
+ return { sessionId: sid, transport: recoveredTransport };
2186
+ } catch (err) {
2187
+ if (recoveredSessionId) closeMcpSession2(recoveredSessionId, "recovery_failed");
2188
+ staleSessionRecoveries.delete(staleSessionId);
2189
+ throw err;
2190
+ } finally {
2191
+ pendingStaleSessionRecoveries.delete(staleSessionId);
2192
+ }
2193
+ }
2039
2194
  const sessionSweepTimer = setInterval(sweepStaleMcpSessions2, MCP_SESSION_SWEEP_MS);
2040
2195
  sessionSweepTimer.unref();
2041
2196
  const httpServer = createHttpServer(async (req, res) => {
@@ -2343,7 +2498,8 @@ async function startMcpHttpServer() {
2343
2498
  enableJsonResponse: true,
2344
2499
  onsessioninitialized: (sid) => {
2345
2500
  const details = { agentId, agentRole, runtime, sessionHint };
2346
- evictDuplicateMcpSessions2(mcpSessionOwnerKey2(details), sid);
2501
+ const ownerKey = mcpSessionOwnerKey2(details);
2502
+ evictDuplicateMcpSessions2(ownerKey, sid);
2347
2503
  if (transports.size >= MAX_MCP_SESSIONS) {
2348
2504
  const now = Date.now();
2349
2505
  for (const [k, ts] of recentEvictions) {
@@ -2352,8 +2508,8 @@ async function startMcpHttpServer() {
2352
2508
  const sorted = [...sessionLastSeen.entries()].sort((a, b) => a[1] - b[1]);
2353
2509
  let evicted = false;
2354
2510
  for (const [candidateSid] of sorted) {
2355
- const ownerKey = sessionOwnerKeys.get(candidateSid) ?? candidateSid;
2356
- if (!recentEvictions.has(ownerKey)) {
2511
+ const ownerKey2 = sessionOwnerKeys.get(candidateSid) ?? candidateSid;
2512
+ if (!recentEvictions.has(ownerKey2)) {
2357
2513
  if (now - _evictionLogWindowStart > EVICTION_LOG_WINDOW_MS) {
2358
2514
  _evictionLogCount = 0;
2359
2515
  _evictionLogWindowStart = now;
@@ -2366,7 +2522,7 @@ async function startMcpHttpServer() {
2366
2522
  `);
2367
2523
  }
2368
2524
  _evictionLogCount++;
2369
- recentEvictions.set(ownerKey, now);
2525
+ recentEvictions.set(ownerKey2, now);
2370
2526
  closeMcpSession2(candidateSid, "session_cap_eviction");
2371
2527
  evicted = true;
2372
2528
  break;
@@ -2382,7 +2538,7 @@ async function startMcpHttpServer() {
2382
2538
  transports.set(sid, transport);
2383
2539
  sessionLastSeen.set(sid, Date.now());
2384
2540
  sessionDetails.set(sid, details);
2385
- sessionOwnerKeys.set(sid, mcpSessionOwnerKey2(details));
2541
+ sessionOwnerKeys.set(sid, ownerKey);
2386
2542
  sessionServers.set(sid, sessionMcp);
2387
2543
  process.stderr.write(`[exed] MCP session opened: ${sid.slice(0, 8)}\u2026 agent=${agentId} role=${agentRole} sessions=${transports.size}
2388
2544
  `);
@@ -2408,81 +2564,10 @@ async function startMcpHttpServer() {
2408
2564
  registerAllTools(wrappedMcp, gateState.license, gateState.hasKey);
2409
2565
  await sessionMcp.connect(transport);
2410
2566
  } else if (sessionId && !transports.has(sessionId) && req.method === "POST") {
2411
- process.stderr.write(
2412
- `[exed] MCP session ${sessionId.slice(0, 8)}\u2026 stale after daemon restart \u2014 transparently recovering
2413
- `
2414
- );
2415
- recordMcpHttpEvent({
2416
- level: "info",
2417
- message: "session_stale_recovering",
2418
- sessionId,
2419
- agentId,
2420
- agentRole,
2421
- runtime,
2422
- activeSessions: transports.size
2423
- });
2424
- try {
2425
- const { initDatabase, isInitialized } = await import("./database.js");
2426
- if (!isInitialized()) {
2427
- const { loadConfig } = await import("./config.js");
2428
- const { getMasterKey } = await import("./keychain.js");
2429
- const config = await loadConfig();
2430
- const masterKey = await getMasterKey();
2431
- if (masterKey) {
2432
- await initDatabase({ dbPath: config.dbPath, encryptionKey: masterKey.toString("hex") });
2433
- process.stderr.write("[exed] DB initialized during stale-session 404\n");
2434
- }
2435
- }
2436
- } catch (dbErr) {
2437
- process.stderr.write(`[exed] DB init during 404 recovery: ${dbErr instanceof Error ? dbErr.message : String(dbErr)}
2438
- `);
2439
- }
2440
- const recoveredSessionId = randomUUID();
2441
- transport = new StreamableHTTPServerTransport({
2442
- sessionIdGenerator: () => recoveredSessionId,
2443
- enableJsonResponse: true
2444
- });
2445
- transport.onclose = () => {
2446
- const sid = Array.from(transports.entries()).find(([, t]) => t === transport)?.[0];
2447
- if (sid) closeMcpSession2(sid, "session_closed");
2448
- };
2449
- const webTransport = transport._webStandardTransport;
2450
- if (!webTransport) {
2451
- throw new Error("MCP recovery failed: missing underlying web transport");
2452
- }
2453
- webTransport.sessionId = recoveredSessionId;
2454
- webTransport._initialized = true;
2455
- req.headers["mcp-session-id"] = recoveredSessionId;
2456
- if (transports.size >= MAX_MCP_SESSIONS) {
2457
- const oldest = [...sessionLastSeen.entries()].sort((a, b) => a[1] - b[1])[0];
2458
- if (oldest) {
2459
- process.stderr.write(`[exed] Session cap (${MAX_MCP_SESSIONS}) reached during recovery \u2014 evicting oldest ${oldest[0].slice(0, 8)}
2460
- `);
2461
- closeMcpSession2(oldest[0], "session_cap_eviction");
2462
- }
2463
- }
2464
2567
  const details = { agentId, agentRole, runtime, sessionHint };
2465
- evictDuplicateMcpSessions2(mcpSessionOwnerKey2(details), recoveredSessionId);
2466
- transports.set(recoveredSessionId, transport);
2467
- sessionLastSeen.set(recoveredSessionId, Date.now());
2468
- sessionDetails.set(recoveredSessionId, details);
2469
- sessionOwnerKeys.set(recoveredSessionId, mcpSessionOwnerKey2(details));
2470
- recordMcpHttpEvent({
2471
- level: "info",
2472
- message: "session_recovered",
2473
- sessionId: recoveredSessionId,
2474
- agentId,
2475
- agentRole,
2476
- runtime,
2477
- activeSessions: transports.size
2478
- });
2479
- const sessionMcp = new McpServer({ name: "exe-os", version: "1.3.0" });
2480
- sessionServers.set(recoveredSessionId, sessionMcp);
2481
- const gateState = getCachedLicenseGate();
2482
- const wrapTelemetry = await getWrapTelemetry();
2483
- const wrappedMcp = wrapTelemetry(sessionMcp);
2484
- registerAllTools(wrappedMcp, gateState.license, gateState.hasKey);
2485
- await sessionMcp.connect(transport);
2568
+ const recovered = await createRecoveredMcpSession(sessionId, details);
2569
+ transport = recovered.transport;
2570
+ req.headers["mcp-session-id"] = recovered.sessionId;
2486
2571
  } else {
2487
2572
  const message = sessionId ? "Bad Request: MCP session is stale or unknown; reconnect MCP client" : "Bad Request: missing MCP session; initialize before tool calls";
2488
2573
  sendJsonRpcError2(res, 400, message, getJsonRpcId2(parsedBody), {
@@ -2518,18 +2603,19 @@ async function startMcpHttpServer() {
2518
2603
  } catch {
2519
2604
  }
2520
2605
  }
2606
+ const activeRequestSessionId = req.headers["mcp-session-id"];
2521
2607
  let effectiveSessionHint = sessionHint || void 0;
2522
- if (effectiveSessionHint && sessionId) {
2523
- const stored = sessionDetails.get(sessionId);
2608
+ if (effectiveSessionHint && activeRequestSessionId) {
2609
+ const stored = sessionDetails.get(activeRequestSessionId);
2524
2610
  if (stored && stored.sessionHint !== effectiveSessionHint) {
2525
2611
  process.stderr.write(
2526
- `[exed] Session hint updated: ${stored.sessionHint} \u2192 ${effectiveSessionHint} (session ${sessionId?.slice(0, 8)})
2612
+ `[exed] Session hint updated: ${stored.sessionHint} \u2192 ${effectiveSessionHint} (session ${activeRequestSessionId?.slice(0, 8)})
2527
2613
  `
2528
2614
  );
2529
2615
  stored.sessionHint = effectiveSessionHint;
2530
2616
  }
2531
- } else if (!effectiveSessionHint && sessionId) {
2532
- const stored = sessionDetails.get(sessionId);
2617
+ } else if (!effectiveSessionHint && activeRequestSessionId) {
2618
+ const stored = sessionDetails.get(activeRequestSessionId);
2533
2619
  if (stored?.sessionHint) {
2534
2620
  effectiveSessionHint = stored.sessionHint;
2535
2621
  }
@@ -2682,13 +2768,13 @@ async function ensureStoreForPolling() {
2682
2768
  }
2683
2769
  }
2684
2770
  async function startReviewPolling() {
2685
- const polling = await import("../review-polling-P2MWEXLR.js");
2771
+ const polling = await import("../review-polling-PHS23C5B.js");
2686
2772
  const state = {
2687
2773
  lastIntercomSent: /* @__PURE__ */ new Map(),
2688
2774
  lastNudgeSent: /* @__PURE__ */ new Map(),
2689
2775
  intervalMs: REVIEW_POLL_INTERVAL_MS
2690
2776
  };
2691
- const { pollReviewNudge, createReviewNudgeRealDeps, loadNudgeState } = await import("../daemon-orchestration-BJ3T5MMF.js");
2777
+ const { pollReviewNudge, createReviewNudgeRealDeps, loadNudgeState } = await import("../daemon-orchestration-FR3LV4G3.js");
2692
2778
  const nudgeState = loadNudgeState();
2693
2779
  const tick = async () => traceDaemonTimer("review_polling", async () => {
2694
2780
  fired("review_polling");
@@ -2731,7 +2817,7 @@ function startSessionTTL() {
2731
2817
  if (!await ensureStoreForPolling()) return;
2732
2818
  try {
2733
2819
  const { getClient } = await import("./database.js");
2734
- const { checkSessionTTL, createSessionTTLRealDeps } = await import("../daemon-orchestration-BJ3T5MMF.js");
2820
+ const { checkSessionTTL, createSessionTTLRealDeps } = await import("../daemon-orchestration-FR3LV4G3.js");
2735
2821
  const deps = createSessionTTLRealDeps(getClient);
2736
2822
  const killed = await checkSessionTTL(deps);
2737
2823
  if (killed.length > 0) acted("session_ttl");
@@ -2779,7 +2865,7 @@ function startIdleKill() {
2779
2865
  const cfg = await getCachedConfig();
2780
2866
  if (!cfg) return;
2781
2867
  const { getClient } = await import("./database.js");
2782
- const { pollIdleKill, createIdleKillRealDeps } = await import("../daemon-orchestration-BJ3T5MMF.js");
2868
+ const { pollIdleKill, createIdleKillRealDeps } = await import("../daemon-orchestration-FR3LV4G3.js");
2783
2869
  const { sessions: liveSessions, paneCache, aliveSessions } = await getSharedOrchestrationCaches();
2784
2870
  const realDeps = createIdleKillRealDeps(
2785
2871
  getClient,
@@ -3031,7 +3117,7 @@ function startSkillRefinement() {
3031
3117
  if (!shouldRunHeavyJob("skill_refinement")) return;
3032
3118
  try {
3033
3119
  if (!await ensureStoreForPolling()) return;
3034
- const { runSkillRefinement } = await import("../skill-refinement-NRG4WWRW.js");
3120
+ const { runSkillRefinement } = await import("../skill-refinement-D3ZYCYS4.js");
3035
3121
  const result = await runSkillRefinement();
3036
3122
  if (result.reviewed > 0) {
3037
3123
  acted("skill_refinement");
@@ -3620,7 +3706,7 @@ function startOrphanReaper() {
3620
3706
  const tick = async () => traceDaemonTimer("orphan_reaper", async () => {
3621
3707
  fired("orphan_reaper");
3622
3708
  try {
3623
- const { reapOrphanedMcpProcesses, createOrphanReaperRealDeps } = await import("../daemon-orchestration-BJ3T5MMF.js");
3709
+ const { reapOrphanedMcpProcesses, createOrphanReaperRealDeps } = await import("../daemon-orchestration-FR3LV4G3.js");
3624
3710
  const deps = createOrphanReaperRealDeps();
3625
3711
  const reaped = await reapOrphanedMcpProcesses(deps);
3626
3712
  if (reaped.length > 0) acted("orphan_reaper");
@@ -3647,7 +3733,7 @@ function startZombieAgentReaper() {
3647
3733
  const tick = async () => traceDaemonTimer("zombie_agent_reaper", async () => {
3648
3734
  fired("zombie_agent_reaper");
3649
3735
  try {
3650
- const { reapZombieAgentProcesses, createZombieAgentReaperRealDeps } = await import("../daemon-orchestration-BJ3T5MMF.js");
3736
+ const { reapZombieAgentProcesses, createZombieAgentReaperRealDeps } = await import("../daemon-orchestration-FR3LV4G3.js");
3651
3737
  const deps = createZombieAgentReaperRealDeps();
3652
3738
  const reaped = reapZombieAgentProcesses(deps);
3653
3739
  if (reaped.length > 0) acted("zombie_agent_reaper");
@@ -3673,7 +3759,7 @@ function startWorktreeReaper() {
3673
3759
  const tick = async () => traceDaemonTimer("worktree_reaper", async () => {
3674
3760
  fired("worktree_reaper");
3675
3761
  try {
3676
- const { reapOrphanedWorktrees, createWorktreeReaperRealDeps } = await import("../daemon-orchestration-BJ3T5MMF.js");
3762
+ const { reapOrphanedWorktrees, createWorktreeReaperRealDeps } = await import("../daemon-orchestration-FR3LV4G3.js");
3677
3763
  const deps = await createWorktreeReaperRealDeps();
3678
3764
  const result = await reapOrphanedWorktrees(deps);
3679
3765
  if (result.pruned.length > 0) {
@@ -3728,7 +3814,7 @@ function startStuckTaskRelease() {
3728
3814
  if (!await ensureStoreForPolling()) return;
3729
3815
  try {
3730
3816
  const { getClient } = await import("./database.js");
3731
- const { releaseStuckTasks, createStuckTaskRealDeps } = await import("../daemon-orchestration-BJ3T5MMF.js");
3817
+ const { releaseStuckTasks, createStuckTaskRealDeps } = await import("../daemon-orchestration-FR3LV4G3.js");
3732
3818
  const deps = createStuckTaskRealDeps(getClient);
3733
3819
  const released = await releaseStuckTasks(deps);
3734
3820
  if (released.length > 0) {
@@ -3832,12 +3918,12 @@ function startTaskEnforcementScanner() {
3832
3918
  const tick = async () => traceDaemonTimer("task_enforcement", async () => {
3833
3919
  fired("task_enforcement");
3834
3920
  try {
3835
- const { runTaskEnforcementTick } = await import("../task-enforcement-XQL77PZH.js");
3921
+ const { runTaskEnforcementTick } = await import("../task-enforcement-WAMGSZ4S.js");
3836
3922
  const { getTransport } = await import("./transport.js");
3837
3923
  const { loadAgentConfig } = await import("./agent-config.js");
3838
3924
  const { getClient } = await import("./database.js");
3839
3925
  const { loadEmployeesSync } = await import("./employees.js");
3840
- const { sessionScopeFilter } = await import("../task-scope-R3XKBIHL.js");
3926
+ const { sessionScopeFilter } = await import("../task-scope-AE6U7XGY.js");
3841
3927
  const transport = getTransport();
3842
3928
  const { sessions: allSessions, paneCache, aliveSessions } = await getSharedOrchestrationCaches();
3843
3929
  await yieldToEventLoop();
@@ -3848,7 +3934,7 @@ function startTaskEnforcementScanner() {
3848
3934
  sendKeysLiteral: transport.sendKeysLiteral?.bind(transport),
3849
3935
  isAlive: (session) => aliveSessions.has(session)
3850
3936
  };
3851
- const { checkAutoWakeGates } = await import("../daemon-orchestration-BJ3T5MMF.js");
3937
+ const { checkAutoWakeGates } = await import("../daemon-orchestration-FR3LV4G3.js");
3852
3938
  const { ensureEmployee } = await import("./tmux-routing.js");
3853
3939
  const { shouldAutoInstance } = await import("./employees.js");
3854
3940
  const projectDir = process.cwd();
@@ -4551,7 +4637,7 @@ try {
4551
4637
  fired("task_group_barriers");
4552
4638
  if (!await ensureStoreForPolling()) return;
4553
4639
  try {
4554
- const { pollTaskGroupBarriers } = await import("../daemon-orchestration-BJ3T5MMF.js");
4640
+ const { pollTaskGroupBarriers } = await import("../daemon-orchestration-FR3LV4G3.js");
4555
4641
  const firedCount = await pollTaskGroupBarriers();
4556
4642
  if (firedCount > 0) {
4557
4643
  acted("task_group_barriers");
@@ -4573,7 +4659,7 @@ try {
4573
4659
  fired("checkpoint_gc");
4574
4660
  if (!await ensureStoreForPolling()) return;
4575
4661
  try {
4576
- const { gcStaleCheckpoints } = await import("../daemon-orchestration-BJ3T5MMF.js");
4662
+ const { gcStaleCheckpoints } = await import("../daemon-orchestration-FR3LV4G3.js");
4577
4663
  const cleared = await gcStaleCheckpoints();
4578
4664
  if (cleared > 0) {
4579
4665
  acted("checkpoint_gc");
@@ -4671,7 +4757,7 @@ try {
4671
4757
  fired("orphan_task_cleanup");
4672
4758
  try {
4673
4759
  if (!await ensureStoreForPolling()) return;
4674
- const { cleanOrphanedTaskFiles, checkTaskFileConsistency } = await import("../tasks-crud-DQOG2NPG.js");
4760
+ const { cleanOrphanedTaskFiles, checkTaskFileConsistency } = await import("../tasks-crud-6GYPBGJP.js");
4675
4761
  const count = await cleanOrphanedTaskFiles();
4676
4762
  if (count > 0) {
4677
4763
  acted("orphan_task_cleanup");
@@ -4709,7 +4795,7 @@ try {
4709
4795
  const TELEMETRY_DAILY_MS = 24 * 60 * 60 * 1e3;
4710
4796
  const telemetryDaily = async () => {
4711
4797
  try {
4712
- const { sendTelemetry, isTelemetryEnabled } = await import("../telemetry-upload-OT5B5HUY.js");
4798
+ const { sendTelemetry, isTelemetryEnabled } = await import("../telemetry-upload-NJ33742S.js");
4713
4799
  if (!isTelemetryEnabled()) return;
4714
4800
  const { upstream } = await sendTelemetry(24);
4715
4801
  if (upstream.startsWith("sent")) {
@@ -5,8 +5,8 @@ import {
5
5
  recentRecords,
6
6
  rrfMerge,
7
7
  rrfMergeMulti
8
- } from "../chunk-2MNLSUC6.js";
9
- import "../chunk-222SI7QC.js";
8
+ } from "../chunk-XGJ5QYBU.js";
9
+ import "../chunk-5IQBMR2V.js";
10
10
  import "../chunk-CHCA3ZM2.js";
11
11
  import "../chunk-IZVKWBIP.js";
12
12
  import "../chunk-Y25OJWOQ.js";
@@ -14,8 +14,8 @@ import {
14
14
  sendMessage,
15
15
  sendTypedMessage,
16
16
  setWsClientSend
17
- } from "../chunk-DGBGIXCC.js";
18
- import "../chunk-F7JLZXHC.js";
17
+ } from "../chunk-6BRFIYYW.js";
18
+ import "../chunk-66AQSKG7.js";
19
19
  import "../chunk-ZKFPHJIJ.js";
20
20
  import "../chunk-5CHYEKMH.js";
21
21
  import "../chunk-4JERP7NT.js";
@@ -3,8 +3,8 @@ import {
3
3
  deleteSchedule,
4
4
  listSchedules,
5
5
  parseHumanCron
6
- } from "../chunk-DR5BGWFR.js";
7
- import "../chunk-222SI7QC.js";
6
+ } from "../chunk-IPWA6HOZ.js";
7
+ import "../chunk-5IQBMR2V.js";
8
8
  import "../chunk-CHCA3ZM2.js";
9
9
  import "../chunk-IZVKWBIP.js";
10
10
  import "../chunk-Y25OJWOQ.js";
package/dist/lib/store.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  updateMemoryStatus,
14
14
  vectorToBlob,
15
15
  writeMemory
16
- } from "../chunk-222SI7QC.js";
16
+ } from "../chunk-5IQBMR2V.js";
17
17
  import "../chunk-CHCA3ZM2.js";
18
18
  import "../chunk-IZVKWBIP.js";
19
19
  import "../chunk-Y25OJWOQ.js";
package/dist/lib/tasks.js CHANGED
@@ -2,8 +2,8 @@ import {
2
2
  createTask,
3
3
  deleteTask,
4
4
  updateTask
5
- } from "../chunk-NBY6R37W.js";
6
- import "../chunk-HOGTZLVU.js";
5
+ } from "../chunk-A7UWQXYD.js";
6
+ import "../chunk-AJAEUU76.js";
7
7
  import {
8
8
  cleanupOrphanedReviews,
9
9
  countNewPendingReviewsSince,
@@ -20,7 +20,7 @@ import {
20
20
  slugify,
21
21
  updateTaskStatus,
22
22
  writeCheckpoint
23
- } from "../chunk-F7JLZXHC.js";
23
+ } from "../chunk-66AQSKG7.js";
24
24
  import "../chunk-ZKFPHJIJ.js";
25
25
  import "../chunk-5CHYEKMH.js";
26
26
  import "../chunk-4JERP7NT.js";
@@ -20,7 +20,7 @@ import {
20
20
  sendIntercom,
21
21
  spawnEmployee,
22
22
  verifyPaneAtCapacity
23
- } from "../chunk-F7JLZXHC.js";
23
+ } from "../chunk-66AQSKG7.js";
24
24
  import "../chunk-ZKFPHJIJ.js";
25
25
  import "../chunk-5CHYEKMH.js";
26
26
  import "../chunk-4JERP7NT.js";