Skip to content

fix(agent-harness): surface attempted unavailable tool names on the timeline (#4118)#4479

Merged
senamakel merged 3 commits into
tinyhumansai:mainfrom
senamakel:fix/GH-4118-unknown-tool-did-you-mean
Jul 4, 2026
Merged

fix(agent-harness): surface attempted unavailable tool names on the timeline (#4118)#4479
senamakel merged 3 commits into
tinyhumansai:mainfrom
senamakel:fix/GH-4118-unknown-tool-did-you-mean

Conversation

@senamakel

Copy link
Copy Markdown
Member

Summary

  • Re-surface the attempted unavailable tool on the "View processing" timeline: on AgentEvent::UnknownToolCall the event bridge now projects a ToolCallStarted + a failed ToolCallCompleted (parent and subagent scopes), labelled "<tool> (unavailable)".
  • Regression test asserting the attempted name is projected as a failed call.
  • (CI) Separate test(ci): commit adds the two config_*_privacy_mode methods 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::ReturnToolError path (post-#4249) + the integrations resolver:

  • Tool names are validated against the live registry before dispatch (self.tools.get(&call.name)), so a hallucinated / out-of-scope tool never executes.
  • The model receives a recoverable unknown tool \` (arguments: …); valid tools: []` corrective and can self-correct.

The remaining gap is observability: the #4249 migration removed the UNKNOWN_TOOL_SENTINEL + UnknownToolRewriteMiddleware, and ReturnToolError recovers the call without emitting Started/Completed events. The bridge's UnknownToolCall arm only logged to tracing, 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 on UnknownToolCall:

  • Emit ToolCallStarted (attempted name + arguments, display_label = "<tool> (unavailable)", display_detail = "tool not available") then a failed ToolCallCompleted (success: false, failure = describe(ToolFailureClass::Unknown) — recoverable, since the model got the valid-tools corrective and can retry), keyed by the same call_id. Handles both the top-level and Subagent* scopes, mirroring how real tool calls are projected.
  • The 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/tinyagents agent_loop/mod.rs), so it belongs in a coordinated tinyagents change — left as follow-up.

Submission Checklist

  • Tests added or updated — unknown_tool_call_projects_attempted_name_as_failed_timeline_row asserts the attempted tool surfaces as a failed timeline call.
  • Diff coverage ≥ 80% — the new UnknownToolCall projection is exercised by the test; both scope branches share the projection shape.
  • Coverage matrix updated — N/A: behaviour-only change (observability projection of an existing event).
  • All affected feature IDs listed under ## Related — N/A.
  • No new external network dependencies introduced.
  • Manual smoke checklist updated if this touches release-cut surfaces — N/A: timeline projection only.
  • Linked issue closed via Closes #NNN.

Impact

  • Desktop/CLI agent harness observability only. No dispatch/behavior change: the crate still validates + recovers unknown tools exactly as before; this only makes the attempted (and recovered-from) call visible on the timeline.

Related


AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: fix/GH-4118-unknown-tool-did-you-mean
  • Commit SHA: see head

Validation Run

  • pnpm --filter openhuman-app format:check — N/A (no frontend changes)
  • pnpm typecheck — N/A (no TS changes)
  • Focused tests: openhuman::tinyagents::observability::tests::unknown_tool_call_projects_attempted_name_as_failed_timeline_row — execution deferred to CI (shared build host)
  • Rust fmt/check (if changed): rustfmt --edition 2021 --check clean; cargo check --bin openhuman-core passed (Finished dev)
  • Tauri fmt/check (if changed): N/A

Validation Blocked

  • command: cargo test --lib -- openhuman::tinyagents::observability::tests
  • error: none — shared build host saturated; test-binary compile did not finish in-window
  • impact: cargo check compiled clean; the test is a deterministic assertion over the projected events, validated by CI

Behavior Changes

  • Intended behavior change: an attempted unavailable tool is now shown on the processing timeline as a failed call, instead of being invisible.
  • User-visible effect: users see " (unavailable)" as a failed step when the agent tries a nonexistent/out-of-scope tool (and recovers).

Parity Contract

  • Legacy behavior preserved: dispatch, unknown-tool recovery, and the model-facing "valid tools: [...]" corrective are all unchanged (crate-owned). Only an additive observability projection is introduced.
  • Guard/fallback/dispatch parity checks: N/A — no dispatch change.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none
  • Canonical PR: this one

senamakel added 2 commits July 3, 2026 23:27
…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
@senamakel senamakel requested a review from a team July 3, 2026 23:27
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 9 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 04d11cea-a738-4607-b2f7-4c9fd5fd2df1

📥 Commits

Reviewing files that changed from the base of the PR and between 17dbc5c and 7d3a159.

📒 Files selected for processing (1)
  • src/openhuman/tinyagents/observability.rs

Comment @coderabbitai help to get the list of available commands.

@senamakel senamakel self-assigned this Jul 4, 2026
# Conflicts:
#	src/openhuman/tinyagents/observability.rs
@senamakel senamakel merged commit fee83bd into tinyhumansai:main Jul 4, 2026
13 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Harness] Agents call unavailable / nonexistent tool names

1 participant