mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
855446e1ab
## Summary Pressing **Stop** while an assistant message is streaming (CopilotRuntime + `HttpAgent` proxy) crashed the chat with: ``` Cannot send event type 'TEXT_MESSAGE_END': The run has already errored with 'RUN_ERROR'. No further events can be sent. ``` Root cause: `finalizeRunEvents` appended a trailing `TEXT_MESSAGE_END` **after** the `RUN_ERROR` that the aborted agent had already emitted. Fixes #5812. ## Root cause When the upstream agent (e.g. pydantic-ai's `AGUIAdapter`) is aborted mid-stream it emits a live `RUN_ERROR` while a text message is still open — it does **not** close the message first. All runners (`in-memory`, `intelligence`, `sqlite`) stream `finalizeRunEvents`' output *after* everything the agent already emitted, so the appended closer landed past the terminal: | | outgoing event order | |---|---| | **Before** | `… TEXT_MESSAGE_CONTENT → RUN_ERROR → TEXT_MESSAGE_END` ❌ verifier throws | | **After** | `… TEXT_MESSAGE_CONTENT → RUN_ERROR` ✅ terminal closes the message client-side | Per the AG-UI invariant: at most one terminal event per run, and no sub-events after it. I confirmed against the real `@ag-ui/client` `verifyEvents` (the verifier the browser runs) that a terminal arriving with a message still open is valid — the terminal implicitly closes it. ## Fix `finalizeRunEvents` (in `@copilotkit/shared`) now returns early and appends **nothing** when the stream already contains a terminal event (`RUN_FINISHED` or `RUN_ERROR`). The abrupt-end path (no terminal → close open streams + synthesize a terminal, in the correct order) is unchanged. No API/signature change; the in-memory, intelligence, and sqlite runners all inherit the fix. ## Testing RED→GREEN verified — each new/updated assertion was confirmed to fail against the pre-fix code: - **`finalize-events.test.ts`** — terminal-present appends nothing (parametrized over `RUN_FINISHED` and `RUN_ERROR`) + a named #5812 case. - **`in-memory-runner.test.ts`** — end-to-end mid-stream-stop regression: a fake `HttpAgent`-style agent is stopped between `TEXT_MESSAGE_START` and `TEXT_MESSAGE_END`; asserts no events follow `RUN_ERROR` **and** that the collected stream passes `verifyEvents` (before the fix this threw the exact browser error). - **`intelligence-runner.test.ts`** — corrected a pre-existing assertion that had encoded the buggy post-terminal `TEXT_MESSAGE_END`. Green: full `@copilotkit/runtime` suite, `@copilotkit/sqlite-runner`, `@copilotkit/shared`, `check-types`, `oxlint` (0 errors), and build. ## Reviewer notes - The behavior change is a single early-return in `finalize-events.ts`; the `terminalEventMissing` guards simplify away because they're only reachable when no terminal exists. - Diff is +204/−55 across 4 files, the bulk of it tests.
CopilotKit - Runtime
✨ Why CopilotKit?
- Minutes to integrate - Get started quickly with our CLI
- Framework agnostic - Works with React, Next.js, AGUI and more
- Production-ready UI - Use customizable components or build with headless UI
- Built-in security - Prompt injection protection
- Open source - Full transparency and community-driven
🧑💻 Real life use cases
Deploy deeply-integrated AI assistants & agents that work alongside your users inside your applications.
🏆 Featured Examples
Documentation
To get started with CopilotKit, please check out the documentation.
Analytics & Privacy
CopilotKit uses Scarf for anonymous usage analytics to help improve the product. Scarf handles all privacy compliance and does not store raw IP addresses. This helps us understand how CopilotKit is being used and prioritize improvements.
Opting Out
To disable analytics, set the environment variable:
export COPILOTKIT_TELEMETRY_DISABLED=true
Or use the DO_NOT_TRACK standard:
export DO_NOT_TRACK=1