opencode
opencode copied to clipboard
fix: wire up ACP agent cleanup on connection end to prevent memory leaks
Summary
This PR wires up cleanup to be called when ACP connections end, ensuring memory is properly freed.
Problem
When an ACP connection ends (e.g., IDE closes, client disconnects):
- Event subscription loops (
for awaitinsetupEventSubscriptions) run forever - Session state in
ACPSessionManager.sessionsMap accumulates indefinitely -
sessionAbortControllersnever get cleaned up
This leads to the memory leak crashes reported in #4315 and #5363.
Solution
-
ACP.Agent.dispose() - New method that:
- Aborts all active event subscriptions via AbortController
- Clears all session state from the session manager
- Logs cleanup for debugging
-
AbortController pattern in setupEventSubscriptions() - Each session's event subscription loop now:
- Has an associated AbortController
- Checks
controller.signal.abortedin the loop - Cleans up controller reference in
finallyblock - Handles errors gracefully during abort
-
Cleanup wired to connection lifecycle - In
acp.tscommand:- Agent instance is captured when created
-
agent.dispose()is called after stdin ends - Ensures cleanup happens even on unexpected disconnection
Changes
| File | Change |
|---|---|
src/acp/agent.ts |
Add dispose(), cleanupSession(), AbortController pattern |
src/acp/session.ts |
Add remove(), size(), sessionIds(), clear() methods |
src/cli/cmd/acp.ts |
Capture agent instance and call dispose() on connection end |
test/acp/session.test.ts |
Tests for session manager cleanup methods |
Testing
- 5 tests added for ACPSessionManager
- All tests pass
- Typecheck passes
- Build passes
Related Issues
- #4315 - Memory stays unbounded: ACP session map + compacted tool outputs
- #5363 - opencode eating 70gb of memory?
Related PRs
- #6682 - FileTime session cleanup (by @elithrar)
- #7032 - Bus subscription cleanup (dispose methods for Share, ShareNext, Format, Plugin)