ag-ui-protocol / ag-ui-protocol/ag-ui
[Feature]: @ag-ui/mastra — opt-in live TOOL_CALL_START for server tools (a long-running server tool paints no running step)
- Vorherrschende Sprache
- Python
- Sterne
- 15.9k
- Forks
- 1.4k
- Ø Merge
- 1 T. 17 Std.
- Gemergte PRs (30 T.)
- 163
Beschreibung
## Context
`@ag-ui/mastra@1.1.1`, `@ag-ui/client@0.0.57`, consumed through `@assistant-ui/react-ag-ui@0.0.54`. Our agent's tools are all SERVER tools on the Mastra agent (audio analysis, generation, retrieval) — several of them run for seconds to minutes.
## Finding
A server tool's call is not visible until it has already finished, so a long-running tool paints no "running" step.
Live tool-call streaming is deliberately gated to CLIENT tools (`isClientTool`, `integrations/mastra/typescript/src/mastra.ts:1467` on `44a92a0`), introduced by #2066 / OSS-393. A server tool therefore takes the buffered path: `case "tool-call"` (`:2007`) parks `{toolCallId, toolName, args}` in `pendingToolCall`, and `flush()` (`:1511`) runs only when the NEXT chunk arrives. For an ordinary sequential tool that next chunk is its own `tool-result`.
The consequence is that the subscriber receives TOOL_CALL_START, TOOL_CALL_ARGS, TOOL_CALL_END and TOOL_CALL_RESULT in one same-millisecond flush, after the tool has completed. A UI that renders tool activity has nothing to render during the part of the turn where the user is actually waiting.
Measured on our own stack (loopback capture of a multi-second transcription tool): the whole call family arrives in a single flush. On a synthetic `fullStream` with a 1500 ms gap between `tool-call-input-streaming-start` and `tool-result`, pristine 1.1.1 gives a 1 ms window between START and RESULT; with the gate widened, 1502 ms.
## Why this is not simply a bug
The gate is load-bearing, and the code says so at `:1459-1466`: buffering is what lets a following `tool-call-suspended` (`:2136`) or `background-task-started` (`:2211`) suppress the normal tool render by clearing `pendingToolCall`. You cannot retract an already-emitted live arg stream, so streaming server tools unconditionally would break those two paths.
## Proposal
An opt-in `MastraAgentConfig.streamServerToolCalls`, default `false` (exactly today's behaviour). When on, `tool-call-input-streaming-start` opens the call for any named tool rather than only client tools.
That leaves one real design question, which is why this is an issue rather than a drive-by PR: **what should a suspend / background-start do to a call that is already open?** Two defensible answers:
1. Close it — emit `TOOL_CALL_END` for the streamed call so no START is left without a terminal. The consumer then sees a tool call with no `TOOL_CALL_RESULT`, and the interrupt or the activity carries the outcome. This keeps the option usable with an honest documented caveat.
2. Refuse the combination — treat `streamServerToolCalls` as unsupported for suspendable / background tools and document that.
I have a branch implementing (1) with tests, happy to open it as a PR against whichever resolution you prefer, or to drop it if the shape is wrong for the project. Per CONTRIBUTING I am filing this first rather than opening the PR cold.
## Related
- #2066 (OSS-393) — the change that introduced the client-tool gate.
- #2380 — `@ag-ui/mastra` continuation-id reuse, same file.
- #2353, #2135 — open PRs touching the same bridge's stream lifecycle.
Beitragsleitfaden
Bewertung
Dieses Issue wurde noch nicht bewertet.