fix(rpc): reclaim shared-host occupancy (idle eviction, session cap, empty-host exit) - #1226
Merged
Merged
Conversation
code-yeongyu
force-pushed
the
fix/mem-shared-host-eviction
branch
from
August 31, 2026 05:41
0f8a1b4 to
9e360e0
Compare
code-yeongyu
force-pushed
the
fix/mem-shared-host-eviction
branch
from
August 31, 2026 07:25
9e360e0 to
76fe034
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The shared multi-session RPC host (
--mode rpc --multi-session) reclaimed nothing without client cooperation:close_session) kept its complete runtime — extension instances, fs.watch handles, transcript — resident forever.open_sessionwas unbounded. Every open owns a full runtime (~340-510 MB RSS each); reconnect churn and multi-window use grow the host without bound. A desktop host measured 1.28 GB in 54 min.runStdioHostparks onreturn new Promise(() => {}); a zero-session host burns its baseline RSS until its pipe dies. The lifecycle supervisor only covers supervised socket hosts with zero connections.Fixes
(4.1) Idle eviction + empty-host exit —
session-registry.ts/session-command-router.ts/multi-session-host.ts:lastCommandAt, refreshed by every routed command (thegetForCommandchokepoint) and by path attach.beginClose->closeMarkedpath (abort -> waitForIdle -> dispose, all attachments drained, path reservation released, pending extension UI requests cancelled,session_closedbroadcast + finalclose_sessionresponse record).isStreaming) or running session-owned bash; their idle clock restarts when the work settles, mirroring the headless completion contract.--listenflavors.nowoption, the repo's established pattern); tests use vitest fake timers, zero sleeps (mindful of the rpc-host-lifecycle: 'does not exit while a turn is active' host never exits (main CI red since ~07:44Z 08-28) #1181 rpc-host-lifecycle flake class).(4.2) Bounded concurrent sessions —
open_sessionbeyond the cap fails with the newtoo_many_sessionserror code. Attach-on-open is exempt (attached: trueadds no runtime), so desktop reconnect/resume flows keep working at cap; an evicted session simply reopens on the nextopen_sessionwith the samesessionPath. Capacity frees automatically once the idle evictor closes an idle session.Defaults (env-overridable)
SENPI_RPC_SESSION_IDLE_EVICTION_MS1800000(30 min)SENPI_RPC_MAX_SESSIONS8SENPI_RPC_HOST_EMPTY_EXIT_MS900000(15 min)Invalid values fall through to defaults (same policy as
SENPI_RPC_HOST_IDLE_EXIT_MS). ExplicitNumber.POSITIVE_INFINITYdisables a bound (tests only).Test evidence (TDD, remote runner
mengmotaMac)0901919cc): 10 failed / 2 passed — idle session still open after the window;onEmptyExitnever called /createHostCoreunexported; thirdopen_sessionresolvedrpc-3instead of rejecting.8fd742289): 10 files / 127 tests passed, exit 0 — newrpc-session-occupancysuite (12 tests) plus existingsession-registry,multi-session*, andhost-lifecyclesuites.Docs
docs/rpc.md: new "Shared host occupancy" section +too_many_sessionsin the stable error-code list.src/modes/rpc/changes.md+CHANGELOG.md[Unreleased] entries.Out of scope by design (other lanes): per-session service sharing, transcript residency,
session-manager/session-resident-store.Summary by cubic
Stops the shared multi-session RPC host from holding abandoned runtimes forever by reclaiming occupancy on its own instead of relying on clients to send
close_session. The host now evicts idle sessions, caps concurrent sessions, and exits when empty; its supervisor reads that clean exit as an intentional stop, not a crash.Behavior changes
sessionPath.open_sessionbeyond 8 concurrently opening/open sessions fails with the new stabletoo_many_sessionserror code; attaching to a live session is exempt.--listenhosts; a connected client counts as occupancy on socket hosts, and the supervisor treats the clean exit as an intentional stop rather than a crash.Configuration
SENPI_RPC_SESSION_IDLE_EVICTION_MS,SENPI_RPC_MAX_SESSIONS, andSENPI_RPC_HOST_EMPTY_EXIT_MSoverride the defaults; invalid values fall back to defaults.Written for commit 76fe034. Summary will update on new commits.