fix: bound per-connection memory in AcpServer - #261
Merged
Merged
Conversation
Session IDs a client sends no longer create lasting per-connection state, and agent output a client has not read is held to a fixed budget by pausing the agent rather than queuing without limit. - Create session streams on demand, drop them once empty and unreceived, and close a connection that buffers output for more than maxBufferedSessionStreams sessions. - Reject session IDs and request IDs longer than maxIdLength. - Flow-control agent output to maxBufferedBytes per connection: the router pauses, held-back client requests wait, and SSE bodies pull on demand. - Close connections whose client reads nothing for maxOutputStallMs, and bound WebSocket frames waiting to be handled. - End streams, held requests and sockets when a connection shuts down.
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.
Summary
Bound the memory
AcpServerkeeps for each connection. Session IDs a client sends no longer create lasting per-connection state, and agent output a client has not read is held to a fixed budget by pausing the agent instead of queuing without limit. Follows #259, which bounded individual incoming messages.Changes
maxBufferedSessionStreams(default 1024) sessions that nobody is receiving closes. Client notifications create no session state, and routes are recorded only for valid requests, which the agent always answers.maxIdLength(default 1024) are rejected with HTTP 400 or WebSocket close code 1008. A connection whose agent issues a longer session ID closes.maxBufferedBytes(default 64 MiB) of output its client has not read: the JSON text queued across its HTTP streams, or the socket'sbufferedAmounton WebSocket. At the limit the router stops taking output, so an agent that awaits its sends pauses, and client requests (not notifications) wait before reaching the agent. SSE bodies take a message only when their reader asks for one.maxBufferedByteswhose client reads none of it formaxOutputStallMs(default 60 s) closes. On WebSocket, any bytes the socket sends count as reading.maxBufferedBytes, at their length plus 1 KiB each, and the socket closes past the limit.closedresolved are still delivered.shift(); the Node adapter destroys a response whose body fails after headers were sent, and cancels the body when the response fails; the example server capswsmessages at 16 MiB.New exports from
@agentclientprotocol/sdk/experimental/server: theConnectionLimitstype andDEFAULT_MAX_BUFFERED_BYTES,DEFAULT_MAX_OUTPUT_STALL_MS,DEFAULT_MAX_BUFFERED_SESSION_STREAMSandDEFAULT_MAX_ID_LENGTH.AcpServerOptionsaccepts the four limits, andWebSocketLikegains an optionalbufferedAmount.Compatibility and scope
new AcpServer()now throwsRangeErrorfor a limit that is not a positive safe integer, or amaxOutputStallMsabove 2^31 − 1.experimental:maxOutputStallMs, and then gets 404. Held requests keep their bodies, so the HTTP server or proxy should limit concurrent requests per client.session/cancelreach the agent before the prompt it cancels. The SDK's HTTP client sends POSTs one at a time; WebSocket keeps order.maxBufferedBytescounts UTF-16 code units for HTTP output and waiting WebSocket frames, and bytes for the WebSocket send buffer. WebSocket flow control needs a socket that reportsbufferedAmount; it was tested with Node'swsonly.Validation
CI=1 npm run checkpassed: schema generation check, lint, formatting, spelling, build, 958 tests, and TypeDoc verification.wsclients: