Add a generic AG-UI browser response stream helper - #1198
Conversation
…ion paths Some hosts already own their own chunk type and execution loop but still need canonical AG-UI bootstrap events, SSE framing, and terminal error handling. A generic browser-response stream helper lets those hosts supply an encoder and final-response callback while keeping the package responsible for the public AG-UI stream shape. Constraint: Public AG-UI helpers must stay generic and must not depend on host-specific chunk types Rejected: Tell every host to keep its own AG-UI SSE wrapper | duplicates package-owned browser framing and stream error handling Confidence: high Scope-risk: moderate Reversibility: clean Directive: Prefer generic encoder/callback-based stream helpers over exposing host-specific AG-UI wrappers Tested: deno test --no-check --allow-all src/agent/ag-ui-browser-response-stream.test.ts src/agent/ag-ui-runtime-handler.test.ts; deno check src/agent/ag-ui-browser-response-stream.ts src/agent/ag-ui-runtime-handler.ts src/agent/index.ts Not-tested: full repo verification suite
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f449c28231
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| for await (const chunk of input.execution.agentUIStream) { | ||
| input.onChunk?.(state, chunk); | ||
| for (const event of input.encoder.encode(chunk)) { | ||
| writeEvent(event); | ||
| } |
There was a problem hiding this comment.
Stop execution when SSE stream is no longer writable
When the client disconnects or the controller is closed, writeEvent() returns false, but the chunk loop keeps running and still calls onChunk, encoder.encode, and waitForFinish. Because cancel() only flips streamClosed, this path can continue consuming the upstream agentUIStream after the response is detached, wasting compute and keeping long-running executions alive with no receiver.
Useful? React with 👍 / 👎.
| event: "StateSnapshot", | ||
| payload: { | ||
| snapshot: input.agUiInput.state, | ||
| }, |
There was a problem hiding this comment.
Normalize missing state before emitting StateSnapshot
agUiInput.state is optional, but this payload forwards it directly as snapshot. If state is undefined, JSON serialization drops the property and emits {} for StateSnapshot rather than {"snapshot":{}}, which violates the expected AG-UI event shape in strict validators and causes the initial snapshot event to be rejected.
Useful? React with 👍 / 👎.
Summary
Testing