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)

Abierto
#2,402 1 comentario 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
Python
Estrellas
15.9k
Forks
1.4k
Merge medio
1 d 17 h
PR fusionados (30 d)
163

Descripción

## 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.

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.