ag-ui-protocol / ag-ui-protocol/ag-ui
[Bug]: [langgraph/typescript] Back-to-back tool calls: second TOOL_CALL_START is never emitted and arg deltas are routed to the wrong tool
- Dominant language
- Python
- Stars
- 15.9k
- Forks
- 1.4k
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 163
Description
## Description
TypeScript-side equivalent of #871 and #1016 (which describe the same bug in the Python integration).
The condition that opens a new tool call in the TypeScript `OnChatModelStream` handler is:
```ts
// integrations/langgraph/typescript/src/agent.ts
const isToolCallStartEvent = !hasCurrentStream && toolCallData?.name;
```
When a first tool call is already streaming (`hasCurrentStream = true`, `currentStream.toolCallId = "tc-1"`) and a new chunk arrives with a different tool name (`toolCallData = { id: "tc-2", name: "lookup" }`), `isToolCallStartEvent` evaluates to `false`. Consequences:
- `TOOL_CALL_START` for `tc-2` is **never emitted**
- Subsequent arg chunks for `tc-2` arrive without a `name`, so `isToolCallArgsEvent` fires and routes those deltas into `tc-1`'s `toolCallId`
- `tc-1` is never explicitly closed with `TOOL_CALL_END`
This matches the event log shown in #1016 — one `TOOL_CALL_START`, one `TOOL_CALL_ARGS` with the wrong `tool_call_id`, one `TOOL_CALL_END` — but produced by the TypeScript handler independently.
## Reproducer
```ts
handleSingleEvent({ tool_call_chunks: [{ id: "tc-1", name: "search" }] });
// → TOOL_CALL_START tc-1 ✓
handleSingleEvent({ tool_call_chunks: [{ id: "tc-2", name: "lookup" }] });
// Actual: nothing (tc-2 start dropped)
// Expected: TOOL_CALL_END tc-1, TOOL_CALL_START tc-2
handleSingleEvent({ tool_call_chunks: [{ args: '{"q":"foo"}' }] });
// Actual: TOOL_CALL_ARGS delta routed to tc-1 (wrong tool)
// Expected: TOOL_CALL_ARGS delta routed to tc-2
```
## Affected component
`integrations/langgraph/typescript`
See also: #871, #1016 (Python)
Contributor guide
Assessment
This issue has not been assessed yet.