fix(gateway): reap stale MCP processes on session reset regardless of…#93559
Conversation
点击展开证明内容 → 直接复制到 https://github.com//pull/93559Real Behavior ProofBehavior addressedMCP processes are now reaped unconditionally on session reset, even if the agent run doesn't terminate within the grace window. Prevents stale MCP processes from holding file locks (SQLite/LadybirdDB) across session boundaries. Real environment tested
Evidence after fix1. Automated test suite (session reset cleanup): 2. Build verification: 3. Code change (verified diff): + // Always reap MCP processes — even if agent run doesn't terminate
+ await retireSessionMcpRuntime({
+ sessionId: params.sessionId,
+ reason: "gateway-session-cleanup",
+ });
+
const ended = await waitForEmbeddedAgentRunEnd(params.sessionId, 15_000);
Observed result after fix
MCP runtime is retired unconditionally on session reset
Stale MCP processes cannot survive session boundaries
SQLite/LadybirdDB file locks released properly for next session
Build and all 24 session reset tests pass
What was not tested
Active MCP connections during reset timeout (requires live MCP server setup)
Long-running gateway with repeated session resets under load
</details> |
|
Codex review: needs maintainer review before merge. Reviewed July 17, 2026, 5:24 AM ET / 09:24 UTC. Summary PR surface: Source +180, Tests +361. Total +541 across 20 files. Reproducibility: yes. at high source confidence: the timeout path and linked reports establish the leak mechanism, while the real stdio child and SQLite-lock harness exercise the affected resource lifecycle. I did not independently run a failing current-main checkout reproduction in this read-only review. Review metrics: none identified. Stored data model Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the lease-aware lifecycle design after refreshing onto current main and confirming that reset-required retirement survives reuse while active leases remain protected, then let the merge close the canonical process-leak issue. Do we have a high-confidence way to reproduce the issue? Yes at high source confidence: the timeout path and linked reports establish the leak mechanism, while the real stdio child and SQLite-lock harness exercise the affected resource lifecycle. I did not independently run a failing current-main checkout reproduction in this read-only review. Is this the best way to solve the issue? Yes. Required deferred retirement is narrower and safer than killing the MCP transport immediately, because it deterministically reaps stale children while preserving in-flight work until the final aggregate lease releases. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 45f36640dc3e. Label changesLabel justifications:
Evidence reviewedPR surface: Source +180, Tests +361. Total +541 across 20 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (13 earlier review cycles; latest 8 shown)
|
|
This assigned pull request has been automatically marked as stale after being open for 27 days. |
c2c8788 to
50e050a
Compare
|
Reworked the patch on current @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
050f9e3 to
757207b
Compare
d312a8b to
87c8e92
Compare
87c8e92 to
28fa82f
Compare
Mark session MCP runtimes for retirement before reset/delete waits, preserve active work through leases, and complete deferred cleanup when the final materialized run lease releases.\n\nCo-Authored-By: Codex <[email protected]>
Co-Authored-By: Codex <[email protected]>
28fa82f to
6aed7bf
Compare
|
Maintainer proof complete on exact head
Review artifacts validate with |
|
Merged via squash.
|
openclaw#93559) * fix(gateway): defer MCP cleanup until active leases release Mark session MCP runtimes for retirement before reset/delete waits, preserve active work through leases, and complete deferred cleanup when the final materialized run lease releases.\n\nCo-Authored-By: Codex <[email protected]> * test(mcp): prove deferred child process retirement Co-Authored-By: Codex <[email protected]> * test(mcp): prove deferred database lock release * fix(gateway): bind MCP retirement to timed-out runs * style(mcp): avoid lifecycle callback shadowing * test(mcp): align nullable wait mocks * fix(gateway): follow replacement runs during cleanup * fix(gateway): close MCP watcher overlap races * test(agents): prove reply-backed run draining * test(gateway): reuse MCP retirement parameter type * fix(gateway): type MCP watcher identity safely * perf(gateway): lazy-load session cleanup runtime * perf(gateway): lazy-load MCP cleanup runtime * perf(agents): lazy-load MCP retirement manager --------- Co-authored-by: Codex <[email protected]> Co-authored-by: Peter Steinberger <[email protected]>
Closes #92569
What Problem This Solves
Resetting or deleting a session could leave its MCP child processes alive when the active agent run exceeded the 15-second cleanup grace period. Those stale children could retain SQLite/LadybugDB write locks or ports until idle-TTL cleanup.
Why This Is the Best Fix
Gateway lifecycle cleanup now requests required, lease-aware MCP retirement before aborting or waiting. The MCP manager owns the distinction between ordinary deferred retirement, which later reuse may cancel, and reset/delete retirement, which survives late runtime creation or reuse. Embedded and reply-backed run waiters follow replacements until the session becomes idle. Materialized run/view/request lease release completes deferred disposal.
The implementation also closes two overlap windows: a replacement registering after a waiter settles, and a new reset beginning while an earlier runtime disposal is still pending. Immediate gateway disposal was rejected because it can close transports under in-flight MCP work; TTL-only cleanup is nondeterministic. Heavy embedded-agent and MCP cleanup modules now load only when reset/delete cleanup runs, so read-only status startup does not pay for those lifecycle dependencies.
User Impact
Session reset/delete no longer strands old MCP processes on the timeout path. Active MCP work remains alive until its final lease releases, then the old runtime and child process retire deterministically.
Evidence
6aed7bf40780c659740b17863a6fc9f57813c553node scripts/run-vitest.mjs src/agents/agent-bundle-mcp-runtime.test.ts src/agents/subagent-announce.format.e2e.test.ts src/agents/embedded-agent-runner/runs.test.ts src/auto-reply/reply/reply-run-registry.test.ts src/gateway/server.sessions.reset-cleanup.test.ts src/gateway/server.sessions.delete-lifecycle.test.tsoxfmt --checkandoxlint --deny-warnings;git diff --checkpasses.BEGIN IMMEDIATEwrite lock during deferred retirement; final lease release terminates the child and lets the parent acquire the lock.Regression coverage includes reset/delete success and timeout, retries, embedded and reply-backed runs, same-session replacements, waiter-settlement races, overlapping disposal, late runtime creation/reuse, active lease preservation, real child exit, and real SQLite lock release.
Risk and Compatibility
Co-authored-by: 詹幸心0668001037 [email protected]