fix(runner): pydantic-ai 2.13.0 — restore WebSearch runs broken by Anthropic pause_turn - #322
Conversation
…andling WebSearch-capability runs on Claude have failed since 2026-07-16 with a 400: "web_search tool use ... without a corresponding web_search_tool_result block". Anthropic now routinely pauses long server-tool turns (stop_reason=pause_turn); pydantic-ai 1.102.0 replays the paused history malformed (pydantic/pydantic-ai#2600). 2.10.0+ continues paused turns and merges the segments (pydantic/pydantic-ai#6303). Wrapper changes for the 2.x API: - Agent(history_processors=...) is gone: scaledown now attaches via the ProcessHistory capability. - Agent(instrument=...) is gone: spec instrument:true maps to the Instrumentation capability. - pydantic-ai 2.x has no [mcp] extra; the MCP client ships by default. Verified: unit tests pass unchanged apart from the two new capability tests; live smoke runs (basic haiku run + a 6-search WebSearch run on claude-sonnet-5, the exact failing shape) complete on 2.13.0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The REST/MCP agent-change path collapsed every Tembo CAP failure to "Tembo Coding Agent rejected the request (http)", hiding the upstream status and error body. Include them (body capped at 300 chars) so a failing dispatch is diagnosable from the caller's error alone — the chat UI already shows the same detail via formatCapError. Logging the body remains forbidden (#44); this only returns it to the caller. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| // the body that #44 forbids — it can echo the submitted prompt. | ||
| const detail = | ||
| res.error.kind === "http" | ||
| ? `HTTP ${res.error.status}: ${res.error.body.slice(0, 300) || "(no body)"}` |
There was a problem hiding this comment.
One wrinkle: requestAgentChangeSystem() reuses this path for learning batches, and the scheduler logs res.error on failure. If CAP echoes the submitted prompt in the body, this return value now writes that body to server logs. Consider keeping the body only for request-backed API/MCP responses and returning a sanitized detail for the system/learning path.
| RUN python3 -m venv "${PYDANTIC_AI_VENV}" && \ | ||
| "${PYDANTIC_AI_VENV}/bin/pip" install --no-cache-dir \ | ||
| 'pydantic-ai[mcp]==1.102.0' pyyaml==6.0.2 composio==0.13.1 \ | ||
| 'pydantic-ai==2.13.0' pyyaml==6.0.2 composio==0.13.1 \ |
There was a problem hiding this comment.
The v2 bump also changes streamed tool-result events from .result to .part (event.tool_call_id carries the id). make_stream_handler() still reads FunctionToolResultEvent.result, so under 2.x live progress can mark successful function tool returns as ok: false until the final captured steps arrive. The version bump should include that handler migration so live run steps don't show false failures.
What
Two fixes from investigating Kevin's report (competitor-monitoring-digest failing every scheduled run since Thu 7/16, and agent-update submissions erroring):
1. Upgrade pydantic-ai 1.102.0 → 2.13.0 (fixes the failing runs)
Every run of the
WebSearch-capability agent has died since 7/16 with:Anthropic now routinely ends long server-tool turns with
stop_reason=pause_turn; pydantic-ai 1.x replays the paused history malformed (pydantic-ai#2600). The fix (pydantic-ai#6303, merged 7/15, first released in v2.10.0) continues paused turns and merges the segments; it was never backported to 1.x. The same failure hit once on 7/9 and became deterministic on 7/16 — consistent with an Anthropic-side ramp, not a TAS deploy (no runner changes since 7/8).Wrapper adaptations for the 2.x API (small — late-1.x already used the v2-style names):
Agent(history_processors=…)removed → scaledown compressor attaches via theProcessHistorycapabilityAgent(instrument=…)removed → specinstrument: truemaps to theInstrumentationcapability[mcp]extra no longer exists — MCP client deps ship by default2. Surface CAP status/body on agent-change dispatch failures
The REST/MCP path collapsed all CAP failures to
(http), which is why Kevin's update-submission error was undiagnosable remotely. Now returnsHTTP <status>: <body ≤300 chars>, matching what the chat UI already shows. (This does not fix his submission failure — root cause is upstream at CAP, still being pinned down — but makes the next failure self-describing.)Verification
pytest tests/test_run_pydantic_build_agent.py— 10 passed on 2.13.0 (8 existing unchanged + 2 new capability-path tests)claude-sonnet-5(the exact failing shape) completes on 2.13.0cd web && npx vitest run— 196 passedFollow-up (not in this PR)
Thinkingcapability — the wrapper still ignores specThinking:entries (competitor-monitoring-digest declares one)ctx.usagetiming changed🤖 Generated with Claude Code