TanStack AI version
@tanstack/ai on feat/ag-ui-metadata-compliance (PR #1174)
Framework/Library version
N/A (engine, @tanstack/ai)
Describe the bug and the steps to reproduce it
ToolCallManager.addToolCallStartEvent keys the internal map by index (event.index ?? map.size). AG-UI spec TOOL_CALL_START has no index. First-party adapters still send index on the adapter yield, and they emit START only once (!started after id and name exist).
If a producer sends TOOL_CALL_START twice for the same toolCallId:
- Same id, same index: the second START overwrites the slot and resets
arguments to ''. Any TOOL_CALL_ARGS already stored are lost.
- Same id, missing or different index: the second START inserts a second map row.
getToolCalls() returns the id twice, and the engine can run the tool twice.
First-party adapters do not hit this. It is a custom-server / malformed-stream case.
We reverted an in-PR guard (skip a second START with the same id) out of #1174 so that PR stays on the AG-UI metadata work.
A later fix can skip a repeat START for an already-tracked toolCallId, or key the map by id.
Minimal sequence:
manager.addToolCallStartEvent({
type: "TOOL_CALL_START",
toolCallId: "call_1",
toolCallName: "ask_user",
index: 0,
})
manager.addToolCallArgsEvent({
type: "TOOL_CALL_ARGS",
toolCallId: "call_1",
delta: '{"question":"Which option?"}',
})
manager.addToolCallStartEvent({
type: "TOOL_CALL_START",
toolCallId: "call_1",
toolCallName: "ask_user",
index: 0,
})
// arguments are now ""
Related: #1174
Your Minimal, Reproducible Example - (Sandbox Highly Recommended)
#1174 — ToolCallManager.addToolCallStartEvent in packages/ai/src/activities/chat/tools/tool-calls.ts. Use the snippet above in packages/ai/tests/tool-call-manager.test.ts.
Do you intend to try to help solve this bug with your own PR?
Maybe, I'll investigate and start debugging
Terms & Code of Conduct
TanStack AI version
@tanstack/aionfeat/ag-ui-metadata-compliance(PR #1174)Framework/Library version
N/A (engine,
@tanstack/ai)Describe the bug and the steps to reproduce it
ToolCallManager.addToolCallStartEventkeys the internal map byindex(event.index ?? map.size). AG-UI specTOOL_CALL_STARThas noindex. First-party adapters still sendindexon the adapter yield, and they emit START only once (!startedafter id and name exist).If a producer sends
TOOL_CALL_STARTtwice for the sametoolCallId:argumentsto''. AnyTOOL_CALL_ARGSalready stored are lost.getToolCalls()returns the id twice, and the engine can run the tool twice.First-party adapters do not hit this. It is a custom-server / malformed-stream case.
We reverted an in-PR guard (skip a second START with the same id) out of #1174 so that PR stays on the AG-UI metadata work.
A later fix can skip a repeat START for an already-tracked
toolCallId, or key the map by id.Minimal sequence:
Related: #1174
Your Minimal, Reproducible Example - (Sandbox Highly Recommended)
#1174 —
ToolCallManager.addToolCallStartEventinpackages/ai/src/activities/chat/tools/tool-calls.ts. Use the snippet above inpackages/ai/tests/tool-call-manager.test.ts.Do you intend to try to help solve this bug with your own PR?
Maybe, I'll investigate and start debugging
Terms & Code of Conduct