Skip to content

fix(agent): list available tools for unknown calls#4360

Open
YOMXXX wants to merge 5 commits into
tinyhumansai:mainfrom
YOMXXX:fix/GH-4118-unknown-tool-list
Open

fix(agent): list available tools for unknown calls#4360
YOMXXX wants to merge 5 commits into
tinyhumansai:mainfrom
YOMXXX:fix/GH-4118-unknown-tool-list

Conversation

@YOMXXX

@YOMXXX YOMXXX commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds corrective unknown-tool feedback that lists the tools available in the current turn.
  • Uses the already filtered/deduped request specs for registry/channel calls so hidden tools are not advertised.
  • Applies the same hinting path to session agent calls and sub-agent tool sources.
  • Adds regression coverage for both registry and session executor unknown-tool paths.

Problem

  • Agents can hallucinate unavailable tool names such as search_files.
  • The previous feedback only said Unknown tool: <name>, which gave the model no concrete recovery path.
  • Hidden or filtered tools must not leak through the corrective hint.

Solution

  • Centralize unknown-tool message formatting in the harness tool executor.
  • Thread the current visible tool-name list into run_one_tool.
  • Build available-tool lists from filtered provider specs, visible session tool names, and sub-agent allowlists/lazy resolver slugs.
  • Preserve existing Unknown tool: ... substrings for compatibility while adding Available tools: ... context.

Submission Checklist

If a section does not apply to this change, mark the item as N/A with a one-line reason. Do not delete items.

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • Diff coverage ≥ 80% — N/A: Rust-only targeted regression; CI cargo-llvm-cov enforces changed-line coverage.
  • Coverage matrix updated — N/A: harness error-message behavior only; no feature row added/removed/renamed.
  • All affected feature IDs from the matrix are listed in the PR description under ## Related — N/A: no coverage-matrix feature ID applies.
  • No new external network dependencies introduced (mock backend used per Testing Strategy)
  • Manual smoke checklist updated if this touches release-cut surfaces (docs/RELEASE-MANUAL-SMOKE.md) — N/A: no release-cut/manual smoke surface touched.
  • Linked issue closed via Closes #NNN in the ## Related section

Impact

  • Runtime impact is limited to agent harness tool-call error feedback.
  • No persistence, migration, network, or platform-specific behavior changes.
  • Security/privacy: available-tool hints are derived from the current visible/allowed tool set, not the full registry when a visibility filter is active.

Related


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

Keep this section for AI-authored PRs. For human-only PRs, mark each field N/A.

Linear Issue

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

Commit & Branch

  • Branch: fix/GH-4118-unknown-tool-list
  • Commit SHA: 257a77d5fe24e3eb46d3109abe870e411ec810aa

Validation Run

  • pnpm --filter openhuman-app format:check — N/A: Rust-only change; frontend app not touched.
  • pnpm typecheck — N/A: Rust-only change; TypeScript not touched.
  • Focused tests: GGML_NATIVE=OFF cargo test --manifest-path Cargo.toml --lib unknown_tool -- --nocapture; GGML_NATIVE=OFF cargo test --manifest-path Cargo.toml --lib visible_tool_names_whitelist_rejects_filtered_out_tools -- --nocapture
  • Rust fmt/check (if changed): cargo fmt --all -- --check; GGML_NATIVE=OFF cargo check --manifest-path Cargo.toml; git diff --check
  • Tauri fmt/check (if changed): N/A: Tauri crate not touched.

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: unknown tool-call errors now include the visible/allowed available tool names for model self-correction.
  • User-visible effect: agent runs should recover more reliably after hallucinating an unavailable tool name.

Parity Contract

  • Legacy behavior preserved: existing Unknown tool: <name> prefix remains in tool results.
  • Guard/fallback/dispatch parity checks: visibility-filtered registry tools, session visible tools, and sub-agent allowlists all build hints from their current allowed surface.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): N/A
  • Canonical PR: N/A
  • Resolution (closed/superseded/updated): N/A

Summary by CodeRabbit

  • Bug Fixes
    • Error messages for unavailable or unknown tools now include clearer “Available tools” guidance.
    • Tool availability hints now reflect session policy restrictions, reducing confusion when a tool is visible but not permitted.
    • Recovery guidance for tool errors is preserved even under small output limits.
    • Visibility-aware tool handling is now more consistent during agent sessions and turn execution.

@YOMXXX YOMXXX requested a review from a team June 30, 2026 18:07
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4b7f89ff-54c4-4ed4-8a67-aff96e427358

📥 Commits

Reviewing files that changed from the base of the PR and between f440067 and c54800c.

📒 Files selected for processing (4)
  • src/openhuman/agent/harness/session/agent_tool_exec.rs
  • src/openhuman/agent/harness/session/turn/core.rs
  • src/openhuman/tinyagents/middleware.rs
  • src/openhuman/tinyagents/mod.rs
💤 Files with no reviewable changes (4)
  • src/openhuman/agent/harness/session/turn/core.rs
  • src/openhuman/tinyagents/mod.rs
  • src/openhuman/agent/harness/session/agent_tool_exec.rs
  • src/openhuman/tinyagents/middleware.rs

📝 Walkthrough

Walkthrough

Adds a visibility_filter_active flag propagated from turn/session configuration through ToolPolicyEnforcement and ToolPolicyMiddleware, computes sorted "available tools" lists filtered by visibility and policy, appends them as hints to unknown-tool and not-visible error messages, exempts such guidance from output truncation, and extends tests accordingly.

Changes

Unknown Tool Error Enrichment

Layer / File(s) Summary
Enforcement contract and wiring
src/openhuman/tinyagents/mod.rs
Adds public visibility_filter_active field to ToolPolicyEnforcement, computes sorted available_tool_names, and wires both into ToolPolicyMiddleware::new and UnknownToolAdapter::new.
ToolPolicyMiddleware visibility handling
src/openhuman/tinyagents/middleware.rs
Adds visibility_filter_active field/constructor param, available_tools_hint() helper, and updates unknown-tool handling to return "not available to this agent" errors with hints.
Recovery-guidance truncation exemption
src/openhuman/tinyagents/middleware.rs
Adds is_recovery_guidance matcher and updates byte-cap truncation to skip guidance messages, with a unit test.
Turn graph visibility wiring
src/openhuman/agent/harness/session/turn/core.rs
Derives visible_tool_names from visible_tool_specs and sets visibility_filter_active based on non-emptiness.
agent_tool_exec error branches and tests
src/openhuman/agent/harness/session/agent_tool_exec.rs
Adds helpers computing visibility+policy-filtered available tools, appends "Available tools" hints to error paths, and adds tests including a PermissionedTool stub.
Turn coverage assertions
tests/agent_session_turn_raw_coverage_e2e.rs
Asserts exact tool-call names invoked and verifies "Allowed tools:" boundary excludes a policy-denied tool.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Agent
  participant ToolPolicyMiddleware
  participant ToolOutputMiddleware
  Agent->>ToolPolicyMiddleware: call unregistered/hidden tool
  ToolPolicyMiddleware->>ToolPolicyMiddleware: check visibility_filter_active
  ToolPolicyMiddleware->>ToolPolicyMiddleware: build available_tools_hint()
  ToolPolicyMiddleware-->>ToolOutputMiddleware: "not available" error + hint
  ToolOutputMiddleware->>ToolOutputMiddleware: detect is_recovery_guidance, skip truncation
  ToolOutputMiddleware-->>Agent: return full guidance message
Loading

Suggested labels: agent, bug

Suggested reviewers: M3gA-Mind

Poem

A rabbit hopped through tools untold,
Some hidden, some too bold to hold.
Now when a name comes up unknown,
The hint list says "here's what you own!" 🐰
No more truncated, cryptic sighs—
Just clear, sorted tool goodbyes. 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately describes the main change to unknown-tool handling.
Linked Issues check ✅ Passed The PR adds early unknown-tool validation and available-tools hints, matching the issue's core fix and regression coverage.
Out of Scope Changes check ✅ Passed The middleware and test changes support the same unknown-tool guidance work and do not appear unrelated.
Docstring Coverage ✅ Passed Docstring coverage is 94.12% which is sufficient. The required threshold is 80.00%.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot added agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. bug labels Jun 30, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 257a77d5fe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/openhuman/agent/harness/session/agent_tool_exec.rs Outdated

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
src/openhuman/agent/harness/subagent_runner/ops/tool_source.rs (1)

98-104: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse format_available_tools_hint here for consistency.

This branch still builds the hint inline via available.join(", "), bypassing the centralized formatter this PR introduces. When available is empty (e.g., empty allowed_names and no lazy resolver) the message degrades to a trailing Available tools: instead of the helper's No tools are available in this turn. fallback.

♻️ Proposed alignment
             let available = self.available_tool_names();
             let text = format!(
-                "Error: tool '{}' is not available to the {} sub-agent. Available tools: {}",
-                call.name,
-                self.agent_id,
-                available.join(", ")
+                "Error: tool '{}' is not available to the {} sub-agent. {}",
+                call.name,
+                self.agent_id,
+                crate::openhuman::agent::harness::engine::tools::format_available_tools_hint(&available)
             );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/agent/harness/subagent_runner/ops/tool_source.rs` around lines
98 - 104, The unavailable-tool error message is still formatting the hint inline
instead of using the centralized formatter. Update the `ToolSource::get_tool`
branch to call `format_available_tools_hint` with the `available_tool_names()`
result so the message stays consistent. This ensures the fallback text is used
when there are no tools available, instead of leaving a blank `Available tools:`
suffix.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/openhuman/agent/harness/subagent_runner/ops/tool_source.rs`:
- Around line 98-104: The unavailable-tool error message is still formatting the
hint inline instead of using the centralized formatter. Update the
`ToolSource::get_tool` branch to call `format_available_tools_hint` with the
`available_tool_names()` result so the message stays consistent. This ensures
the fallback text is used when there are no tools available, instead of leaving
a blank `Available tools:` suffix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d8fa0f09-e7ef-4279-a987-c0ae2610213e

📥 Commits

Reviewing files that changed from the base of the PR and between f861420 and 257a77d.

📒 Files selected for processing (5)
  • src/openhuman/agent/harness/engine/tool_source.rs
  • src/openhuman/agent/harness/engine/tools.rs
  • src/openhuman/agent/harness/session/agent_tool_exec.rs
  • src/openhuman/agent/harness/subagent_runner/ops/tool_source.rs
  • src/openhuman/agent/harness/test_support_tests.rs

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 30, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 30, 2026
@coderabbitai coderabbitai Bot removed agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. bug labels Jul 1, 2026

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
tests/agent_session_turn_raw_coverage_e2e.rs (1)

824-862: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider asserting the “Available tools” hint for the hidden_tool error.

The ordered tool-call assertion and the allowed-tools boundary check look correct and match the scripted provider order and policy rendering logic. However, since this test specifically exercises the unknown-tool path (hidden_tool), it would be a good opportunity to also assert that the tool_result content includes the new Available tools: ... hint introduced by this cohort, rather than only checking the error status tag.

♻️ Suggested addition
     assert!(joined.contains("<tool_result name=\"hidden_tool\" status=\"error\">"));
+    assert!(
+        joined.contains("Unknown tool: hidden_tool") && joined.contains("Available tools:"),
+        "expected unknown-tool error to include available-tools hint: {joined}"
+    );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/agent_session_turn_raw_coverage_e2e.rs` around lines 824 - 862, The
unknown-tool path in this test already checks the hidden_tool error tag, but it
should also verify the new “Available tools” hint appears in the tool_result
content. Update the assertions around the provider output in the existing test
to check the joined message text includes the hint for hidden_tool, using the
same provider.requests()/joined content and the hidden_tool tool_result as the
anchor.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/agent_session_turn_raw_coverage_e2e.rs`:
- Around line 824-862: The unknown-tool path in this test already checks the
hidden_tool error tag, but it should also verify the new “Available tools” hint
appears in the tool_result content. Update the assertions around the provider
output in the existing test to check the joined message text includes the hint
for hidden_tool, using the same provider.requests()/joined content and the
hidden_tool tool_result as the anchor.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: afeb34ad-4719-43ac-86fc-3c27926a7cfe

📥 Commits

Reviewing files that changed from the base of the PR and between 0e5a191 and f440067.

📒 Files selected for processing (1)
  • tests/agent_session_turn_raw_coverage_e2e.rs

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 1, 2026
@coderabbitai coderabbitai Bot added agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. bug labels Jul 2, 2026

@sanil-23 sanil-23 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.

Review — #4360 fix(agent): list available tools for unknown calls

Walkthrough. The change enriches the "unknown tool" / "not available" recovery messages so a hallucinated tool call gets an Available tools: … hint the model can self-correct against, and exempts that guidance from the byte-cap truncation so it survives a small budget. The hint is correctly derived from the visible + policy-filtered set (not the full registry), so the "does it leak the curated tool set / blow up tokens" concern is largely a non-issue — it lists tool names only, bounded by the agent's advertised surface (see caveat in Major #2). The core wiring — visibility_filter_active threaded separately from the policy-filtered allowed set, and turn/core.rs switching visible_tool_names to the policy-filtered visible_tool_specs — is sound and matches the existing ParentExecutionContext precedent in turn/tools.rs:125-131. My main concerns are maintainability/dead-path and cross-PR collision, not correctness. No blockers.

Changed files

File What Assessment
tinyagents/mod.rs visibility_filter_active field + available_tool_names (allowed-filtered), wired into ToolPolicyMiddleware/UnknownToolAdapter ✅ correct
tinyagents/middleware.rs visibility_filter_active param, available_tools_hint(), is_recovery_guidance() truncation exemption ✅ correct, one caveat
tinyagents/tools.rs UnknownToolAdapter carries available_tool_names; new pub(crate) format_available_tools_hint ✅ correct
agent/harness/session/turn/core.rs visible names from visible_tool_specs; visibility_filter_active from raw scope ✅ correct (see Minor #3)
agent/harness/session/agent_tool_exec.rs available_tool_names_for_ctx + hints on 2 error branches + 2 tests ⚠️ dead production path (Major #1)
tinyagents/tests.rs, tests/agent_session_turn_raw_coverage_e2e.rs regression coverage ✅ good, one gap

Major

1. agent_tool_exec.rs changes target a code path with no production callers (test-only).
run_agent_tool_call is reached only via execute_tool_callexecute_tools, and execute_tools has zero non-test callers anywhere in the repo (grep: every other reference is a doc comment). turn/core.rs:695 confirms "the legacy run_turn_engine has been removed" and every chat/sub-agent turn now routes through run_turn_via_tinyagents_shared. So the ~140 added lines here (available_tool_names_for_ctx + the two Available tools: branches + session_tool_executor_unknown_tool_* tests) modify and test a path that no longer executes in production. The real production behavior lives entirely in middleware.rs / tools.rs. This isn't a bug, but the two new tests read as covering "the session unknown-tool path" when they actually exercise dead code — false coverage confidence. Recommend either (a) dropping the agent_tool_exec.rs hunk and relying on tinyagents/tests.rs + the e2e test, or (b) adding a one-line comment noting the path is retained for parity only. (If I've missed a dynamic/trait caller, disregard — but I checked the whole tree.)

2. Direct collision + intent overlap with open M3gA-Mind PRs on the same functions.

  • #4390 (fix(agent-harness): structured policy-block messages) edits both agent_tool_exec.rs and tinyagents/middleware.rs, restructuring the exact "not available to this agent" / policy-deny message surface that this PR appends hints to. High textual-conflict risk and semantic duplication risk — #4390's "workaround" text may duplicate #4360's Available tools: hint. These two are solving adjacent halves of the same problem and should be sequenced/coordinated, not merged blind.
  • #4389 (change strategy on repeated unproductive tool results) edits tinyagents/middleware.rs and tinyagents/mod.rs, both adding middleware registration inside run_turn_via_tinyagents_shared. Merge-order conflict is near-certain; also functionally adjacent (the richer-but-still-stable unknown-tool message keeps RepeatedToolFailureMiddleware/no-progress detection working, which is good — just call it out).
  • #4386 / #4387 both touch turn/core.rs; low-to-moderate textual conflict with this PR's lines 928-949.
  • #4419 (merged, observability timeline name) is orthogonal and complementary — it fixes the attempted name shown in the timeline; #4360 fixes the result content. No file overlap. But note this branch has not rebased on #4419 (its observability.rs is still at the #4249 base commit), so the sentinel timeline-name fix only applies once #4360 lands on top of current main — verify at merge. No action needed beyond a rebase.

Minor

3. Recovery-guidance truncation exemption removes all byte-capping. is_recovery_guidance() skips the budget backstop entirely for these messages. For a wildcard-visibility agent with a very large advertised tool set (e.g. a Composio-heavy agent), available_tool_names is the whole callable set and the Available tools: … line is now injected uncapped on every hallucinated call. It's names-only and bounded by the advertised surface, so no leak, but consider capping the hint (e.g. first N names + … (+K more)) so the exemption can't produce a multi-KB message. The model-controlled requested tool name also rides into this un-truncated message — again bounded in practice, but worth a length guard.

4. Duplicated format_available_tools_hint. agent_tool_exec.rs defines its own private copy identical to the new pub(crate) one in tinyagents/tools.rs. If #1 keeps the agent_tool_exec.rs code, reuse the tinyagents one instead of duplicating the fallback string ("No tools are currently available.").

5. Semantic downgrade in the wildcard-scope + channel-readonly case. Because allowed is now the policy-filtered set, a channel-denied tool (e.g. a write tool on a readonly channel) is no longer in valid_tools, so a call to it is rewritten onto the sentinel and — since visibility_filter_active is false in the wildcard case — falls through to UnknownToolAdapter producing Unknown tool: <write_tool> rather than a "denied by policy / not available on this channel" message. The mod.rs doc comment says this is intentional (the model never saw the tool advertised, so "unknown" is defensible), and the e2e test confirms denied by policy 'round17-deny' still fires for named-rule denials. Just confirm the "unknown" wording for channel-permission-denied tools is the desired model-facing UX.


Nitpicks / questions

  • PR description is stale. It claims hints are applied to "sub-agent tool sources … allowlists/lazy resolver slugs", but the final diff has no subagent_runner/ops/tool_source.rs or engine/tools.rs changes (those existed at commit 257a77d that CodeRabbit/Codex reviewed, then were dropped when the branch merged main). Sub-agents now get the hint solely via UnknownToolAdapter(subagent=true). Please update the description so reviewers aren't hunting for reverted code — and confirm the drop of the subagent_runner path was intentional (that path's un-hinted error was the subject of the outstanding Codex nitpick, which is now moot).
  • The e2e test asserts the ordered tool-call names and the allowed-tools boundary but not the Available tools: hint on the hidden_tool result (CodeRabbit flagged this). One extra assert!(joined.contains("Available tools:")) would close the loop.
  • available_tools_hint() in the middleware sorts visible_tool_names (already allowed) on every call — fine given it only fires on the error path, but it could be precomputed alongside available_tool_names for symmetry with UnknownToolAdapter.

Looks good

  • Threading visibility_filter_active separately from the (policy-filtered) allowed set is the right call — it preserves "genuinely unknown, no scope → generic Unknown tool" vs "scoped, hidden → not available to this agent". The mod.rs doc comment explaining why is excellent.
  • Hint is built from valid_tools minus the sentinel / from the policy-filtered visible set, so hidden and policy-denied tools are not advertised — the leak concern is properly handled, and tinyagents/tests.rs::unknown_tool_hint_uses_the_policy_filtered_available_set locks it in end-to-end.
  • Preserving the Unknown tool: <name> substring keeps backward-compat with anything grepping the legacy wording.

Net: no blockers; correctness is solid. Please (a) resolve the dead-path question for the agent_tool_exec.rs hunk, (b) coordinate merge order with #4390/#4389, and (c) refresh the PR description.

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

Labels

agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. bug

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

[Harness] Agents call unavailable / nonexistent tool names

2 participants