fix(agents): do not refresh lastUsedAt on MCP lease release#91124
Conversation
|
Codex review: passed. Reviewed June 7, 2026, 10:06 PM ET / 02:06 UTC. Summary PR surface: Source 0, Tests +74. Total +74 across 2 files. Reproducibility: yes. from source inspection: current main refreshes Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the focused lease-release fix after required CI/automerge gates, keeping Do we have a high-confidence way to reproduce the issue? Yes from source inspection: current main refreshes Is this the best way to solve the issue? Yes; this is the narrowest maintainable fix because release should only end the lease, while materialization and MCP tool/resource/prompt execution already call AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against 75c1790b504c. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source 0, Tests +74. Total +74 across 2 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
|
244d164 to
3535e5c
Compare
|
@clawsweeper automerge |
|
🦞✅ Source: What merged:
Automerge notes:
The automerge loop is complete. Automerge progress:
|
|
🦞✅ Source: Why human review is needed: What the maintainer can do as a next step: I added |
|
@clawsweeper approve |
|
🦞👀 Command router queued. I will update this comment with the next step. |
3535e5c to
c914478
Compare
…#91124) Summary: - The PR removes release-time `lastUsedAt` refresh from session MCP runtime lease cleanup and adds regression tests for idle eviction after a lease expires while active. - PR surface: Source 0, Tests +74. Total +74 across 2 files. - Reproducibility: yes. from source inspection: current main refreshes `lastUsedAt` in the release callback, a ... timestamp after active leases drop to zero. I did not execute the focused Vitest in this read-only review. Automerge notes: - PR branch already contained follow-up commit before automerge: fix(agents): do not refresh lastUsedAt on MCP lease release Validation: - ClawSweeper review passed for head c914478. - Required merge gates passed before the squash merge. Prepared head SHA: c914478 Review: openclaw#91124 (comment) Co-authored-by: openperf <16864032@qq.com> Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com> Approved-by: takhoffman Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary
sweepIdleRuntimesnever reclaims any of them.acquireLease()increateSessionMcpRuntimereturns a release callback that unconditionally setslastUsedAt = Date.now(). The idle sweep evicts a runtime only whennowMs - runtime.lastUsedAt >= idleTtlMsandactiveLeases === 0. When a cron agent holds a lease for longer thansessionIdleTtlMs, releasing that lease resets the clock to the moment of release; the very next sweep finds the runtime within TTL again and keeps it. Because cron agents re-lease before the next full TTL cycle elapses, the sweep never fires. The accumulated runtimes — including their MCP child processes and SSE/HTTP handles — are never disposed.lastUsedAt = Date.now()from the release callback.markUsed()already stamps the clock at the correct points: insidematerializeBundleMcpToolsForRunwhen tools are prepared and before every tool/resource/prompt execution. Releasing a lease is not "using" the runtime and must not extend the TTL window.src/agents/agent-bundle-mcp-runtime.ts— removelastUsedAt = Date.now()fromacquireLease()release callback; add a one-line comment explaining the invariant.src/agents/agent-bundle-mcp-runtime.test.ts— update the existing idle-eviction test mock to match corrected production behavior (remove mirroredlastUsedAt = nowfrom the mock release callback); add new regression testevicts immediately after release when TTL already elapsed during active lease.markUsed()call sites inagent-bundle-mcp-materialize.tsare unchanged; the TTL window resets correctly at materialization and tool execution.sweepIdleRuntimes()logic, active-lease guard, and dispose path are unchanged.docs/reference/config).extensions/*api/runtime-api, registry/loader).Reproduction
mcp.sessionIdleTtlMs(default 600 s).lastUsedAtto now; the nextsweepIdleRuntimescall findsnowMs - lastUsedAt < idleTtlMsand skips the runtime; process accumulation is unbounded.lastUsedAtat its last genuine use time; a runtime held past its TTL is evicted on the first sweep after the final lease is released, without requiring any further clock advance.Real behavior proof
Behavior addressed (#91075): MCP server processes no longer accumulate after the patch; a runtime whose TTL elapsed while a lease was active is evicted on the next
sweepIdleRuntimescall after the lease is released.Real environment tested (Linux, Node 22 — Vitest against the production
createSessionMcpRuntime,acquireLease, andsweepIdleRuntimesimplementations inagent-bundle-mcp-runtime.ts): tests drive the real manager and runtime factory with a controlled clock; no mocking of the idle-sweep or dispose logic.Exact steps or command run after this patch:
node scripts/run-vitest.mjs src/agents/agent-bundle-mcp-runtime.test.ts -t "evicts idle runtimes|evicts immediately after release"— 4 passed;pnpm format:fixon changed files;node scripts/run-oxlint.mjson changed files — clean.Evidence after fix: Vitest: 4 passed, 0 failed; autoreview: no accepted/actionable findings, correctness 0.97.
Observed result after fix: in the new regression test, the clock does not advance after the lease is released — yet
sweepIdleRuntimesreturns 1 anddisposeis called, confirming the runtime is evicted immediately rather than after an additional full TTL cycle.What was not tested: live gateway cron scenario with real MCP child processes; the sweep logic and dispose path are covered by the test harness against production functions.
Repro confirmation: the new regression test (
evicts immediately after release when TTL already elapsed during active lease) fails on the unfixed tree because the mock release callback refresheslastUsedAtand the sweep returns 0 instead of 1; it passes with the fix, directly covering the reported accumulation path.Risk / Mitigation
markUsed()is the only documented mechanism and is already invoked atacquireLeasetime insidematerializeBundleMcpToolsForRun.activeLeasesandlastUsedAtare out of sync. Mitigation:sweepIdleRuntimesstill skips any runtime withactiveLeases > 0; the TTL is only evaluated after all leases are released.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
Fixes #91075