Skip to content

fix(agents): normalize Command-wrapped tool results - #4977

Open
BetterAndBetterII wants to merge 1 commit into
bytedance:mainfrom
BetterAndBetterII:fix/command-tool-result-semantics
Open

fix(agents): normalize Command-wrapped tool results#4977
BetterAndBetterII wants to merge 1 commit into
bytedance:mainfrom
BetterAndBetterII:fix/command-tool-result-semantics

Conversation

@BetterAndBetterII

Copy link
Copy Markdown

Closes #4976

Why

ToolMessages returned inside Command(update={"messages": [...]}) skip result normalization and progress tracking. Error payloads then get a default success receipt even though the content is Error: ....

What changed

  • normalize_tool_result now stamps matching ToolMessages inside a Command (same metadata contract as a bare result; producer-supplied meta is left alone).
  • ToolErrorHandlingMiddleware passes the current tool_call_id into that normalization.
  • ToolProgressMiddleware assesses the matching Command message instead of ignoring every Command.

Receipts already match Command messages by tool_call_id; they now see the normalized status.

Surface area

  • Agents / LangGraph — agent node, graph wiring, langgraph.json, or prompt change

Bug fix verification

  • Test path: backend/tests/test_command_tool_result_semantics.py
  • Red on main, green on this branch: yes
  • 20 cases: bare-result controls, Command success/error/partial_success, producer-meta preservation, unrelated messages left intact, sync/async error-handling and progress paths, receipts, and setup_agent / view_image through the production middleware chain.

Validation

cd backend && .venv/bin/python -m pytest tests/test_command_tool_result_semantics.py tests/test_tool_error_handling_middleware.py tests/test_tool_progress_middleware.py tests/test_tool_receipt.py tests/test_tool_receipt_middleware.py tests/test_tool_result_meta.py -q

20 new tests passed. Related suites passed except one pre-existing test_build_subagent_runtime_middlewares_threads_app_config_to_llm_middleware ordering failure on main.

AI assistance

Tool(s) used: none

How you used it: n/a

  • I've read and understand every line of this change and take responsibility for it — it's not unreviewed AI output.

Command-wrapped ToolMessages skipped result metadata and progress
tracking, so error receipts could be recorded as success.
@CLAassistant

CLAassistant commented Aug 23, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@github-actions github-actions Bot added area:agents Agents, subagents, graph wiring, prompts, langgraph.json needs-validation Touches front/back contract surface; needs real-path validation risk:high High risk: backend API, agents, sandbox, auth, deps, CI size/L PR changes 300-700 lines labels Aug 23, 2026
continue
if tool_call_id and str(message.tool_call_id) != tool_call_id:
continue
normalize_tool_message(message)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Delegated task failures still get a success receipt through this path. _task_result_command carries the authoritative subagent_status (failed, cancelled, timed_out, or polling_timed_out) but leaves ToolMessage.status at LangChain's default success; its text starts with Task failed... / Task cancelled..., not the Error: prefix that normalize_tool_message recognizes. As a result this call stamps deerflow_tool_meta.status="success", and the outer receipt layer records success as well. I reproduced all four non-completed statuses with the production error-handling + receipt chain. Please derive task metadata from the structured subagent_status (or stamp it in _task_result_command) and add regression cases for these statuses, otherwise one of the principal Command producers named in #4976 remains unfixed.

@willem-bd willem-bd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Command handling is correct for the newly covered setup_agent/view_image cases, but delegated task failures remain misclassified: failed, cancelled, timed-out, and polling-timeout task results all receive deerflow_tool_meta.status=success and therefore a success receipt. I reproduced this against the current head using the production error-handling and receipt chain. Please use the structured subagent_status when normalizing task results and add coverage for the non-completed statuses. Details are in the inline comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:agents Agents, subagents, graph wiring, prompts, langgraph.json needs-validation Touches front/back contract surface; needs real-path validation risk:high High risk: backend API, agents, sandbox, auth, deps, CI size/L PR changes 300-700 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] Command-wrapped ToolMessages bypass result normalization and report error receipts as success

3 participants