@@ -71,12 +71,15 @@ export function formatSSEErrorEvent(error: unknown): string {
7171 * Without this cap a malicious or broken server can stream bytes that never
7272 * form a complete line — or `data:` lines whose blank-line terminator never
7373 * arrives — growing an in-memory buffer without limit until the client
74- * process exhausts memory (CWE-400, uncontrolled resource consumption). The
75- * default is deliberately generous so that legitimate large JSON events
76- * (e.g. base64 file parts) still pass; callers handling larger payloads can
77- * raise it via the `maxEventSizeBytes` argument.
74+ * process exhausts memory (CWE-400, uncontrolled resource consumption).
75+ *
76+ * Realistic A2A events (Message/Task JSON) are KB-scale, and large files
77+ * should be referenced via `FileWithUri` parts rather than inlined, so the
78+ * 4 MiB default (matching gRPC's default max message size) leaves ample
79+ * headroom. Callers that must inline larger payloads can raise it via the
80+ * `maxEventSizeBytes` argument.
7881 */
79- export const DEFAULT_MAX_SSE_EVENT_SIZE_BYTES = 20 * 1024 * 1024 ; // 20 MiB
82+ export const DEFAULT_MAX_SSE_EVENT_SIZE_BYTES = 4 * 1024 * 1024 ; // 4 MiB
8083
8184/**
8285 * Parses an SSE stream from a `Response`, yielding events as they arrive.
@@ -107,9 +110,7 @@ export async function* parseSseStream(
107110
108111 while ( ( lineEndIndex = buffer . indexOf ( '\n' ) ) >= 0 ) {
109112 if ( lineEndIndex > maxEventSizeBytes ) {
110- throw new Error (
111- `SSE line exceeded the maximum allowed size of ${ maxEventSizeBytes } bytes.`
112- ) ;
113+ throw sseSizeError ( 'SSE line' , maxEventSizeBytes ) ;
113114 }
114115 // Per the SSE spec lines may end with `\r\n`, `\r`, or `\n`. We
115116 // strip a trailing `\r` explicitly rather than calling `.trim()`,
@@ -137,17 +138,15 @@ export async function* parseSseStream(
137138 const fieldValue = stripOptionalLeadingSpace ( line . substring ( 'data:' . length ) ) ;
138139 eventData = eventData === '' ? fieldValue : `${ eventData } \n${ fieldValue } ` ;
139140 if ( eventData . length > maxEventSizeBytes ) {
140- throw new Error (
141- `SSE event data exceeded the maximum allowed size of ${ maxEventSizeBytes } bytes.`
142- ) ;
141+ throw sseSizeError ( 'SSE event data' , maxEventSizeBytes ) ;
143142 }
144143 }
145144 }
146145
147146 // Same cap for a line that never terminates: the loop above never runs,
148147 // so the residual buffer would otherwise grow without bound.
149148 if ( buffer . length > maxEventSizeBytes ) {
150- throw new Error ( ` SSE line exceeded the maximum allowed size of ${ maxEventSizeBytes } bytes.` ) ;
149+ throw sseSizeError ( ' SSE line' , maxEventSizeBytes ) ;
151150 }
152151 }
153152
@@ -157,6 +156,13 @@ export async function* parseSseStream(
157156 }
158157}
159158
159+ function sseSizeError ( what : string , maxEventSizeBytes : number ) : Error {
160+ return new Error (
161+ `${ what } exceeded the maximum allowed size of ${ maxEventSizeBytes } bytes. ` +
162+ `Pass maxEventSizeBytes to raise the limit, or prefer FileWithUri parts for large payloads.`
163+ ) ;
164+ }
165+
160166/**
161167 * Per the SSE spec, the optional single leading space after the field-name
162168 * colon is consumed by the parser; embedded and trailing whitespace are
0 commit comments