fix(agent-harness): surface attempted unavailable tool names on the timeline (#4118)#4479
Conversation
…imeline (tinyhumansai#4118) Pre-dispatch validation of tool names against the live registry, and the structured "unknown tool `<name>`; valid tools: [...]" corrective that names the available set, are already handled end-to-end by the crate `UnknownToolPolicy::ReturnToolError` path (post-tinyhumansai#4249) — a hallucinated / out-of-scope tool never executes and the model gets a recoverable error listing the valid tools. What was lost in that migration: the *attempted* tool no longer appears on the "View processing" timeline. `ReturnToolError` recovers the call without emitting Started/Completed, and the event bridge's `UnknownToolCall` arm only logged to tracing — so an agent trying an unavailable tool showed nothing, and the recovery was invisible (the intent of tinyhumansai#4419, dropped by the tinyhumansai#4249 sentinel removal). Project the attempted tool as a failed timeline row: on `UnknownToolCall` the bridge now emits `ToolCallStarted` + a failed `ToolCallCompleted` (both parent and subagent scopes) keyed by the same call_id, labelled "<tool> (unavailable)" with a `tool not available` detail and an `Unknown` (recoverable) failure class. Test asserts the attempted name surfaces as a failed call. Claude-Session: https://claude.ai/code/session_01KcmdqJVpjmnH31HqTHRLwG
Unrelated CI unblock: tinyhumansai#4446 (Privacy Mode) registered `config_get_privacy_mode` / `config_set_privacy_mode` unconditionally but did not update the `worker_a_controller_schemas_are_fully_exposed` golden, so that test fails on main for every PR without the fix (same fix as tinyhumansai#4475). Claude-Session: https://claude.ai/code/session_01KcmdqJVpjmnH31HqTHRLwG
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 9 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
Comment |
# Conflicts: # src/openhuman/tinyagents/observability.rs
Summary
AgentEvent::UnknownToolCallthe event bridge now projects aToolCallStarted+ a failedToolCallCompleted(parent and subagent scopes), labelled"<tool> (unavailable)".test(ci):commit adds the twoconfig_*_privacy_modemethods to a stale schema-catalog golden — see## Related.Problem
Issue #4118: agents call tools that don't exist in their available set; the call should be caught early with a corrective signal (available set surfaced) instead of silently failing into a retry loop.
The core behavior is already implemented by the crate
UnknownToolPolicy::ReturnToolErrorpath (post-#4249) + the integrations resolver:self.tools.get(&call.name)), so a hallucinated / out-of-scope tool never executes.unknown tool \` (arguments: …); valid tools: []` corrective and can self-correct.The remaining gap is observability: the #4249 migration removed the
UNKNOWN_TOOL_SENTINEL+UnknownToolRewriteMiddleware, andReturnToolErrorrecovers the call without emitting Started/Completed events. The bridge'sUnknownToolCallarm only logged totracing, so an agent attempting an unavailable tool showed nothing on the timeline — the recovery was invisible. This is exactly the intent of #4419 (keep the attempted tool name in the timeline), dropped by the sentinel removal.Solution
In
OpenhumanEventBridge, project the attempted tool as a failed timeline row onUnknownToolCall:ToolCallStarted(attempted name + arguments,display_label = "<tool> (unavailable)",display_detail = "tool not available") then a failedToolCallCompleted(success: false,failure = describe(ToolFailureClass::Unknown)— recoverable, since the model got the valid-tools corrective and can retry), keyed by the samecall_id. Handles both the top-level andSubagent*scopes, mirroring how real tool calls are projected.tracing::debug!is retained.Scope note: a ranked "did you mean" suggestion in the corrective message would be the natural next step, but that message is built inside the vendored crate (
vendor/tinyagentsagent_loop/mod.rs), so it belongs in a coordinatedtinyagentschange — left as follow-up.Submission Checklist
unknown_tool_call_projects_attempted_name_as_failed_timeline_rowasserts the attempted tool surfaces as a failed timeline call.UnknownToolCallprojection is exercised by the test; both scope branches share the projection shape.N/A: behaviour-only change(observability projection of an existing event).## Related— N/A.N/A: timeline projection only.Closes #NNN.Impact
Related
tinyagents); thetest(ci):commit addsconfig_get_privacy_mode/config_set_privacy_modeto theworker_a_controller_schemas_are_fully_exposedgolden (pre-existing feat(privacy): Privacy Mode + local-only inference enforcement (#4435) #4446 break; also fixed in feat(orchestration): nested contacts → sessions with create + session send #4475) — drop once main carries the fix.AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
fix/GH-4118-unknown-tool-did-you-meanValidation Run
pnpm --filter openhuman-app format:check— N/A (no frontend changes)pnpm typecheck— N/A (no TS changes)openhuman::tinyagents::observability::tests::unknown_tool_call_projects_attempted_name_as_failed_timeline_row— execution deferred to CI (shared build host)rustfmt --edition 2021 --checkclean;cargo check --bin openhuman-corepassed (Finished dev)Validation Blocked
command:cargo test --lib -- openhuman::tinyagents::observability::testserror:none — shared build host saturated; test-binary compile did not finish in-windowimpact:cargo checkcompiled clean; the test is a deterministic assertion over the projected events, validated by CIBehavior Changes
Parity Contract
Duplicate / Superseded PR Handling