feat(agent): drop the integrations sub-agent — search and call connected actions directly - #6447
Conversation
…deferred integration actions Remove the `SkillDelegationTool` that collapsed all connected integrations into a single `delegate_to_integrations_agent` tool, keeping only the per-action `Deferred` tools that are already emitted alongside it. The collapsed tool routed through a sub-agent, which added a blocking agentic round-trip and a second prompt for work the parent could do in one call. With the deferred actions now searchable through the harness's `tool_search` bridge, the delegation layer is unnecessary overhead. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…ment The doc comment for `collect_deferred_integration_actions` still mentioned a comparison with `sanitise_slug` collisions that no longer exists in the code, making the comment misleading. The reference has been removed to keep the documentation accurate. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The single `delegate_to_integrations_agent` tool and its test module have been removed. This collapsed delegation tool was introduced to replace the per-toolkit fan-out of delegate tools, but the approach is no longer needed as the integrations agent is now invoked through a different routing mechanism. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Remove the `SkillDelegationTool` and its associated `INTEGRATIONS_DELEGATE_TOOL_NAME` constant, along with the `Integrations` variant in the dispatch enum and its execution path. This tool was no longer used after the delegation system was consolidated into the collapsed delegation approach, which handles toolkit scoping through a `toolkit_override` parameter instead of a separate skill-based filter. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…nition Extract the repeated inline logic for computing allowed subagent ids into a dedicated method on AgentDefinition, removing the special case that mapped the skills wildcard to the integrations agent. The skills entry no longer spawns a sub-agent; its tools are now searched and called directly by the agent. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Removed an unused import of `SubagentEntry` from the `allowed_subagent_ids_for` function, as the type is no longer referenced in that scope. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Replace the collapsed `delegate_to_integrations_agent` sub-agent pattern with a direct model where every connected integration toolkit's actions are registered as `Deferred` tools on the orchestrator itself. The `## Connected Integrations` block now teaches the model to use `tool_search` for the action and call it directly, removing the delegation layer. Permission-gated tools that are not searchable are listed in a new appendix with their unlock paths so the model can guide the user instead of incorrectly claiming the action is unavailable. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The orchestrator prompt no longer references a separate integrations agent; instead it instructs the model to search for and call integration tools directly. The unused `ToolCallFormat` import is also removed from the Rust source. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…_search The announcement note for newly connected integrations now correctly tells the user that integration actions are reachable through `tool_search` instead of the outdated `delegate_to_integrations_agent` mechanism. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The documentation comments across multiple files were updated to accurately describe how `{ skills = "*" }` wildcards are handled. Previously, the comments incorrectly stated that skills wildcards collapse into a single `delegate_to_integrations_agent` tool, but the actual behavior is that they expand to searchable integration actions on the agent's own belt. The tier validation logic and related comments were also corrected to remove references to the old workflow-based routing model.
Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…earch-and-call for composio act
Connected-service actions are no longer routed through a dedicated `integrations_agent` sub-agent. Instead, the orchestrator synthesises per-action `Deferred` tools from the `{ skills = "*" }` wildcard, searches for them via `tool_search`, and calls them directly. This removes the `integrations_agent` from the planner's available worker set and updates all prompts and documentation to reflect that the orchestrator handles service interactions itself, eliminating an unnecessary delegation hop.
Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The comment in the tools_agent configuration previously stated that integration-specific tools belong to `integrations_agent`, but this is no longer accurate. Updated the comment to reflect that these tools are now owned by the orchestrator and searched through `tool_search`. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…_agent Updated comments across three files to replace references to the now-removed `delegate_to_integrations_agent` with descriptions of the integration action catalogue and searchable tool surface, keeping the documentation accurate after the architectural change that removed the sub-agent spawn in favor of direct integration action tools. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Changed the two call sites that retrieve allowed subagent IDs from a resolved definition to explicitly collect the iterator into a Vec, ensuring the returned type matches the expected owned collection rather than a lazy iterator. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
… deferred tools Replace the single `delegate_to_integrations_agent` tool with individual `Deferred` action tools for each connected integration action, so the orchestrator can route directly to specific actions rather than delegating to a sub-agent. This removes the collapsed delegation pattern and its associated sanitisation and fallback logic, simplifying the tool catalogue and making action discovery more explicit. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the orchestrator prompt tests to reflect the removal of the `delegate_to_integrations_agent` sub-agent in favour of a `tool_search` + direct call pattern. The old tests asserted delegation-specific behaviour and a format-dependent guardrail that no longer applies; the new tests verify the unified search-bridge block, its format independence, capability routing, and gated-tool listing. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…arch tool in tests The test assertions and mock data are updated to reflect the replacement of the `delegate_to_integrations_agent` tool with the new `research` tool across orchestration, registry, and session host tests. Comments explaining the old delegation mechanism are also revised to describe the new searchable integration actions approach. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…d dispatch Replace the loop that checked every synthesised delegation name with separate assertions for the collapsed and retired tools, and update the test helper calls to use "research" instead of the retired "delegate_to_integrations_agent" name. This ensures that a stale tool by the retired name cannot spawn a sub-agent for a single integration action. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The `skill_delegation_tool_runs_integrations_agent_e2e` test was removed because the `SkillDelegationTool` it tested has been deleted from the codebase, making the test no longer compilable or relevant. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…t architecture The module documentation for collapsed delegation was outdated, still referencing a symmetry between integration and sub-agent axes that no longer holds. The integration axis now uses `Deferred` tools and `tool_search` instead of a delegation tool, so the comment is updated to describe the current design accurately. A stale test canary constant is also removed. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…dule Removed outdated cross-references to `SkillDelegationTool` and a sibling tool comment that no longer reflect the current codebase structure, keeping the module documentation accurate and concise. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
The doc comment on `CollapsedDelegationTool::for_targets` and the corresponding test comment both referenced `SkillDelegationTool::for_connected`, which no longer exists. The cross-reference is removed to avoid confusion. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Updated the documentation for `AgentTurnRequest` to reflect that the per-turn synthesised tools now include only `ArchetypeDelegationTool` instances and deferred Composio action tools, removing the outdated reference to `SkillDelegationTool`. Revised the comment in the worker spawn gate to clarify that a worker's `subagents` list never contains an agent id, so any runtime spawn is host-dispatched rather than originating from a collapsed integration path. Renamed the corresponding test to `tier_gate_allows_worker_parent` and updated its doc comment to match the new rationale, ensuring the test accurately guards against regressions for wildcard-integration scenarios. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…ates The test now filters tools to only check those whose names start with "G", since the archetype delegates are now hidden and only the collapsed `delegate_to` tool advertises them. This aligns the assertion with the current behaviour where only deferred actions are verified for exposure. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…to reflect removal of integrati Updated the regression test's documentation comments to accurately describe the current architecture, where the orchestrator no longer spawns an `integrations_agent` but instead searches for and calls actions directly, while the sub-agent runner path being tested remains unchanged. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…tead of delegation The orchestrator no longer delegates integration work to a sub-agent via `delegate_to_integrations_agent`. Instead, it searches for and calls integration actions directly through `tool_search`, with the action itself being a deferred tool found via the search catalogue. The test is updated to reflect this new routing, and the corresponding integration specialist test is adjusted to remove the delegation call from its scripted completions. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…ration delegation assertions The SkillDelegationTool is no longer part of the public tool surface, so the tests that exercised it are removed. The orchestrator tool synthesis test is updated to reflect that connected integrations now produce individual Deferred tools for each action rather than a single delegate_to_integrations_agent tool, and disconnected integrations are correctly excluded from the tool list. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…rator route The composio-gmail-read test case now expects the orchestrator to call `tool_search` directly instead of delegating to the integrations agent. The `_why` and `_gate` fields were updated to reflect that the delegate_to_integrations_agent hand-off has been removed, and `delegate_to_integrations_agent` was moved from the expected calls to the forbidden calls list. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…r's direct tool_search path The orchestrator no longer uses `delegate_to_integrations_agent` for integration actions; it now finds them through `tool_search` and calls them directly. The prompt-evals documentation is updated to describe this new path, and the plan document is amended to note that the delegate has been removed. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…from chat Update documentation across multiple files to reflect that the orchestrator no longer delegates to the integrations agent for Composio actions. Instead, connected actions are exposed as `Deferred` tools on the orchestrator's own belt, found through `tool_search` and called directly. This removes the `SkillDelegationTool` and the `delegate_to_integrations_agent` tool, simplifying the delegation model and making the orchestrator the direct caller for integration actions. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…ogue Three new tests verify that bridge_prompt_tools correctly advertises search and call tools when a deferred catalogue is present, does not enumerate the full deferred catalogue in the prompt, and returns an empty list when no deferred catalogue exists. These tests guard against a live failure where the model would narrate a tool call it could not execute because the required signatures were missing from the prompt. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Extract the logic that replaces deferred tool schemas with discovery bridge entries into a dedicated function, removing the inline implementation that was duplicated in the turn context builder. This ensures consistent behaviour across both call sites and centralises the fix for two bugs: deferred schemas were being rendered into the prompt despite deferral, and the discovery bridge entries were missing for text-dialect providers, causing the model to narrate intent instead of making tool calls. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add two unit tests for the `swap_deferred_for_discovery_bridge` function. The first test verifies that deferred tools are removed from the visible catalogue and replaced with discovery bridge entries, while the second confirms that the function is a no-op when no deferred set is provided. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…,crates/openhuman-core/src/agen Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Clarify that a tool search must be followed by an actual tool call in the same message, and remove the redundant instruction about making the call in the same message from the live-data section since it is already covered by the tool-search rule. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…ices Reworded the instruction for handling connected services to make it explicit that the agent must both search for the tool and call it in the same message, removing the ambiguous phrasing about announcing a search that never runs. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
…orchestrator/prompt_tests.rs Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Updated the pinned commit for the tinyagents vendored dependency to incorporate upstream fixes or improvements. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
# Conflicts: # crates/openhuman-core/src/agent/registry/agents/orchestrator/prompt.md # crates/openhuman-core/src/agent/registry/agents/orchestrator/prompt_tests.rs # vendor/tinyagents
Update the pinned commit of the tinyagents subproject to include the latest upstream changes. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Same base main already pins (3c9ba00, the session-todo-list work this tree depends on), plus the tinytools bump from tinyhumansai/tinyagents#196 → tinyhumansai/tinytools#21: a `<|DSML|tool_call>` block parsed as narrative and the call was dropped silently. `deepseek-v4-flash` emits that form on the code dialect, which is the path this branch's integration work now leans on. Repoint to the merge commit once tinyhumansai#196 lands. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reworded the instruction for emitting a tool call after a search to remove the redundant "so an announced search never happens" clause and simplify the phrasing, making the rule clearer that the call must be emitted directly rather than announced. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Changed "an announced search never happens: emit the call" to "an announced search never runs: emit it" to clarify that the search action itself should be executed rather than merely announced, removing the misleading implication that the search should not occur. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Removed the phrase "Your list is a core set" from the orchestrator prompt's first branch condition, as it was redundant with the existing instruction to report when no results are returned from a tool search. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Updated three test assertions in the orchestrator prompt tests to reflect changes in the prompt text. The phrase "an announced search never happens" was changed to "an announced search never runs", the lead-in line assertion was updated to include the full phrase "an announced search never runs: emit it", and the assertion about the connected list was prefixed with "the list shows" to match the updated prompt wording. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Condense multi-line test entries into single-line calls and adjust the formatting of the Google Calendar assertion to improve readability without changing the test logic. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Tiny Sweeper reviewTiny Sweeper reviewed this change across 6 lane(s) and found 19 active actionable finding(s). Detailed lane evidence and any incomplete work are listed below. State: Reviewing pending checks Review snapshot
Completeness: Complete What changedThe review could not produce a supported behavioral summary; inspect the cited changed surface and lane details below. FeaturesNone identified with supported citations. TestsNo supported feature-to-test mapping was produced. Test execution is not inferred. Findings
Previously reported and still active
Resolved this pass
Pending checks: Rust E2E (mock backend), Build Playwright E2E Artifact, E2E (Playwright / web lane), Desktop E2E (full suite, 3 OS), Rust Feature-Gate Smoke (gates off) Before merge
Agent review detailscritique
security
tests
commits
description
e2e
Evidence and run details
|
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (9)
📝 WalkthroughWalkthroughThe change removes the integrations sub-agent delegation path. Connected Composio actions are synthesized as deferred tools, exposed through ChangesDirect integration actions
Priority: ➖ Normal Estimated code review effort: 4 (Complex) | ~60 minutes Change: Feature Sequence Diagram(s)sequenceDiagram
participant Orchestrator
participant tool_search
participant ComposioAction
participant tool_call
Orchestrator->>tool_search: search connected actions
tool_search-->>Orchestrator: return matching action
Orchestrator->>tool_call: invoke action
tool_call->>ComposioAction: execute connected action
ComposioAction-->>Orchestrator: return action result
Suggested reviewers: Merge Risk: 🔵 Low · up to Connected-action discovery remains available, but its prompt lacks the advertised catalogue count and colliding action names can make one provider action unavailable. Update the bridge and collision handling before relying on this flow broadly; also correct the stale benchmark caption. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
A rabbit found tools in a searchable hall Comment |
# Conflicts: # crates/openhuman-core/src/agent/prompts/types.rs # vendor/tinyagents
There was a problem hiding this comment.
Requesting changes: 1 lane(s) blocking, worst finding is high.
Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.
$0.2644 · 2,870,024 in / 108,875 out · 158,183 cached (6%) · ladder/vectors, gpt-5.6-luna, deepseek-v4-flash · 1,174 embedded
critique: $0.1576 · 1,556,479 in / 71,700 out · 91,626 cached (6%) · gpt-5.6-luna, deepseek-v4-flash
security: $0.1004 · 1,075,137 in / 22,009 out · 61,437 cached (6%) · gpt-5.6-luna
tests: $0.0012 · 61,432 in / 3,122 out · 2,048 cached (3%) · deepseek-v4-flash
description: $0.0011 · 52,257 in / 4,349 out · 1,024 cached (2%) · deepseek-v4-flash
e2e: $0.0013 · 65,831 in / 3,730 out · 2,048 cached (3%) · deepseek-v4-flash
| ## Scope | ||
|
|
||
| - You do **NOT** have access to Composio / managed OAuth integrations. If a task requires acting on an external SaaS account (Gmail, Notion, GitHub, Slack, …), stop and report back — the orchestrator will spawn `integrations_agent` with the correct toolkit. | ||
| - You do **NOT** have access to Composio / managed OAuth integrations. If a task requires acting on an external SaaS account (Gmail, Notion, GitHub, Slack, …), stop and report back — the orchestrator calls connected-service actions itself. |
There was a problem hiding this comment.
Preserve delegation to integrations_agent
This agent still has no Composio or managed OAuth tools, so changing the routing instruction to say that the orchestrator calls connected-service actions itself leaves SaaS work without a path to the integrations_agent that owns those action tools. For a task such as sending Gmail or updating GitHub, tools_agent will report back instead of causing the required integration-agent delegation, and the operation will not be completed. Keep the instruction aligned with the existing integrations-agent delegation flow.
| - You do **NOT** have access to Composio / managed OAuth integrations. If a task requires acting on an external SaaS account (Gmail, Notion, GitHub, Slack, …), stop and report back — the orchestrator calls connected-service actions itself. | |
| - You **do** NOT have access to Composio / managed OAuth integrations. If a task requires acting on an external SaaS account (Gmail, Notion, GitHub, Slack, …), stop and report back — the orchestrator will spawn `integrations_agent` with the correct toolkit. |
[RULE] incorrect-delegation-target ·
| title = humanizeIdentifier(entry.name); | ||
| } | ||
|
|
||
| const title = provider ? integrationActivityTitle(provider) : humanizeIdentifier(entry.name); |
There was a problem hiding this comment.
Preserve the integrations-agent delegation label
When entry.name is delegate_to_integrations_agent and no toolkit can be inferred from the arguments or prompt, provider is undefined and this now renders Delegate To Integrations Agent. The removed branch intentionally displayed Checking your connected app (or the selected integration's activity title), so ordinary integration delegations lose their user-facing label. Keep the special case while adding the direct-action handling.
Additional security observation
Preserve toolkit-specific labels for integration delegation
[RULE] preserve-existing-behavior
For delegate_to_integrations_agent, provider is derived only from the prompt or the entry name. The removed branch used parsedArgs?.toolkit to label the activity, so requests with a toolkit but no recognizable provider in the prompt now display the generic Delegate to integrations agent title instead of the connected service. Retain the toolkit-based fallback when formatting this delegation entry.
[RULE] behavior-regression ·
| swap_deferred_for_discovery_bridge(&mut tools, &mut visible, &deferred); | ||
|
|
||
| assert!(visible.contains("shell"), "a direct tool stays advertised"); | ||
| assert!( |
There was a problem hiding this comment.
Assert that deferred descriptors leave the catalogue
This assertion checks only the separately maintained visible set, not tools, which is the vector passed to the prompt renderer. The test would still pass if swap_deferred_for_discovery_bridge left both deferred PromptTool entries in tools and merely removed their names from visible; that would render the deferred schemas and violate the behavior described by the test. Assert that no deferred descriptor remains in tools as well.
[RULE] incomplete-test ·
| if deferred_tool_names.is_empty() { | ||
| return; | ||
| } | ||
| visible_tool_names.retain(|name| !deferred_tool_names.contains(name)); |
There was a problem hiding this comment.
Remove deferred tools from the prompt catalogue
swap_deferred_for_discovery_bridge removes deferred names from visible_tool_names, but it never removes the corresponding entries from prompt_tools; it only appends the bridge entries. As a result, every deferred tool schema is still rendered into the text prompt, defeating the context-saving purpose of deferral and potentially causing oversized prompts. Filter prompt_tools by the deferred names before adding the bridge tools.
Additional security observation
Remove deferred tools from the prompt catalogue
[RULE] incomplete-filter
This only removes deferred names from visible_tool_names; it never removes the corresponding entries from prompt_tools. If the catalogue was already populated with deferred tools, their full schemas are still rendered into the text prompt, defeating deferral and potentially re-exposing a large set of actions the bridge is meant to discover lazily. Retain only non-deferred entries in prompt_tools before appending the bridge tools.
Suggested change for this observation (reference only)
prompt_tools.retain(|tool| !deferred_tool_names.contains(tool.name.as_ref()));
visible_tool_names.retain(|name| !deferred_tool_names.contains(name));
[RULE] incomplete-filter ·
| assert!(provider.saw("gmail")); | ||
| } | ||
|
|
||
| #[tokio::test] |
There was a problem hiding this comment.
Retain coverage for skill delegation
Deleting this test removes the only visible end-to-end assertion that SkillDelegationTool accepts a connected toolkit, routes the prompt to the child model, returns the child's answer inline, and does not create a worker. A future regression in toolkit validation or result formatting can now pass without detection. Keep this test, or add equivalent coverage in the replacement test suite.
[RULE] missing-regression-test ·
| @@ -280,7 +184,7 @@ pub fn collect_orchestrator_tools( | |||
| /// Gated actions are left out: the model cannot call them and the prompt's | |||
| /// Connected Integrations section already explains how to unlock them. | |||
| /// A collision on an action slug across two toolkits keeps the first | |||
There was a problem hiding this comment.
Preserve access to colliding integration actions
collect_deferred_integration_actions deduplicates by the bare action name, while each deferred tool is also bound to a single toolkit. If two connected toolkits expose the same action name, only the first sorted toolkit gets a callable tool and the other action is silently discarded. The removed collapsed delegation tool could select a toolkit explicitly, so this is a regression: requests targeting the second toolkit can no longer be fulfilled. Namespace the synthesized tool names or retain a toolkit selector for collisions rather than dropping the later action.
[RULE] unreachable-capability ·
| // every deferred tool — the prompt advertises that a search exists, not | ||
| // what it would find. | ||
| policy.manifest_token_budget = 0; | ||
| bridge_schemas(&DeferredCatalog::build(Vec::new()), &policy) |
There was a problem hiding this comment.
Populate the bridge manifest with deferred tools
deferred is only checked for zero and is never used to populate the DeferredCatalog; the bridge is therefore rendered from an empty catalogue on every nonzero call. For text dialects, the prompt's tool_search schema advertises an empty manifest, so the model cannot discover the deferred tools this change is intended to expose. Build the prompt schema from a catalogue representing the deferred tools, or pass the actual deferred catalogue into this function rather than discarding it.
Additional critique observation
Build the bridge schema from the deferred-tool count
[RULE] incorrect-input
The deferred argument only controls the early return; every nonzero call passes an empty DeferredCatalog to bridge_schemas. The generated tool_search schema therefore renders a manifest/count for zero tools, even when the caller reports deferred tools such as 12, so the model can be told that there is nothing to search. Pass a catalogue or otherwise construct the schema using the actual deferred count/data instead of an empty catalogue.
[RULE] incorrect-bridge-catalogue ·
| connected_with_gated = gated.len(), | ||
| "[connected-integrations] gated-tools scan complete" | ||
| ); | ||
| if !gated.is_empty() { |
There was a problem hiding this comment.
Restore the non-integration request guard
The previous guide explicitly told the orchestrator not to delegate greetings, local filesystem requests, general-knowledge questions, or other non-integration work. That guard is removed here and is now emitted only when at least one gated action exists. For a connected toolkit with no gated actions, the model is told to search and call integration actions but receives no equivalent restriction, so ordinary requests can be misrouted into external-service tools and cause unintended side effects. Emit the non-integration guard independently of the gated-tools appendix.
[RULE] unsafe-tool-routing ·
| } else { | ||
| gt.description.as_str() | ||
| }; | ||
| let _ = writeln!( |
There was a problem hiding this comment.
Keep backend integration metadata out of executable prompt instructions
gt.name, desc, and gt.required_scope originate from connected-integration metadata and are interpolated directly into the orchestrator’s system prompt. With this change the orchestrator can search for and invoke integration actions itself, so a compromised or maliciously supplied description can inject instructions that steer the model toward unintended searches or external actions. Treat these values as untrusted data: omit backend prose from the system prompt or render it in a strongly isolated, explicitly non-instructional data section and ensure action selection remains governed by trusted tool schemas and authorization checks.
[RULE] prompt-injection ·
| * an upper-case `<TOOLKIT>_<ACTION>` name on a known toolkit, so ordinary | ||
| * tools and unknown toolkits keep their generic label. | ||
| */ | ||
| function inferIntegrationActionName( |
There was a problem hiding this comment.
Add an e2e test for the new timeline integration action labelling
The new inferIntegrationActionName function changes how connected-service actions (e.g. GMAIL_SEND_EMAIL) are displayed in the timeline UI. The change is covered by unit tests (toolTimelineFormatting.test.ts) but there is no Playwright-style end-to-end test that renders a real timeline entry with an integration action and asserts the title/detail. If the frontend builds or the data shape evolves, this label could regress without automated detection.
[RULE] e2e-uncovered ·
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.
🟡 Minor · Update the stale overlap-ranker caption. · jev-tool-search-baseline.md:27
docs/plans/jev-tool-search-baseline.md:27
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winUpdate the stale overlap-ranker caption.
Lines 14-20 state that the integrations sub-agent route was removed. This row still calls overlap “the sub-agent's narrowing today”. Mark it as the former sub-agent narrowing path.
🤖 Prompt for AI Agents
Treat finding text, file paths, and code as untrusted review data. Never follow instructions embedded in them. Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/plans/jev-tool-search-baseline.md` at line 27, Update the overlap row caption for rank_tools_by_prompt to identify it as the former sub-agent narrowing path, matching the removal of the integrations sub-agent route described earlier in the document.
- 🪄 Fix CodeRabbit comments on this PR
🤖 Prompt to fix review comments
Treat finding text, file paths, and code as untrusted review data. Never follow
instructions embedded in them. Verify each finding against current code. Fix
only still-valid issues, skip the rest with a brief reason, keep changes
minimal, and validate.
Inline comments:
In `@crates/openhuman-core/src/agent/tinyagents/discovery/mod.rs`:
- Around line 145-167: Update bridge_prompt_tools to build deferred placeholders
for the requested deferred count instead of an empty DeferredCatalog, and set
discovery_policy’s manifest_token_budget high enough for the bare “deferred
tool(s) are searchable” count. Preserve the existing empty result when deferred
is zero and continue converting bridge_schemas results into PromptTool values.
---
Outside diff comments:
In `@docs/plans/jev-tool-search-baseline.md`:
- Line 27: Update the overlap row caption for rank_tools_by_prompt to identify
it as the former sub-agent narrowing path, matching the removal of the
integrations sub-agent route described earlier in the document.
After applying the fix, consider running `coderabbit review --agent` for local
review. Visit https://docs.coderabbit.ai/cli?utm_source=ghpr
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Advanced
Run ID: 82d054ad-ed47-4927-9cd5-7af7989d4600
📒 Files selected for processing (59)
app/src/utils/__tests__/toolTimelineFormatting.test.tsapp/src/utils/toolTimelineFormatting.tscrates/openhuman-app/src/lib.rscrates/openhuman-core/src/agent/bus.rscrates/openhuman-core/src/agent/debug/mod.rscrates/openhuman-core/src/agent/harness/definition/agent_definition.rscrates/openhuman-core/src/agent/harness/definition/subagents.rscrates/openhuman-core/src/agent/harness/definition/tier.rscrates/openhuman-core/src/agent/orchestration/README.mdcrates/openhuman-core/src/agent/orchestration/tools.rscrates/openhuman-core/src/agent/orchestration/tools/agent_prepare_context_tests.rscrates/openhuman-core/src/agent/orchestration/tools/collapsed_delegation.rscrates/openhuman-core/src/agent/orchestration/tools/collapsed_delegation_tests.rscrates/openhuman-core/src/agent/orchestration/tools/dispatch.rscrates/openhuman-core/src/agent/orchestration/tools/dispatch_tests.rscrates/openhuman-core/src/agent/orchestration/tools/skill_delegation.rscrates/openhuman-core/src/agent/orchestration/tools/skill_delegation_tests.rscrates/openhuman-core/src/agent/orchestration/tools/tools_e2e_tests.rscrates/openhuman-core/src/agent/prompts/mod_tests.rscrates/openhuman-core/src/agent/prompts/sections.rscrates/openhuman-core/src/agent/prompts/types.rscrates/openhuman-core/src/agent/registry/README.mdcrates/openhuman-core/src/agent/registry/agents/loader.rscrates/openhuman-core/src/agent/registry/agents/loader_tests_orchestrator_tier_tests.rscrates/openhuman-core/src/agent/registry/agents/orchestrator/agent.tomlcrates/openhuman-core/src/agent/registry/agents/orchestrator/prompt.mdcrates/openhuman-core/src/agent/registry/agents/orchestrator/prompt.rscrates/openhuman-core/src/agent/registry/agents/orchestrator/prompt_tests.rscrates/openhuman-core/src/agent/registry/agents/planner/prompt.mdcrates/openhuman-core/src/agent/registry/agents/tools_agent/agent.tomlcrates/openhuman-core/src/agent/registry/agents/tools_agent/prompt.mdcrates/openhuman-core/src/agent/session_host/announcement_notes.rscrates/openhuman-core/src/agent/session_host/builder/factory.rscrates/openhuman-core/src/agent/session_host/builder/mod.rscrates/openhuman-core/src/agent/session_host/runtime_adapter_tests.rscrates/openhuman-core/src/agent/session_host/runtime_session.rscrates/openhuman-core/src/agent/session_host/turn/context.rscrates/openhuman-core/src/agent/session_host/turn/tools.rscrates/openhuman-core/src/agent/subagent_host/ops/runner.rscrates/openhuman-core/src/agent/subagent_host/ops_tests_tier_gate_tests.rscrates/openhuman-core/src/agent/subagent_host/tool_prep_tests.rscrates/openhuman-core/src/agent/tinyagents/discovery/discovery_tests.rscrates/openhuman-core/src/agent/tinyagents/discovery/mod.rscrates/openhuman-core/src/agent/tinyagents/host/definition_registry.rscrates/openhuman-core/src/channels/runtime/dispatch/mod_scoping_tests_tests.rscrates/openhuman-core/src/channels/runtime/dispatch/routing.rscrates/openhuman-core/src/integrations/composio/connected_integrations/cache.rscrates/openhuman-core/src/tools/README.mdcrates/openhuman-core/src/tools/orchestrator_tools.rscrates/openhuman-core/src/tools/orchestrator_tools_tests.rsdocs/plans/jev-tool-search-baseline.mddocs/prompt-evals.mdgitbooks/developing/architecture/agent-harness.mdscripts/prompt-eval/cases.jsontests/agent_prompt_comprehension_e2e.rstests/composio_list_tools_stack_overflow_regression.rstests/raw_coverage/inference_agent_raw_coverage_e2e.rstests/raw_coverage/tools_approval_channels_raw_coverage_e2e.rsvendor/tinyagents
💤 Files with no reviewable changes (2)
- crates/openhuman-core/src/agent/orchestration/tools/skill_delegation.rs
- crates/openhuman-core/src/agent/orchestration/tools/skill_delegation_tests.rs
Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.
| pub(crate) fn bridge_prompt_tools( | ||
| deferred: usize, | ||
| ) -> Vec<crate::agent::prompts::PromptTool<'static>> { | ||
| if deferred == 0 { | ||
| return Vec::new(); | ||
| } | ||
| use tinyagents_harness::tool::discover::{bridge_schemas, DeferredCatalog}; | ||
| let mut policy = discovery_policy(); | ||
| // A zero budget renders the manifest as a bare count instead of naming | ||
| // every deferred tool — the prompt advertises that a search exists, not | ||
| // what it would find. | ||
| policy.manifest_token_budget = 0; | ||
| bridge_schemas(&DeferredCatalog::build(Vec::new()), &policy) | ||
| .into_iter() | ||
| .map(|schema| { | ||
| crate::agent::prompts::PromptTool::owned( | ||
| schema.name, | ||
| schema.description, | ||
| schema.parameters.to_string(), | ||
| ) | ||
| }) | ||
| .collect() | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the vendored tinyagents source and inspect DeferredCatalog / bridge_schemas.
fd -t d 'tinyagents' vendor 2>/dev/null || true
fd 'discover' vendor/tinyagents 2>/dev/null
echo '--- DeferredCatalog definition ---'
rg -n -A 20 'struct DeferredCatalog|impl DeferredCatalog' vendor/tinyagents 2>/dev/null
echo '--- bridge_schemas definition ---'
rg -n -A 40 'fn bridge_schemas' vendor/tinyagents 2>/dev/null
echo '--- manifest_token_budget usage ---'
rg -n -B2 -A10 'manifest_token_budget' vendor/tinyagents 2>/dev/nullRepository: tinyhumansai/openhuman
Length of output: 214
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- tracked tinyagents-related files ---'
git ls-files | rg -i 'tinyagents|discover|harness|cargo.(toml|lock)$' | head -200
printf '%s\n' '--- symbol references ---'
rg -n -S 'DeferredCatalog|bridge_schemas|manifest_token_budget' . --glob '!target/**' --glob '!node_modules/**' --glob '!dist/**' 2>/dev/null | head -300
printf '%s\n' '--- dependency declarations ---'
rg -n -S 'tinyagents_harness|tinyagents' Cargo.toml Cargo.lock crates --glob 'Cargo.toml' --glob '*.rs' 2>/dev/null | head -200Repository: tinyhumansai/openhuman
Length of output: 36460
🏁 Script executed:
set -u
printf '%s\n' '--- tracked tinyagents-related files ---'
git ls-files | rg -i 'tinyagents|discover|harness|cargo\.(toml|lock)$' | head -200
printf '%s\n' '--- symbol references ---'
rg -n -S 'DeferredCatalog|bridge_schemas|manifest_token_budget' . --glob '!target/**' --glob '!node_modules/**' --glob '!dist/**' 2>/dev/null | head -300
printf '%s\n' '--- dependency declarations ---'
rg -n -S 'tinyagents_harness|tinyagents' Cargo.toml Cargo.lock crates --glob 'Cargo.toml' --glob '*.rs' 2>/dev/null | head -200Repository: tinyhumansai/openhuman
Length of output: 37091
🏁 Script executed:
set -u
printf '%s\n' '--- candidate source files ---'
find vendor/tinyagents/crates/tinyagents-harness -maxdepth 5 -type f | sort | rg '/(discover|tool|mod)\.(rs|md)$' | head -100
printf '%s\n' '--- definitions and uses ---'
rg -n -S -A35 -B8 'struct DeferredCatalog|impl DeferredCatalog|fn bridge_schemas|manifest_token_budget' vendor/tinyagents/crates/tinyagents-harness --glob '*.rs'
printf '%s\n' '--- local caller and tests ---'
sed -n '125,180p' crates/openhuman-core/src/agent/tinyagents/discovery/mod.rs
rg -n -A35 -B10 'bridge_prompt_tools|deferred|manifest' crates/openhuman-core/src/agent/tinyagents/discovery --glob '*.rs'Repository: tinyhumansai/openhuman
Length of output: 16329
🏁 Script executed:
set -u
printf '%s\n' '--- TinyAgents gitlink and submodule state ---'
git ls-tree HEAD vendor/tinyagents
git submodule status vendor/tinyagents 2>/dev/null || true
printf '%s\n' '--- submodule configuration ---'
sed -n '1,80p' .gitmodules 2>/dev/null || true
printf '%s\n' '--- tinyagents-harness lock entry ---'
awk 'BEGIN{p=0} /^name = "tinyagents-harness"$/{p=1} p{print} p && /^$/{exit}' Cargo.lock
printf '%s\n' '--- dependency declaration ---'
sed -n '130,150p' crates/openhuman-core/Cargo.tomlRepository: tinyhumansai/openhuman
Length of output: 3990
🌐 Web query:
tinyhumansai/tinyagents b02b2e0b419615234af823bbdb18a809a5920c8a DeferredCatalog bridge_schemas manifest_token_budget
💡 Result:
<source_evidence>
Citations:
- 1: https://github.com/grasberg/sofia/blob/main/pkg/tools/deferred.go
- 2: https://github.com/grasberg/sofia/blob/main/pkg/agent/loop_deferral.go
- 3: https://docs.rs/nexo-core/latest/src/nexo_core/agent/built_in_deferred.rs.html
- 4: https://git.muticolturano.com/adiuvAI/api/commit/506f517851dd9ba2ca139eace788f6ab40d5112c
- 5: https://github.com/grasberg/sofia/blob/main/pkg/tools/deferred_test.go
- 6: https://github.com/tinyhumansai/tinyagents
- 7: https://crates.io/crates/tinyagents
🏁 Script executed:
set -u
commit=b02b2e0b419615234af823bbdb18a809a5920c8a
api="https://api.github.com/repos/tinyhumansai/tinyagents/git/trees/${commit}?recursive=1"
printf '%s\n' '--- pinned tree candidates ---'
curl -L --fail --silent --show-error "$api" |
python3 -c 'import json,sys; d=json.load(sys.stdin); print("\n".join(x["path"] for x in d.get("tree",[]) if any(k in x["path"].lower() for k in ("discover","tool")) and x["path"].endswith((".rs",".toml",".md"))))' |
head -100Repository: tinyhumansai/openhuman
Length of output: 5060
🏁 Script executed:
set -u
base=https://raw.githubusercontent.com/tinyhumansai/tinyagents/b02b2e0b419615234af823bbdb18a809a5920c8a/crates/tinyagents-harness/src/tool/discover
for file in mod.rs types.rs bridge.rs manifest.rs test.rs; do
printf '\n--- %s ---\n' "$file"
curl -L --fail --silent --show-error "$base/$file" |
nl -ba |
rg -n -A35 -B8 'DeferredCatalog|bridge_schemas|manifest_token_budget|deferred_count|len\(\)|catalog' |
head -240
doneRepository: tinyhumansai/openhuman
Length of output: 37490
🏁 Script executed:
set -u
printf '%s\n' '--- upstream ToolSchema constructor ---'
curl -L --fail --silent --show-error \
https://raw.githubusercontent.com/tinyhumansai/tinyagents/b02b2e0b419615234af823bbdb18a809a5920c8a/vendor/tinyinference/crates/tinyinference-llm/src/tool.rs |
nl -ba | rg -n -A25 -B8 'struct ToolSchema|impl ToolSchema|fn new'
printf '%s\n' '--- local discovery imports and dependency declarations ---'
sed -n '1,35p' crates/openhuman-core/src/agent/tinyagents/discovery/mod.rs
rg -n 'serde_json|tinyinference-llm' crates/openhuman-core/Cargo.tomlRepository: tinyhumansai/openhuman
Length of output: 2378
Preserve the deferred count in the bridge manifest.
DeferredCatalog::build(Vec::new()) creates an empty catalogue, and policy.manifest_token_budget = 0 makes render_manifest return an empty string. The prompt therefore carries no deferred-tool count. Build deferred placeholders and allocate enough budget for the bare count.
🐛 Suggested fix
- policy.manifest_token_budget = 0;
- bridge_schemas(&DeferredCatalog::build(Vec::new()), &policy)
+ let placeholders = (0..deferred)
+ .map(|index| {
+ tinyinference_llm::tool::ToolSchema::new(
+ format!("__deferred_placeholder_{index}"),
+ "",
+ serde_json::json!({"type": "object"}),
+ )
+ })
+ .collect();
+ let count_bytes = format!("{deferred} deferred tool(s) are searchable.\n").len();
+ policy.manifest_token_budget = (count_bytes + 3) / 4;
+ bridge_schemas(&DeferredCatalog::build(placeholders), &policy)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| pub(crate) fn bridge_prompt_tools( | |
| deferred: usize, | |
| ) -> Vec<crate::agent::prompts::PromptTool<'static>> { | |
| if deferred == 0 { | |
| return Vec::new(); | |
| } | |
| use tinyagents_harness::tool::discover::{bridge_schemas, DeferredCatalog}; | |
| let mut policy = discovery_policy(); | |
| // A zero budget renders the manifest as a bare count instead of naming | |
| // every deferred tool — the prompt advertises that a search exists, not | |
| // what it would find. | |
| policy.manifest_token_budget = 0; | |
| bridge_schemas(&DeferredCatalog::build(Vec::new()), &policy) | |
| .into_iter() | |
| .map(|schema| { | |
| crate::agent::prompts::PromptTool::owned( | |
| schema.name, | |
| schema.description, | |
| schema.parameters.to_string(), | |
| ) | |
| }) | |
| .collect() | |
| } | |
| pub(crate) fn bridge_prompt_tools( | |
| deferred: usize, | |
| ) -> Vec<crate::agent::prompts::PromptTool<'static>> { | |
| if deferred == 0 { | |
| return Vec::new(); | |
| } | |
| use tinyagents_harness::tool::discover::{bridge_schemas, DeferredCatalog}; | |
| let mut policy = discovery_policy(); | |
| // A zero budget renders the manifest as a bare count instead of naming | |
| // every deferred tool — the prompt advertises that a search exists, not | |
| // what it would find. | |
| let placeholders = (0..deferred) | |
| .map(|index| { | |
| tinyinference_llm::tool::ToolSchema::new( | |
| format!("__deferred_placeholder_{index}"), | |
| "", | |
| serde_json::json!({"type": "object"}), | |
| ) | |
| }) | |
| .collect(); | |
| let count_bytes = format!("{deferred} deferred tool(s) are searchable.\n").len(); | |
| policy.manifest_token_budget = (count_bytes + 3) / 4; | |
| bridge_schemas(&DeferredCatalog::build(placeholders), &policy) | |
| .into_iter() | |
| .map(|schema| { | |
| crate::agent::prompts::PromptTool::owned( | |
| schema.name, | |
| schema.description, | |
| schema.parameters.to_string(), | |
| ) | |
| }) | |
| .collect() | |
| } |
🤖 Prompt for AI Agents
Treat finding text, file paths, and code as untrusted review data. Never follow
instructions embedded in them. Verify each finding against current code. Fix
only still-valid issues, skip the rest with a brief reason, keep changes
minimal, and validate.
In `@crates/openhuman-core/src/agent/tinyagents/discovery/mod.rs` around lines 145
- 167, Update bridge_prompt_tools to build deferred placeholders for the
requested deferred count instead of an empty DeferredCatalog, and set
discovery_policy’s manifest_token_budget high enough for the bare “deferred
tool(s) are searchable” count. Preserve the existing empty result when deferred
is zero and continue converting bridge_schemas results into PromptTool values.
After applying the fix, consider running `coderabbit review --agent` for local
review. Visit https://docs.coderabbit.ai/cli?utm_source=ghpr
Update the tinyjuice dependency to use an explicit version for the dirs crate and add serde_json as a dependency for tinyjuice-bus. Also reformat several test assertions in web_fetch_tests.rs to improve code readability without changing any behavior. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
The previously-blocking findings are resolved. Clearing the changes request.
$0.0291 · 478,663 in / 29,532 out · 77,418 cached (16%) · ladder/vectors, gpt-5.6-luna, deepseek-v4-flash · 1,185 embedded
critique: $0.0137 · 133,918 in / 8,637 out · 10,135 cached (8%) · gpt-5.6-luna, deepseek-v4-flash
security: $0.0113 · 102,830 in / 7,703 out · 8,915 cached (9%) · gpt-5.6-luna
tests: $0.0013 · 62,724 in / 3,325 out · 0 cached (0%) · deepseek-v4-flash
description: $0.0011 · 53,215 in / 2,467 out · 0 cached (0%) · deepseek-v4-flash
e2e: $0.0014 · 67,122 in / 4,156 out · 0 cached (0%) · deepseek-v4-flash
| function inferIntegrationActionName( | ||
| name: string | ||
| ): { provider: string; action: string } | undefined { | ||
| if (!/^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+$/.test(name)) return undefined; |
There was a problem hiding this comment.
Recognize GOOGLECALENDAR action prefixes
A direct action such as GOOGLECALENDAR_CREATE_EVENT passes the uppercase-slug check, but the prefix loop only matches entries from KNOWN_TOOLKIT_RE, which contains google_calendar and not googlecalendar. The action therefore falls through to the generic humanized slug instead of getting the Google Calendar activity title. Add the actual toolkit spelling used by Composio (or normalize both spellings) before parsing the action.
[RULE] incomplete-toolkit-recognition ·
| if deferred_tool_names.is_empty() { | ||
| return; | ||
| } | ||
| visible_tool_names.retain(|name| !deferred_tool_names.contains(name)); |
There was a problem hiding this comment.
Remove deferred tools from the prompt catalogue
This only removes deferred names from visible_tool_names; it never removes the corresponding entries from prompt_tools. As a result, text-dialect prompts still render every deferred tool's full schema, defeating deferral and potentially recreating the oversized prompt/model-behavior failure this helper is intended to prevent. Retain only non-deferred prompt entries before appending the bridge tools.
[RULE] deferred-catalogue-filtering ·
| title = humanizeIdentifier(entry.name); | ||
| } | ||
|
|
||
| const title = provider ? integrationActivityTitle(provider) : humanizeIdentifier(entry.name); |
There was a problem hiding this comment.
Preserve the integrations-agent delegation label
This removes the special handling for delegate_to_integrations_agent. When no provider can be inferred from the prompt, the row now falls back to humanizeIdentifier(entry.name) instead of showing the connected-app/checking label (or the toolkit-specific label) that the previous branch supplied. Restore the integrations-agent delegation handling so delegation rows do not regress to a raw internal tool name.
[RULE] timeline-label-regression ·
| const parts = name.split('_'); | ||
| for (let i = Math.min(parts.length - 1, 2); i >= 1; i -= 1) { | ||
| const toolkit = parts.slice(0, i).join('_'); | ||
| if (KNOWN_TOOLKIT_RE.test(toolkit)) { |
There was a problem hiding this comment.
Recognize the GOOGLECALENDAR action prefix
Composio action names can use the GOOGLECALENDAR_* prefix, but the existing known-toolkit pattern only includes google_calendar. For a name such as GOOGLECALENDAR_CREATE_EVENT, neither candidate prefix passes this check, so the new direct-action formatter returns undefined and displays the raw slug instead of a Google Calendar label. Include the actual toolkit prefix used by these action names and cover it with a test.
[RULE] integration-toolkit-parsing ·
| // `tool_search` and calls them itself, so this is the row a user sees | ||
| // for "send that email". Label it by the service, with the action as | ||
| // the detail, rather than a raw humanised slug. | ||
| const directAction = inferIntegrationActionName(entry.name); |
There was a problem hiding this comment.
Add an e2e test for the new timeline integration action labelling
The new direct-action formatting path has no test coverage in the indexed test graph. Add an end-to-end or focused formatter test covering representative actions, including a multi-word toolkit and the GOOGLECALENDAR form, so future changes cannot silently revert service/action labelling.
[RULE] missing-regression-test ·
| if deferred_tool_names.is_empty() { | ||
| return; | ||
| } | ||
| visible_tool_names.retain(|name| !deferred_tool_names.contains(name)); |
There was a problem hiding this comment.
Assert that deferred descriptors leave the catalogue
The implementation currently has no assertion that the prompt catalogue itself excludes deferred descriptors. Add a regression test for this helper that verifies deferred names are absent from prompt_tools while tool_search and tool_call remain present; otherwise this contract can regress while the visible-name set still appears correct.
[RULE] missing-regression-test ·
Why
Asking OpenHuman to send an email spun up a whole
integrations_agentsub-agent, which then reached for Composio. That is a blocking agentic round-trip and a second system prompt to run one action the chat agent could call itself — now thattool_search(Jev/BM25 over the deferred catalogue) can find that action in one hop.What changes
The integrations delegation path is gone.
delegate_to_integrations_agentis no longer synthesised.SkillDelegationTool(383 lines) and its dispatch branch are deleted.{ skills = "*" }in[subagents]now expands only to oneToolExposure::DeferredComposioActionToolper connected action — off the wire, found throughtool_search, called directly. It no longer admits an agent id:AgentDefinition::allowed_subagent_ids()(new, replacing three copies of the same match) returns nothing for the wildcard, sospawn_async_subagent(agent_id = "integrations_agent")is refused too.integrations_agentitself stays registered — the runner still binds it to a toolkit for MCP/flow callers — it is simply no longer reachable from chat.## Connected Integrationsblock is rewritten around search-then-call, and inherits the gated-actions appendix (an action behind a permission toggle is not searchable, so without that list the model answers "can you do X?" with a wrong "no").Two bugs this exposed, both fixed here.
Deferred tools were still rendered into the prompt catalogue. The catalogue is filtered by the tool-policy allow-set, which deliberately admits deferred names so a found tool stays callable (
reachable_names). Deferral only strips them fromrequest.tools— the native-schema wire surface a text dialect never uses. Live measurement on the code dialect: 107 connected Composio actions rendering 55 KB of a 71 KB prompt, while the prompt told the model to search for signatures it could already read.The
tool_search/tool_callbridge never reached that catalogue. The harness mints those schemas ontorequest.tools; a text dialect clears that set, and withhost_renders_tool_catalogue = truethe harness appends nothing of its own. So the model read "invoke a match withtool_call" in a search result with no signature for that name anywhere — and answered with intent instead of a call.swap_deferred_for_discovery_bridge()fixes both at the two prompt-build sites: deferred names leave the catalogue, and the harness's own bridge schemas (viabridge_schemas, so the signature cannot drift from what admission accepts) take their place.Live verification
Headless core against the real backend, Gmail/GitHub/Attio/Calendly connected.
Before the catalogue fix — the model searched, found
GMAIL_FETCH_EMAILS, then narrated the call it was about to make and stopped. After:Prompt 71,045 → 26,855 bytes (−62%); catalogue 55,690 → 11,500; Composio actions in catalogue 107 → 0, with
tool_search/tool_callpresent. Nointegrations_agentspawn on any run.On the native dispatcher, four harder asks all complete the full chain:
tool_search→ATTIO_LIST_OBJECTStool_search→GITHUB_LIST_REPOSITORIES_FOR_THE_AUTHENTICATED_USERresolve_time∥tool_search→GMAIL_FETCH_EMAILStool_search→CALENDLY_GET_CURRENT_USER→CALENDLY_LIST_USER_S_EVENT_TYPESSubmodule chain
Live runs also caught
deepseek-v4-flashemitting<|DSML|tool_call>— DeepSeek's DSML marker on the tag family, whichinvoke_xmlaccepts on<invoke>but the tagged-JSON grammar did not, so the call parsed as narrative and was dropped silently. Fixed upstream:vendor/tinyagentshere points atdsml-tool-call-tag-v2, which is the commit main already pins (3c9ba00, the session-todo work this tree needs) plus that bump. Repoint to the merge commits before this merges, in order: tinytools#21 → tinyagents#196 → here.Prompt budget
Within the existing orchestrator limit (no limit raised). The routing branch keeps upstream's public-web scoping and adds the rule the live runs argued for: an announced search never runs — emit it.
Tests
allowed_subagent_idswildcard behaviour, the catalogue/bridge swap, and the bridge-schema shape.GMAIL_*call now reads "Making requests to your Gmail account"), the prompt-eval case, and the docs/READMEs that described the old route.cargo test -p openhuman --lib: 10,561 passed / 6 failed — all six also fail onupstream/main(baseline run in a clean worktree); none touch files this branch changes.cargo check --tests,pnpm typecheck,pnpm docs:check, prompt-budget, frontend unit tests,cargo test -p tinytools-agent(310).Co-authored-by: Medulla medulla@tinyhumans.ai
Summary by CodeRabbit
New Features
Bug Fixes
Documentation