Commit d24bc7e
feat(agent-stream): thinking and tool streaming (#5671)
* feat(agent-stream): add agent-events thinking/tool streaming for chat and canvas
Ship the agent-events-v1 protocol with provider tool loops, dual-gated chat thinking, DeepSeek/Groq/OpenAI reasoning wiring, and ChatGPT-like thinking chrome.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(agent-stream): clear stuck streaming UI and format db snapshot
Biome was failing CI on migrations/meta/0261_snapshot.json. Also settle
assistant streaming/tool flags when SSE ends without a terminal frame,
without clobbering Stop's finalized content.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(agent-stream): satisfy biome format and import order
Auto-format the sim package for CI lint:check, and repair the Anthropic
streaming tool-loop payload after an unsafe delete-to-undefined rewrite.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(agent-stream): keep drained answer on abort and update migration journal test
Treat AbortError from reader.cancel as a cancelled pump result so soft-complete
retains answerText. Point the workspace storage migration journal assertion at
0261_chat_include_thinking.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(chat): keep Stop notice when server emits cancel error
Ignore terminal SSE error frames after the user aborts so
"Client cancelled request" cannot overwrite "Response stopped by user".
Co-authored-by: Cursor <cursoragent@cursor.com>
* improvement(chat): ChatGPT-style thinking shimmer and stick-to-bottom scroll
Add left-to-right shimmer on live thinking label/body, keep scroll working by
shimmering an inner node, and follow the answer only while near the bottom.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(agent-stream): stop pump on client disconnect; soft-complete agents only
Abort the agent stream pump when the projected HTTP body is cancelled so
provider work does not continue after disconnect. Limit AbortError soft-success
to Agent blocks so Function/HTTP cancels still fail in logs.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(agent-stream): persist includeThinking across pause snapshots
Paused chat runs with Include thinking enabled were dropping the flag when
serializing the pause snapshot, so resume always rebuilt streams without
thinking/tool SSE frames.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(agent-stream): keep drained answer text when stream times out
Persist pump answerText onto the streaming execution before throwing on
timeout, and carry that partial content into the failed block output so
logs match what the client already saw.
Co-authored-by: Cursor <cursoragent@cursor.com>
* improvement(chat): auto-collapse tools chrome when tool streaming ends
Match thinking UX: open while tools run, collapse when finished, and keep
the panel open only if the user manually reopens it.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(agent-stream): settle canvas stream chrome on failure paths
Clear agentStreamActive and settle running tool chips when blocks error,
timeouts cancel runs, or execution ends without stream:done so the output
panel does not stay on live Thinking/Using tools chrome.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(lint): organize imports in terminal console store
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(agent-stream): mark open tools cancelled on HITL pause
Pause can interrupt a tool loop without tool end events; settling those
chips as success incorrectly showed unfinished tools as complete.
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore(db): drop branch-local 0261 migration ahead of staging merge
* chore(db): regenerate include_thinking migration as 0266 post staging merge
* fix(providers): resolve type errors in streaming tool loop call sites
* fix(agent-stream): gate agent events opt-in and correct provider loop behavior
- streamToolCalls and provider thinking requests now require run-level
agentEvents opt-in (canvas on, chat dual-gated, API off) so existing
runs keep pre-agent-events behavior exactly
- OpenAI reasoning summaries opt-in + strip-and-retry on unverified-org 400
- streaming loops run tool postProcess again (firecrawl/exa async results)
- bedrock live loop falls back to silent path for responseFormat
- deepseek: reasoning_content pass-back unconditional, 'none' sends disabled
- groq: x_groq.usage fallback, reasoning params gated, qwen none disables
- gemini: functionCall parts echoed verbatim, local ids only for events
- truncated turns (max_tokens/length) no longer execute partial tool calls
- MAX_TOOL_ITERATIONS exit flushes last turn text as final answer
- iterations reports actual model calls; shared loop plumbing extracted
* refactor(agent-stream): consolidate protocol, dedupe client/server plumbing, hygiene
- canonical ChatStreamFrame union + type guards consumed by server emitters
and the chat client; stream_error restored to legacy log-only handling
- strip thinking/tool args from providerTiming on public final envelopes
- shared tool-chip lifecycle module for chat, canvas, and console store
- shared sink-to-execution-events forwarder replaces the copy-pasted
adapter in the execute route and HITL manager; LIVE_ONLY event set shared
- stream:thinking payload field renamed data->text; canvas thinking batched
- abort reasons carried as AbortError DOMExceptions so raw fetch consumers
classify correctly; thinking cap renamed to chars and scope-documented
- kimi wired for agent events like the other compat providers
- deleted dead exports/step-N comments; fixtures match real wire shapes;
loop tests use explicit mocks instead of importOriginal
* test(agent-stream): cover the dual-gated execution path and typed abort reasons
- chat route tests assert agentEvents reaches executeWorkflow only when
policy and protocol header agree
- execution-limits tests assert AbortError-typed reasons
- executor metadata type carries agentEvents
* fix(deploy-modal): align include-thinking spacing with the modal's 6.5px rhythm
* docs(agent-stream): autogenerate per-model thinking/tool stream support on the Agent block page
- capabilities.thinking.streamed ('full' | 'summary' | 'none') on models.ts,
explicit for the Anthropic family where visibility varies per generation;
getThinkingStreamVisibility exposes the derivation for docs and UI alike
- scripts/sync-agent-stream-docs.ts regenerates the support tables between
markers in workflows/blocks/agent.mdx from the model registry and
STREAMING_TOOL_CALL_PROVIDERS; --check fails on drift or missing metadata
- wired agent-stream-docs:check into CI next to the other sync gates
* feat(anthropic): request summarized thinking display for omitted-default Claude models
The newest Claude generations (Fable 5, Sonnet 5, Opus 4.8/4.7) default
thinking.display to omitted — empty thinking blocks, no deltas. On
agent-events runs Sim now opts back in with display: 'summarized', driven
by the registry's streamed metadata; legacy runs keep the exact
pre-agent-events request shape. Registry, generated docs, and the family
capability table updated accordingly.
* docs(skills): cover thinking.streamed and agent-stream docs sync in model skills
* chore(deps): upgrade @anthropic-ai/sdk to 0.114.0 and adopt official types
- adaptive thinking, display, and output_config are now SDK-typed; the only
remaining custom payload field is output_format (beta-header structured
outputs, which the SDK models as output_config.format instead)
- anthropic stream events narrow on the SDK's discriminated unions instead
of anonymous casts; compat deltas type content/tool_calls from the OpenAI
SDK with vendor reasoning fields as an explicit optional extension
- @sim/auth exposes an explicit VerifyAuth contract so its declarations no
longer reference better-auth's nested zod instance (TS2883 under fresh
install layouts); realtime consumer aligned
- docs app zod pinned to the repo's exact 4.3.6 so ai SDK types bind the
same zod instance (docs type-check was latently broken)
- knowledge embedding tests made hermetic against local .env keys and
hosted rotation fallback
* refactor(providers): replace legacy as-any stream casts with annotated typed casts
* refactor(providers): finish provider audit — remove dead byte-stream helper, annotate remaining legacy casts
Audit of all 26 providers for the agent-events feature confirmed every
streaming execution declares agent-events-v1 and every adapter emits
AgentStreamEvent objects. Cleanup from the audit: the unconsumed legacy
createOpenAICompatibleStream byte helper is deleted, and the remaining
streamResponse-as-any casts (xai, nvidia, kimi, meta, zai, sakana) are
annotated typed casts matching the groq/deepseek fix.
* feat(streaming): stream answer text live during tool loops via turn_end protocol
The live tool loops buffered all answer text per model turn (classification
of intermediate vs final is only known at turn end), so gated surfaces saw
thinking stream, then dead air with the thinking chrome stuck open, then the
whole answer at once.
Loops now emit text deltas live as `turn: 'pending'` plus a `turn_end`
event per turn. The pump buffers pending text and projects it to the byte
path (answerText/logs/memory/legacy clients) only on a final turn_end, so
all settled semantics are unchanged. Gated surfaces render the pending text
as it streams and reconcile with a reset when a turn resolves to tools:
- public chat: live `chunk` frames from the sink + dual-gated `chunk_reset`;
byte-path frame emission is suppressed to avoid duplicates (kept for
response-format transformed streams via clientStreamTransformed)
- canvas: forwarder emits live `stream:chunk` + `stream:chunk_reset`; the
execute route and HITL resume readers stop re-emitting byte chunks; panel
chat tracks per-block segments and replaces content on flush
- chat client: per-block text segments, chunk_reset handling, and thinking
chrome now settles on tool start as well as first answer chunk
* fix(streaming): address validated review findings across provider gating and reset reconciliation
Three-reviewer pass over the branch, findings validated against staging:
- agent-handler forwards agentEvents to executeProviderRequest — the flag was
computed but dropped in the field-by-field copy, so provider-side thinking
requests (OpenAI summaries, Gemini includeThoughts, Anthropic summarized
display) never activated on opted-in runs
- openai: restore summary:'auto' alongside explicit reasoning effort — staging
always paired them; gating summary purely on agentEvents changed legacy
payloads
- gemini: Gemini 2 + tools + responseFormat falls back to the silent path;
the live loop never applied the deferred responseSchema for AUTO tools
- openai-compat loop: malformed tool-argument JSON fails the call instead of
executing with defaulted {} args (staging parsed inside the execution try)
- openai-compat parser: a vendor id arriving after a synthesized start no
longer renames the call (start/end ids stayed consistent)
- stream-pump: abort closes the byte projection so a drain blocked on
backpressure cannot deadlock teardown
- chunk_reset removes the block from the client text order (deployed chat +
panel chat) so a reset block re-registers at arrival position — fixes
separator/order corruption when parallel blocks stream around a reset
- resume route echoes the negotiated X-Sim-Stream-Protocol response header
(parity with the chat route); docs: [DONE] wire shape + final-vs-error
terminal semantics corrected
* chore(deps): exempt pinned @anthropic-ai/sdk 0.114.0 from the release-age gate
CI's bun install --frozen-lockfile blocks 0.114.0 (published 2026-07-23,
younger than the 7-day supply-chain gate). The pin is exact and was vetted
for the agent-events streaming work; following the existing bunfig pattern,
the exclusion ages out on 2026-07-30 and should be dropped then.
* chore(providers): fix double-cast-allowed annotation placement for the strict boundary audit
The audit only recognizes the annotation on the line directly above the cast;
two annotations had drifted behind intervening code lines (groq stream params,
deepseek loop messages) and the OpenAI reasoning-summary widening cast was
never annotated. No behavior change.
* fix(chat): settle straggler tool chips as error when final reports failure
A failed run can still terminate with a `final` frame carrying success: false;
running chips previously settled green regardless of the outcome.
* fix(canvas): wire agent stream chrome into run-from-block
Run-from-block executions emit the same live stream:thinking/stream:tool
events as full runs but registered none of the handlers, so the terminal
never showed thinking or tool chips on that path. The per-run chrome
(batched thinking writes + tool chip lifecycle + settlement on stream done,
block error, and every terminal execution state) is extracted into a shared
createAgentStreamChrome factory consumed by both paths.
---------
Co-authored-by: Bill Leoutsakos <billleoutsakos@Bills-MacBook-Pro.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>1 parent 03adc8f commit d24bc7e
165 files changed
Lines changed: 30702 additions & 823 deletions
File tree
- .agents/skills
- add-model
- validate-model
- .claude/commands
- .cursor/commands
- .github/workflows
- apps
- docs
- content/docs/en/workflows
- blocks
- deployment
- realtime/src/middleware
- sim
- app
- (interfaces)/chat
- [identifier]
- components/message
- hooks
- api
- chat
- [identifier]
- otp
- manage/[id]
- knowledge
- search
- providers
- resume/[workflowId]/[executionId]/[contextId]
- workflows/[id]
- chat/status
- execute
- workspace/[workspaceId]/w/[workflowId]
- components
- chat
- panel/components/deploy/components/deploy-modal/components/chat
- terminal/components/output-panel
- hooks
- components
- agent-stream
- ui
- executor
- execution
- handlers/agent
- hooks
- queries
- lib
- api/contracts
- copilot/tools/handlers
- deployment
- core/execution-limits
- workflows
- executor
- orchestration
- streaming
- providers
- __fixtures__
- anthropic
- openai-compat
- anthropic
- azure-openai
- baseten
- bedrock
- cerebras
- deepseek
- fireworks
- gemini
- google
- groq
- kimi
- litellm
- meta
- mistral
- nvidia
- ollama-cloud
- ollama
- openai-compat
- openai
- openrouter
- sakana
- together
- vllm
- xai
- zai
- stores
- chat
- terminal/console
- packages
- auth/src
- db
- migrations
- meta
- testing/src/mocks
- scripts
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
52 | 52 | | |
53 | 53 | | |
54 | 54 | | |
| 55 | + | |
55 | 56 | | |
56 | 57 | | |
57 | 58 | | |
| |||
146 | 147 | | |
147 | 148 | | |
148 | 149 | | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
149 | 159 | | |
150 | 160 | | |
151 | 161 | | |
| |||
155 | 165 | | |
156 | 166 | | |
157 | 167 | | |
| 168 | + | |
158 | 169 | | |
159 | 170 | | |
160 | 171 | | |
| |||
201 | 212 | | |
202 | 213 | | |
203 | 214 | | |
| 215 | + | |
204 | 216 | | |
205 | 217 | | |
206 | 218 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
89 | 89 | | |
90 | 90 | | |
91 | 91 | | |
| 92 | + | |
92 | 93 | | |
93 | 94 | | |
94 | 95 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
| 54 | + | |
54 | 55 | | |
55 | 56 | | |
56 | 57 | | |
| |||
145 | 146 | | |
146 | 147 | | |
147 | 148 | | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
148 | 158 | | |
149 | 159 | | |
150 | 160 | | |
| |||
154 | 164 | | |
155 | 165 | | |
156 | 166 | | |
| 167 | + | |
157 | 168 | | |
158 | 169 | | |
159 | 170 | | |
| |||
200 | 211 | | |
201 | 212 | | |
202 | 213 | | |
| 214 | + | |
203 | 215 | | |
204 | 216 | | |
205 | 217 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
88 | 88 | | |
89 | 89 | | |
90 | 90 | | |
| 91 | + | |
91 | 92 | | |
92 | 93 | | |
93 | 94 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
| 49 | + | |
49 | 50 | | |
50 | 51 | | |
51 | 52 | | |
| |||
140 | 141 | | |
141 | 142 | | |
142 | 143 | | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
143 | 153 | | |
144 | 154 | | |
145 | 155 | | |
| |||
149 | 159 | | |
150 | 160 | | |
151 | 161 | | |
| 162 | + | |
152 | 163 | | |
153 | 164 | | |
154 | 165 | | |
| |||
195 | 206 | | |
196 | 207 | | |
197 | 208 | | |
| 209 | + | |
198 | 210 | | |
199 | 211 | | |
200 | 212 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
| 86 | + | |
86 | 87 | | |
87 | 88 | | |
88 | 89 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
140 | 140 | | |
141 | 141 | | |
142 | 142 | | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
143 | 146 | | |
144 | 147 | | |
145 | 148 | | |
| |||
0 commit comments