Two bugs in our pinned ag_ui_langgraph (CopilotKit's LangGraph → AG-UI bridge, $VENV/site-packages/ag_ui_langgraph/agent.py) cause the chat UI to render tool calls with an empty args body and either null or a missing result.
Bug 1 — TOOL_CALL_ARGS never fires for atomic tool calls
When the LLM returns the whole tool call in one chunk (the dominant path for Gemini via langchain-google-genai and any non-streaming .ainvoke path), agent.py:566-580 emits TOOL_CALL_START and then returns without emitting TOOL_CALL_ARGS. The args are sitting on the same raw chunk (rawEvent.data.chunk.tool_calls[0].args), so the data is available — it's just never promoted to its own event.
Symptom: <chat> row's args body renders empty.
Live evidence (idun-assistant on port 8002, Gemini + MCP tools, POST /agent/run):
```json
{"type":"TOOL_CALL_START","toolCallId":"f9c62e39-9d9d-4857-a99d-739822b44877","toolCallName":"search_idun_platform",
"rawEvent":{"data":{"chunk":{"tool_calls":[{"name":"search_idun_platform","args":{"query":"guardrails"},"id":"f9c62e39-..."}]}}}}
```
No TOOL_CALL_ARGS event for f9c62e39-... ever follows.
Bug 2 — TOOL_CALL_RESULT.tool_call_id ≠ TOOL_CALL_START.tool_call_id
agent.py:763-770 emits ToolCallResultEvent with tool_call_id=tool_call_output.tool_call_id — for our agent's flow (custom call_tools node returning {"messages": [ToolMessage(...)]}), tool_call_output ends up as a LangGraph internal handle whose tool_call_id is the node run_id, not the LLM's tool call id.
Symptom: UI gets a TOOL_CALL_RESULT with content but no matching tool call to attach it to → result silently dropped → row shows nothing (or null in older builds where the END handler synthesised a phantom result).
Live evidence:
```json
{"type":"TOOL_CALL_START","toolCallId":"f9c62e39-9d9d-4857-a99d-739822b44877", ...}
{"type":"TOOL_CALL_END","toolCallId":"f9c62e39-9d9d-4857-a99d-739822b44877"}
{"type":"TOOL_CALL_RESULT","toolCallId":"019e18a0-5a1f-7cc3-a2a4-c41e9d2690f3", "content": "Title: Guardrails\nLink: https://docs.idunplatform.com/guardrails/overview..."}
```
f9c62e39… vs 019e18a0… — different ids for the same logical tool call.
Workarounds in PR
Both bugs are upstream and require a fix in ag_ui_langgraph (the package is in .venv/lib/python3.12/site-packages/ag_ui_langgraph/agent.py after install — CopilotKit org). Until that lands, we work around them in services/idun_agent_standalone_ui/lib/use-chat.ts:
- On
TOOL_CALL_START, extract args from e.rawEvent.data.chunk.tool_calls[0].args (dict or string) so the row body has content when no follow-up TOOL_CALL_ARGS event arrives.
- On
TOOL_CALL_RESULT, if no tool call matches the id, attach the result to the most recent tool call without a result. Pragmatic, correct in the single-tool-at-a-time path.
Two bugs in our pinned
ag_ui_langgraph(CopilotKit's LangGraph → AG-UI bridge,$VENV/site-packages/ag_ui_langgraph/agent.py) cause the chat UI to render tool calls with an empty args body and eithernullor a missing result.Bug 1 — TOOL_CALL_ARGS never fires for atomic tool calls
When the LLM returns the whole tool call in one chunk (the dominant path for Gemini via
langchain-google-genaiand any non-streaming.ainvokepath),agent.py:566-580emitsTOOL_CALL_STARTand thenreturns without emittingTOOL_CALL_ARGS. The args are sitting on the same raw chunk (rawEvent.data.chunk.tool_calls[0].args), so the data is available — it's just never promoted to its own event.Symptom:
<chat>row's args body renders empty.Live evidence (idun-assistant on port 8002, Gemini + MCP tools,
POST /agent/run):```json
{"type":"TOOL_CALL_START","toolCallId":"f9c62e39-9d9d-4857-a99d-739822b44877","toolCallName":"search_idun_platform",
"rawEvent":{"data":{"chunk":{"tool_calls":[{"name":"search_idun_platform","args":{"query":"guardrails"},"id":"f9c62e39-..."}]}}}}
```
No
TOOL_CALL_ARGSevent forf9c62e39-...ever follows.Bug 2 — TOOL_CALL_RESULT.tool_call_id ≠ TOOL_CALL_START.tool_call_id
agent.py:763-770emitsToolCallResultEventwithtool_call_id=tool_call_output.tool_call_id— for our agent's flow (customcall_toolsnode returning{"messages": [ToolMessage(...)]}),tool_call_outputends up as a LangGraph internal handle whosetool_call_idis the node run_id, not the LLM's tool call id.Symptom: UI gets a
TOOL_CALL_RESULTwith content but no matching tool call to attach it to → result silently dropped → row shows nothing (ornullin older builds where the END handler synthesised a phantom result).Live evidence:
```json
{"type":"TOOL_CALL_START","toolCallId":"f9c62e39-9d9d-4857-a99d-739822b44877", ...}
{"type":"TOOL_CALL_END","toolCallId":"f9c62e39-9d9d-4857-a99d-739822b44877"}
{"type":"TOOL_CALL_RESULT","toolCallId":"019e18a0-5a1f-7cc3-a2a4-c41e9d2690f3", "content": "Title: Guardrails\nLink: https://docs.idunplatform.com/guardrails/overview..."}
```
f9c62e39…vs019e18a0…— different ids for the same logical tool call.Workarounds in PR
Both bugs are upstream and require a fix in
ag_ui_langgraph(the package is in.venv/lib/python3.12/site-packages/ag_ui_langgraph/agent.pyafter install — CopilotKit org). Until that lands, we work around them inservices/idun_agent_standalone_ui/lib/use-chat.ts:TOOL_CALL_START, extract args frome.rawEvent.data.chunk.tool_calls[0].args(dict or string) so the row body has content when no follow-upTOOL_CALL_ARGSevent arrives.TOOL_CALL_RESULT, if no tool call matches the id, attach the result to the most recent tool call without a result. Pragmatic, correct in the single-tool-at-a-time path.