mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
feat: improve sub-agent tooling, conversation timeline UX, and Tauri setup (#646)
* refactor(tauri): update development scripts and configuration for CEF integration - Modified `package.json` scripts to consistently export `CEF_PATH` for all `cargo tauri` commands, ensuring a unified CEF binary distribution location. - Removed the overlay window configuration from `tauri.conf.json` and updated related Rust functions to reflect this change, while retaining helper functions for potential future use. - Updated documentation in `install.md` to clarify the importance of setting `CEF_PATH` for consistent CEF integration across builds. - Enhanced the `ensure-tauri-cli.sh` script to set `CEF_PATH` and ensure proper installation of the vendored CEF-aware `tauri-cli`. These changes streamline the development workflow and improve the reliability of CEF integration in the application. * refactor(release): enhance macOS signing script for nested frameworks and helper apps - Introduced a new `codesign_hardened` function to streamline the signing process with consistent options. - Improved the signing logic for nested frameworks and helper applications, ensuring all binaries are signed correctly. - Updated output messages for better clarity during the signing process, including detailed listings of bundle contents. - Disabled the summarizer payload threshold in the configuration to prevent recursive invocations until the issue is resolved. These changes improve the reliability and maintainability of the macOS signing and notarization workflow. * feat(orchestrator): add current_time tool and enhance agent capabilities - Introduced the `current_time` tool to provide the current date and time in UTC and local time zones, facilitating scheduling and reminders. - Updated `agent.toml` to include new tools: `current_time`, `cron_add`, `cron_list`, `cron_remove`, and `schedule`, enhancing the orchestrator's functionality. - Expanded documentation in `prompt.md` to guide users on utilizing the new direct tools effectively. These changes improve the orchestrator's ability to handle time-related queries and scheduling tasks directly, enhancing user experience. * feat(gmail): implement post-processing for Gmail responses to convert HTML to markdown - Added a new `post_process` module specifically for Gmail, which modifies action responses to convert HTML content into markdown format, improving usability and reducing context token usage. - Enhanced the `ComposioProvider` trait with a `post_process_action_result` method to allow providers to handle response modifications. - Introduced a `post_process` function that checks for a `raw_html` flag in the arguments to determine whether to apply the conversion. - Implemented tests to validate the HTML detection and conversion logic, ensuring the integrity of the post-processing functionality. These changes enhance the handling of Gmail responses, making them more suitable for further processing and display in the application. * refactor(gmail): improve HTML to markdown conversion and tool result budget - Enhanced the `post_process` module for Gmail to handle large HTML payloads more efficiently, implementing a fallback mechanism for oversized content. - Updated the `DEFAULT_TOOL_RESULT_BUDGET_BYTES` to `0`, disabling the budget temporarily while reworking the oversized-output path. - Refined the `extract_markdown_body` function to better manage HTML content, ensuring cleaner markdown output and improved performance. - Added utility functions for stripping HTML noise and handling large email bodies, enhancing the overall robustness of the email processing logic. These changes optimize the handling of Gmail responses, improving usability and performance in processing large HTML content. * feat(thread): implement thread title generation from user and assistant messages - Added a new `generateTitleIfNeeded` function in the `threadApi` to create a thread title based on the first user message and the assistant's reply. - Introduced a new `GenerateConversationThreadTitleRequest` struct to handle requests for title generation. - Updated the `ChatRuntimeProvider` to dispatch the title generation action after processing inference responses. - Enhanced the `threadSlice` with a new async thunk for generating thread titles, ensuring proper error handling and thread loading. - Added tests for the new title generation functionality to validate the integration with the threads RPC. These changes improve the user experience by automatically generating relevant thread titles, enhancing the organization of conversations. * feat(conversations): add thread title update functionality - Implemented `update_thread_title` method in `ConversationStore` to allow updating the title of existing conversation threads. - Added a corresponding public function `update_thread_title` for external access. - Enhanced tests to verify that thread titles are correctly updated and persisted in the store. These changes improve the management of conversation threads by enabling dynamic title updates, enhancing user experience and organization. * feat(docs): add comprehensive agent and subagent tool flow documentation - Introduced a new document detailing the runtime flow of the agent harness, including execution paths for main agents and tools. - Explained the differences between typed and fork subagents, and provided guidance for debugging harness and delegation issues. - Included a file map outlining key components and their roles within the Rust implementation. - Added a flow diagram to visually represent the interaction between agents, tools, and subagents. These changes enhance the understanding of the agent architecture and improve the documentation for developers working with the system. * feat(subagent): implement extraction tool and handoff cache for oversized results - Introduced `extract_from_result` tool to allow targeted queries against oversized tool outputs, improving efficiency by directly interacting with the extraction model. - Added `ResultHandoffCache` to manage oversized payloads, enabling progressive disclosure and reducing context length issues in sub-agent history. - Implemented hygiene helpers for cleaning tool outputs before caching, ensuring only relevant data is stored. - Enhanced the sub-agent runner with new modules for tool preparation and execution, streamlining the overall agent workflow. These changes enhance the sub-agent's ability to handle large tool results effectively, improving performance and user experience. * feat(conversations): enhance message bubble rendering and timeline entry formatting - Introduced new utility functions for parsing and rendering agent messages, including `splitAgentMessageIntoBubbles` and `parseMarkdownTable`, to improve the display of messages in conversation threads. - Implemented `BubbleMarkdown` and `TableCellMarkdown` components for better formatting of user and agent messages, ensuring consistent styling and interaction. - Enhanced the `formatTimelineEntry` function to provide clearer titles and details for tool timeline entries, improving the user experience during interactions with subagents. - Updated the `ChatRuntimeProvider` to utilize the new formatting functions, ensuring that tool timeline entries are displayed with relevant context and detail. These changes improve the overall presentation and usability of conversation messages and tool interactions, enhancing user engagement and clarity. * feat(subagent): enforce restrictions on sub-agent spawning tools - Introduced a filter to prevent sub-agents from invoking their own spawning tools, specifically `spawn_subagent` and `delegate_*`, to avoid recursion issues and ensure proper delegation by the top-level orchestrator. - Updated the `is_subagent_spawn_tool` function to identify these tools and integrated checks in both `run_typed_mode` and `run_fork_mode` to maintain the integrity of the sub-agent execution environment. - Enhanced logging to track the removal of restricted tools from the sub-agent's tool surface, improving observability and debugging capabilities. These changes strengthen the sub-agent architecture by enforcing strict boundaries on tool invocation, enhancing stability and performance. * feat(conversations): add ToolTimelineBlock component and enhance timeline entry formatting - Introduced the `ToolTimelineBlock` component to display tool timeline entries with improved formatting and user interaction, including auto-expansion for running entries. - Enhanced the `formatTimelineEntry` function to include user-friendly titles for specific tool actions, such as 'Viewing your Integrations'. - Updated the rendering logic in the `Conversations` component to filter and display visible messages more effectively, improving user experience during conversations. These changes enhance the clarity and usability of tool interactions within conversation threads, providing users with better context and engagement. * refactor(conversations): streamline component imports and enhance formatting consistency - Consolidated import statements in `Conversations.tsx` and `ChatRuntimeProvider.tsx` for improved readability. - Refactored the `ToolTimelineBlock` component to simplify its props structure. - Enhanced formatting consistency in the rendering logic of agent message bubbles and timeline entries, ensuring cleaner code and better maintainability. - Updated test cases for `splitAgentMessageIntoBubbles` and `formatTimelineEntry` to reflect formatting changes and ensure accuracy. These changes improve code clarity and maintainability while enhancing the overall user experience in conversation threads. * fix: satisfy pre-push lint on fix/tauri * fix(chat): refresh usage counters after responses * refactor(ChatRuntimeProvider): remove redundant import of requestUsageRefresh - Eliminated the duplicate import statement for `requestUsageRefresh` in `ChatRuntimeProvider.tsx`, streamlining the code for better readability and maintainability. * fix(chat): satisfy pre-push checks * refactor(conversations): improve error handling and remove unused title generation logic - Removed the unused `generateThreadTitleIfNeeded` function call from the `Conversations` component, simplifying the message dispatch logic. - Enhanced error handling during message dispatch by using `unwrap()` to catch and log errors, providing clearer feedback on send failures. - Updated the `ChatRuntimeProvider` to ensure proper error logging when generating thread titles, improving observability of issues related to title generation. These changes streamline the conversation handling process and improve the robustness of error management in the chat system. * refactor(conversations): improve formatting of title redaction and streamline test assertions - Reformatted the `redact_title_for_log` function for better readability by adjusting the formatting of the output string. - Simplified the assertion in the timezone test to enhance clarity and maintainability. These changes contribute to cleaner code and improved test structure in the conversations module. * refactor(tokenjuice): sort fact parts for improved formatting in inline summary - Modified the `format_inline` function to sort the fact parts before generating the summary string. This change enhances the readability and consistency of the output by ensuring that facts are presented in a stable order. These changes contribute to better formatted summaries in the token juice module. * fix: address remaining CodeRabbit review comments * refactor: streamline error handling and improve code readability - Updated error handling in prompt loading to use `std::io::Error::other` for better clarity. - Simplified conditional checks using `is_none_or` and `is_some_and` for improved readability. - Refactored string trimming logic to utilize array syntax for better clarity. - Enhanced default implementations for several structs to reduce boilerplate code. These changes contribute to cleaner code and improved maintainability across various modules. * refactor: improve code formatting and structure - Adjusted formatting in `post_process.rs` for better readability by aligning the conditional block. - Combined derive attributes in `tools.rs` for the `ComputerControlConfig` struct to reduce redundancy. - Streamlined entry retrieval in `compatible.rs` by condensing multiple lines into a single line for clarity. These changes enhance code readability and maintainability across the affected modules.
This commit is contained in:
@@ -0,0 +1,657 @@
|
||||
# Agent / Subagent / Tool Flow
|
||||
|
||||
This document explains the current runtime flow around the agent harness, with emphasis on:
|
||||
|
||||
- how the main agent turn executes
|
||||
- how tools are exposed and executed
|
||||
- how `spawn_subagent` works
|
||||
- how typed vs fork subagents differ
|
||||
- where to look when debugging harness and delegation issues
|
||||
|
||||
Scope: current Rust implementation under `src/openhuman/agent/` and `src/openhuman/tools/`.
|
||||
|
||||
## Why This Exists
|
||||
|
||||
The code path is split across several layers:
|
||||
|
||||
- built-in agent definitions in `src/openhuman/agent/agents/`
|
||||
- harness data + task-local plumbing in `src/openhuman/agent/harness/`
|
||||
- main session lifecycle in `src/openhuman/agent/harness/session/`
|
||||
- delegation tools in `src/openhuman/tools/impl/agent/`
|
||||
- synthesised `delegate_*` tools in `src/openhuman/tools/orchestrator_tools.rs`
|
||||
|
||||
If you only read one file, the system looks simpler than it is. The actual runtime path crosses all of them.
|
||||
|
||||
## File Map
|
||||
|
||||
### Registry and definitions
|
||||
|
||||
- `src/openhuman/agent/agents/loader.rs`
|
||||
Loads built-in agents from `agent.toml` plus dynamic `prompt.rs` builders.
|
||||
- `src/openhuman/agent/harness/definition.rs`
|
||||
Defines `AgentDefinition`, `ToolScope`, `SubagentEntry`, `PromptSource`, and registry-facing data.
|
||||
- `src/openhuman/agent/harness/mod.rs`
|
||||
Re-exports the harness entrypoints.
|
||||
|
||||
### Main agent session
|
||||
|
||||
- `src/openhuman/agent/harness/session/builder.rs`
|
||||
Builds an `Agent`, chooses dispatcher, applies visible-tool filtering, synthesises delegation tools.
|
||||
- `src/openhuman/agent/harness/session/turn.rs`
|
||||
Main turn lifecycle, tool execution, parent/fork context setup, transcript persistence, post-turn hooks.
|
||||
|
||||
### Subagent path
|
||||
|
||||
- `src/openhuman/tools/impl/agent/spawn_subagent.rs`
|
||||
Runtime tool entrypoint for explicit subagent spawns.
|
||||
- `src/openhuman/agent/harness/fork_context.rs`
|
||||
Task-local parent and fork context.
|
||||
- `src/openhuman/agent/harness/subagent_runner.rs`
|
||||
Typed/fork subagent execution, inner loop, tool filtering, transcript writes, large-result handoff.
|
||||
|
||||
### Generic tool loop / bus path
|
||||
|
||||
- `src/openhuman/agent/harness/tool_loop.rs`
|
||||
Shared LLM -> tool -> tool result -> LLM loop used by the bus handler and legacy call sites.
|
||||
- `src/openhuman/agent/bus.rs`
|
||||
Native event-bus entrypoint `agent.run_turn`.
|
||||
|
||||
## High-Level Model
|
||||
|
||||
There are two related but distinct execution tiers:
|
||||
|
||||
1. `Agent::turn`
|
||||
This is the stateful session runtime. It owns conversation history, system prompt reuse, memory loading, hooks, transcript resume, and the parent context needed for subagents.
|
||||
|
||||
2. `run_subagent`
|
||||
This is an isolated delegated run. It does not become a nested full `Agent` session. It runs a smaller inner loop and returns a single compact text result to the parent as a normal tool result.
|
||||
|
||||
That distinction matters when debugging. A subagent is not a second copy of the full session runtime.
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
### Full parent -> tool -> subagent flow
|
||||
|
||||
```text
|
||||
User message
|
||||
|
|
||||
v
|
||||
+---------------------------+
|
||||
| Agent::turn |
|
||||
| session/turn.rs |
|
||||
+---------------------------+
|
||||
|
|
||||
| 1. resume transcript if present
|
||||
| 2. build/reuse system prompt
|
||||
| 3. load memory context
|
||||
| 4. install ParentExecutionContext task-local
|
||||
v
|
||||
+---------------------------+
|
||||
| Parent iteration loop |
|
||||
| provider call |
|
||||
+---------------------------+
|
||||
|
|
||||
| provider response
|
||||
v
|
||||
+---------------------------+
|
||||
| Parse tool calls |
|
||||
| dispatcher + parser |
|
||||
+---------------------------+
|
||||
|
|
||||
+-------------------------------+
|
||||
| no tool calls |
|
||||
| |
|
||||
v |
|
||||
+---------------------------+ |
|
||||
| Final assistant text | |
|
||||
| appended to parent history| |
|
||||
+---------------------------+ |
|
||||
| |
|
||||
v |
|
||||
Return to caller |
|
||||
|
|
||||
| has tool calls
|
||||
v
|
||||
+---------------------------+
|
||||
| Execute tool calls |
|
||||
| parent tool runtime |
|
||||
+---------------------------+
|
||||
|
|
||||
+-------------------+-------------------+
|
||||
| |
|
||||
| regular tool | spawn_subagent
|
||||
v v
|
||||
+---------------------------+ +---------------------------+
|
||||
| Tool::execute(...) | | SpawnSubagentTool |
|
||||
+---------------------------+ | impl/agent/ |
|
||||
| | spawn_subagent.rs |
|
||||
| result +---------------------------+
|
||||
v |
|
||||
+---------------------------+ | validate args
|
||||
| append tool result | | lookup AgentDefinition
|
||||
| to parent history | | publish spawn event
|
||||
+---------------------------+ v
|
||||
| +---------------------------+
|
||||
+-------------------------->| run_subagent(...) |
|
||||
| subagent_runner.rs |
|
||||
+---------------------------+
|
||||
|
|
||||
+-------------------------+-------------------------+
|
||||
| |
|
||||
| typed mode | fork mode
|
||||
v v
|
||||
+---------------------------+ +---------------------------+
|
||||
| run_typed_mode | | run_fork_mode |
|
||||
| - resolve model | | - require ForkContext |
|
||||
| - filter tools | | - replay parent prefix |
|
||||
| - build narrow prompt | | - reuse parent tool specs |
|
||||
+---------------------------+ +---------------------------+
|
||||
| |
|
||||
+-------------------------+-------------------------+
|
||||
|
|
||||
v
|
||||
+---------------------------+
|
||||
| run_inner_loop |
|
||||
| subagent private loop |
|
||||
+---------------------------+
|
||||
|
|
||||
+---------------------------+---------------------------+
|
||||
| |
|
||||
| no tool calls | tool calls
|
||||
v v
|
||||
+---------------------------+ +---------------------------+
|
||||
| final child text | | child executes allowed |
|
||||
| returned to parent tool | | tools, appends results, |
|
||||
+---------------------------+ | loops again |
|
||||
+---------------------------+
|
||||
|
|
||||
v
|
||||
+---------------------------+
|
||||
| SpawnSubagentTool returns |
|
||||
| ToolResult(output) |
|
||||
+---------------------------+
|
||||
|
|
||||
v
|
||||
+---------------------------+
|
||||
| parent appends tool |
|
||||
| result to history |
|
||||
+---------------------------+
|
||||
|
|
||||
v
|
||||
+---------------------------+
|
||||
| next parent iteration |
|
||||
| synthesizes final answer |
|
||||
+---------------------------+
|
||||
```
|
||||
|
||||
### Context wiring for subagents
|
||||
|
||||
```text
|
||||
Agent::turn
|
||||
|
|
||||
+--> build ParentExecutionContext
|
||||
| - provider
|
||||
| - all_tools / all_tool_specs
|
||||
| - model / temperature
|
||||
| - memory / memory_context
|
||||
| - connected_integrations
|
||||
| - composio_client
|
||||
| - tool_call_format
|
||||
| - session lineage
|
||||
|
|
||||
+--> with_parent_context(...)
|
||||
|
|
||||
+--> any tool call inside this turn can read current_parent()
|
||||
|
|
||||
+--> SpawnSubagentTool
|
||||
|
|
||||
+--> run_subagent(...)
|
||||
|
|
||||
+--> typed mode uses ParentExecutionContext directly
|
||||
|
|
||||
+--> fork mode also requires current_fork()
|
||||
|
|
||||
+--> exact parent prompt + prefix replay
|
||||
```
|
||||
|
||||
## Startup and Registry Loading
|
||||
|
||||
Built-in agents live under `src/openhuman/agent/agents/*/` as:
|
||||
|
||||
- `agent.toml`
|
||||
- `prompt.rs`
|
||||
- optional `prompt.md` kept as nearby reference material
|
||||
|
||||
`loader.rs` parses each `agent.toml`, stamps the source as builtin, and installs the `prompt.rs` builder as `PromptSource::Dynamic`.
|
||||
|
||||
The global `AgentDefinitionRegistry` is initialized at startup. `spawn_subagent` depends on it. If the registry is missing, the tool returns a clear error instead of trying to run.
|
||||
|
||||
Important consequence: agent delegation is data-driven. The runtime does not hardcode an enum of built-in agents.
|
||||
|
||||
## How a Main Agent Session Is Built
|
||||
|
||||
`AgentBuilder::build` in `session/builder.rs` assembles:
|
||||
|
||||
- provider
|
||||
- full tool registry
|
||||
- visible tool specs
|
||||
- memory backend
|
||||
- prompt builder
|
||||
- dispatcher
|
||||
- context manager
|
||||
|
||||
Two tool sets exist at build time:
|
||||
|
||||
- full tool registry: what the runtime can execute
|
||||
- visible tool set: what the model can see in its schema/prompt
|
||||
|
||||
That split is intentional. The parent may have access to more runtime tools than it exposes directly to the model.
|
||||
|
||||
### Synthesised delegation tools
|
||||
|
||||
For agents with `subagents = [...]` in their definition, the builder synthesises `delegate_*` tools using `collect_orchestrator_tools()`:
|
||||
|
||||
- `SubagentEntry::AgentId("researcher")` becomes an `ArchetypeDelegationTool`
|
||||
- `SubagentEntry::Skills({ skills = "*" })` expands to one `SkillDelegationTool` per connected integration
|
||||
|
||||
These tools are added to the model-visible surface at build time. They are wrappers around delegation, not standalone business logic.
|
||||
|
||||
## Main Turn Flow
|
||||
|
||||
`Agent::turn` in `session/turn.rs` is the main harness path.
|
||||
|
||||
### 1. Transcript resume and prompt bootstrap
|
||||
|
||||
On a fresh session:
|
||||
|
||||
- it tries to resume a previous transcript for KV-cache reuse
|
||||
- fetches connected integrations
|
||||
- fetches learned context
|
||||
- builds the system prompt once
|
||||
- stores that system prompt as the first message
|
||||
|
||||
On later turns it deliberately does not rebuild the system prompt. Byte stability is treated as a runtime invariant for backend prefix caching.
|
||||
|
||||
### 2. Memory context injection
|
||||
|
||||
Per turn, it asks the memory loader for relevant context and prepends that context to the user message. This is parent-session behavior. Subagents do not run the same memory lookup path.
|
||||
|
||||
### 3. Parent execution context is captured
|
||||
|
||||
Before the loop starts, `Agent::turn` snapshots a `ParentExecutionContext` and installs it on the task-local via `with_parent_context(...)`.
|
||||
|
||||
That context carries the data subagents need:
|
||||
|
||||
- provider
|
||||
- all tools and tool specs
|
||||
- model / temperature
|
||||
- memory handle
|
||||
- loaded memory context
|
||||
- connected integrations
|
||||
- composio client
|
||||
- tool call format
|
||||
- session / transcript lineage
|
||||
|
||||
Without this task-local, `spawn_subagent` cannot work.
|
||||
|
||||
### 4. Iterative provider loop
|
||||
|
||||
For each iteration:
|
||||
|
||||
- context reduction runs first
|
||||
- the dispatcher converts history into provider messages
|
||||
- the provider is called
|
||||
- response text and tool calls are parsed
|
||||
- tool calls are executed
|
||||
- tool results are appended to history
|
||||
- the loop repeats until no tool calls remain
|
||||
|
||||
This is the full parent loop. It also emits progress events and drives post-turn hooks.
|
||||
|
||||
## Tool Execution in the Parent Loop
|
||||
|
||||
The parent loop special-cases delegation but otherwise treats tools generically.
|
||||
|
||||
Core behaviors:
|
||||
|
||||
- unknown or filtered-out tools become structured error results
|
||||
- `CliRpcOnly` tools are blocked in the autonomous loop
|
||||
- approval-gated tools can be denied before execution
|
||||
- successful outputs may be scrubbed / compacted / summarized
|
||||
|
||||
The parent’s history preserves:
|
||||
|
||||
- assistant tool call intent
|
||||
- tool results
|
||||
- final assistant response
|
||||
|
||||
That history format is what the next iteration reasons from.
|
||||
|
||||
## Where `spawn_subagent` Enters
|
||||
|
||||
The explicit delegation tool lives in `src/openhuman/tools/impl/agent/spawn_subagent.rs`.
|
||||
|
||||
Its flow is:
|
||||
|
||||
1. parse `agent_id`, `prompt`, optional `context`, optional `toolkit`, optional `mode`
|
||||
2. require the global `AgentDefinitionRegistry`
|
||||
3. resolve the target definition
|
||||
4. run pre-flight validation for `integrations_agent`
|
||||
5. publish `DomainEvent::SubagentSpawned`
|
||||
6. call `run_subagent(...)`
|
||||
7. publish completed or failed event
|
||||
8. return the subagent’s final text as a normal `ToolResult`
|
||||
|
||||
Important: the parent model never sees the subagent’s internal transcript. It only sees the final tool result string returned by `spawn_subagent`.
|
||||
|
||||
## Typed vs Fork Subagents
|
||||
|
||||
`run_subagent` chooses one of two modes.
|
||||
|
||||
### Typed mode
|
||||
|
||||
Default path. Implemented by `run_typed_mode(...)`.
|
||||
|
||||
Behavior:
|
||||
|
||||
- resolves model from the definition
|
||||
- filters the parent’s tools down to what the child is allowed to use
|
||||
- builds a fresh narrow system prompt
|
||||
- optionally injects inherited memory context
|
||||
- runs an isolated inner tool loop
|
||||
|
||||
This is the normal specialist-agent path.
|
||||
|
||||
### Fork mode
|
||||
|
||||
Optimization path. Implemented by `run_fork_mode(...)`.
|
||||
|
||||
Behavior:
|
||||
|
||||
- requires a `ForkContext` task-local
|
||||
- replays the parent’s exact rendered prompt and exact message prefix
|
||||
- reuses the parent’s tool schema snapshot
|
||||
- appends only the new fork task prompt
|
||||
- runs the same inner loop
|
||||
|
||||
This is for prefix-cache reuse, not for stricter isolation. It is deliberately byte-stable and closely coupled to the parent request shape.
|
||||
|
||||
## How Tool Filtering Works for Subagents
|
||||
|
||||
Typed subagents do not get a cloned tool registry. Instead the runner filters the parent’s tool list by index.
|
||||
|
||||
Filtering inputs:
|
||||
|
||||
- `definition.tools`
|
||||
- `definition.disallowed_tools`
|
||||
- `definition.skill_filter`
|
||||
- `SubagentRunOptions.skill_filter_override`
|
||||
- `definition.extra_tools`
|
||||
|
||||
Additional runtime rules:
|
||||
|
||||
- non-`welcome` subagents lose `complete_onboarding`
|
||||
- `tools_agent` strips Composio skill tools
|
||||
- `integrations_agent` with a bound toolkit may inject dynamic per-action Composio tools
|
||||
|
||||
The allowed tool names become both:
|
||||
|
||||
- the execution allowlist
|
||||
- the prompt-visible tool catalog
|
||||
|
||||
If the model emits a tool call outside that allowlist, the runner feeds back an error result and continues.
|
||||
|
||||
## Prompt Construction for Typed Subagents
|
||||
|
||||
Typed mode creates a `PromptContext` and then does one of:
|
||||
|
||||
- `PromptSource::Dynamic`: call the Rust prompt builder directly
|
||||
- `PromptSource::Inline` or `PromptSource::File`: load raw body, then wrap it with `render_subagent_system_prompt(...)`
|
||||
|
||||
Definition flags control which standard sections are omitted:
|
||||
|
||||
- `omit_identity`
|
||||
- `omit_memory_context`
|
||||
- `omit_safety_preamble`
|
||||
- `omit_skills_catalog`
|
||||
- `omit_profile`
|
||||
- `omit_memory_md`
|
||||
|
||||
This is one of the main token-saving levers in the harness.
|
||||
|
||||
## The Subagent Inner Loop
|
||||
|
||||
The actual delegated execution happens in `run_inner_loop(...)`.
|
||||
|
||||
It is a slimmed-down tool loop:
|
||||
|
||||
- call provider
|
||||
- parse tool calls
|
||||
- persist transcript after provider response
|
||||
- execute tools
|
||||
- append results
|
||||
- persist transcript again
|
||||
- stop on final text or max iterations
|
||||
|
||||
It returns:
|
||||
|
||||
- final output text
|
||||
- iteration count
|
||||
- aggregated usage
|
||||
|
||||
Unlike the parent `Agent::turn`, it does not own the broader session lifecycle.
|
||||
|
||||
## Integrations Agent Special Cases
|
||||
|
||||
`integrations_agent` is the trickiest subagent path.
|
||||
|
||||
### Toolkit gate in `spawn_subagent`
|
||||
|
||||
If `agent_id == "integrations_agent"`:
|
||||
|
||||
- `toolkit` is mandatory
|
||||
- the toolkit must exist in the allowlist
|
||||
- if it exists but is not connected, the tool returns a success message explaining that authorization is required
|
||||
|
||||
This is intentionally not always treated as a hard tool failure, because disconnected integrations are a user-facing state, not necessarily a runtime error.
|
||||
|
||||
### Text-mode override
|
||||
|
||||
In `run_inner_loop`, `integrations_agent` with tool specs forces text mode instead of native tool calling.
|
||||
|
||||
Why:
|
||||
|
||||
- large Composio JSON schemas can blow provider grammar/context limits
|
||||
|
||||
What changes:
|
||||
|
||||
- tool specs are omitted from the API payload
|
||||
- XML-style tool instructions are injected into the system prompt
|
||||
- the runner parses `<tool_call>...</tool_call>` blocks out of plain text
|
||||
- tool results in text mode are fed back as a user message containing `<tool_result>` tags
|
||||
|
||||
If a delegated integration run looks different from native-tool runs, this is usually why.
|
||||
|
||||
### Large result handoff cache
|
||||
|
||||
For toolkit-scoped `integrations_agent` runs, oversized tool results may be replaced by placeholders and stashed in an in-memory `ResultHandoffCache`.
|
||||
|
||||
The child can then call `extract_from_result(result_id, query)` to ask targeted follow-up questions against the cached payload.
|
||||
|
||||
This is not the same as generic payload summarization. It is a progressive-disclosure path specific to oversized delegated tool outputs.
|
||||
|
||||
## Parent -> Subagent -> Parent Result Shape
|
||||
|
||||
Conceptually the data flow is:
|
||||
|
||||
1. parent model emits `spawn_subagent(...)`
|
||||
2. tool runtime executes the delegated subagent loop
|
||||
3. subagent finishes with one final text output
|
||||
4. `spawn_subagent` returns that text as its tool result
|
||||
5. parent history receives the tool result
|
||||
6. parent model gets another iteration and synthesizes the user-facing answer
|
||||
|
||||
The parent does not absorb the child’s internal reasoning trace or full message history. Only the compact final output crosses the boundary.
|
||||
|
||||
## Bus Path vs Session Path
|
||||
|
||||
There are two outer entrypoints to keep straight.
|
||||
|
||||
### `Agent::turn`
|
||||
|
||||
Used for full stateful sessions. This is the richer harness.
|
||||
|
||||
### `agent.run_turn` via `src/openhuman/agent/bus.rs`
|
||||
|
||||
This native event-bus handler calls `run_tool_call_loop(...)` directly using owned Rust payloads.
|
||||
|
||||
It supports:
|
||||
|
||||
- provider reuse
|
||||
- tool filtering
|
||||
- per-turn extra tools
|
||||
- progress streaming
|
||||
|
||||
But it does not create a full `Agent` session object. If you are debugging channel-dispatch behavior, this distinction matters.
|
||||
|
||||
## Debugging Checklist
|
||||
|
||||
### 1. Confirm which execution tier you are in
|
||||
|
||||
Ask first:
|
||||
|
||||
- full `Agent::turn` session?
|
||||
- bus `agent.run_turn` path?
|
||||
- explicit `spawn_subagent` tool?
|
||||
- synthesised `delegate_*` tool leading into `spawn_subagent`?
|
||||
|
||||
If you confuse these, logs will look contradictory.
|
||||
|
||||
### 2. Check registry state
|
||||
|
||||
If delegation fails very early, confirm:
|
||||
|
||||
- `AgentDefinitionRegistry::init_global(...)` ran at startup
|
||||
- the target agent id exists
|
||||
- workspace overrides did not shadow the expected built-in definition
|
||||
|
||||
### 3. Check task-local availability
|
||||
|
||||
If `run_subagent` errors with missing context:
|
||||
|
||||
- `NoParentContext` means the tool ran outside a parent turn
|
||||
- `NoForkContext` means fork mode was requested but the fork snapshot was never installed
|
||||
|
||||
These are wiring issues, not prompt issues.
|
||||
|
||||
### 4. Check tool visibility vs tool execution
|
||||
|
||||
A tool can exist in the parent registry but still be invisible to a child due to:
|
||||
|
||||
- named `ToolScope`
|
||||
- `disallowed_tools`
|
||||
- `skill_filter`
|
||||
- welcome-only stripping
|
||||
- toolkit narrowing
|
||||
|
||||
If the model says “Unknown tool” or “not available to this sub-agent”, inspect filtering first.
|
||||
|
||||
### 5. Check transcript artifacts
|
||||
|
||||
Subagents persist transcripts per iteration using the parent session lineage plus a child session key. This is useful for debugging partial runs and crashes during tool execution.
|
||||
|
||||
Parent sessions and subagents do not write identical transcript shapes, so compare like with like.
|
||||
|
||||
### 6. Check the provider mode
|
||||
|
||||
If tool calling is malformed, verify whether the run used:
|
||||
|
||||
- native tools
|
||||
- p-format / xml instructions
|
||||
- integrations-agent text mode
|
||||
|
||||
The parser and message shape differ.
|
||||
|
||||
## Useful Log Prefixes
|
||||
|
||||
These prefixes are the most useful grep anchors:
|
||||
|
||||
- `[agent_loop]`
|
||||
- `[agent]`
|
||||
- `[tool-loop]`
|
||||
- `[spawn_subagent]`
|
||||
- `[subagent_runner]`
|
||||
- `[subagent_runner:typed]`
|
||||
- `[subagent_runner:fork]`
|
||||
- `[subagent_runner:text-mode]`
|
||||
- `[subagent_runner:handoff]`
|
||||
- `[orchestrator_tools]`
|
||||
- `[agent::bus]`
|
||||
- `[transcript]`
|
||||
|
||||
## Best Existing Tests to Read First
|
||||
|
||||
For end-to-end harness behavior:
|
||||
|
||||
- `src/openhuman/agent/harness/session/tests.rs`
|
||||
- `turn_dispatches_spawn_subagent_through_full_path`
|
||||
- `turn_dispatches_spawn_subagent_in_fork_mode`
|
||||
|
||||
For runner behavior in isolation:
|
||||
|
||||
- `src/openhuman/agent/harness/subagent_runner.rs` tests
|
||||
- typed mode returns text
|
||||
- memory-context inclusion/omission
|
||||
- tool filtering
|
||||
- one-tool execution
|
||||
- blocked tool recovery
|
||||
- fork prefix replay
|
||||
- missing parent/fork context errors
|
||||
|
||||
For orchestration-tool synthesis:
|
||||
|
||||
- `src/openhuman/tools/orchestrator_tools.rs` tests
|
||||
|
||||
For generic parent loop behavior:
|
||||
|
||||
- `src/openhuman/agent/tests.rs`
|
||||
|
||||
## Common Failure Modes
|
||||
|
||||
### Subagent never starts
|
||||
|
||||
Usually one of:
|
||||
|
||||
- registry not initialized
|
||||
- invalid `agent_id`
|
||||
- missing parent context
|
||||
- missing fork context
|
||||
|
||||
### Subagent starts but cannot call expected tools
|
||||
|
||||
Usually one of:
|
||||
|
||||
- tool filtered out by definition scope
|
||||
- `skill_filter` or toolkit override narrowed too aggressively
|
||||
- tool is `CliRpcOnly`
|
||||
- dynamic integration tools were not injected because the toolkit/client state was missing
|
||||
|
||||
### Integrations agent behaves unlike other agents
|
||||
|
||||
Usually expected. It may be in text mode and may be using the oversized-result handoff cache.
|
||||
|
||||
### Parent seems to “lose” child reasoning
|
||||
|
||||
Expected. Only the child’s final output is returned to the parent. Internal child history stays isolated.
|
||||
|
||||
## Practical Mental Model
|
||||
|
||||
The safest mental model is:
|
||||
|
||||
- the parent session is the durable conversation runtime
|
||||
- tools are the execution boundary
|
||||
- subagents are tool implementations that happen to run their own mini LLM loop
|
||||
- fork mode is a cache-optimization path, not a different product feature
|
||||
- `integrations_agent` is a special delegated runtime with extra provider and payload safeguards
|
||||
|
||||
If you debug from that model, the current codebase makes much more sense.
|
||||
+6
-2
@@ -185,10 +185,14 @@ echo "$(cat openhuman-core-${VERSION}-${TARGET}.tar.gz.sha256) openhuman-core-$
|
||||
|
||||
The default runtime is **CEF** (bundled Chromium), which requires the **vendored CEF-aware `tauri-cli`** at `app/src-tauri/vendor/tauri-cef/crates/tauri-cli`. The stock `@tauri-apps/cli` does **not** know how to bundle the Chromium Embedded Framework into `OpenHuman.app/Contents/Frameworks/`, so a bundle produced by it panics at startup inside `cef::library_loader::LibraryLoader::new` with `No such file or directory`.
|
||||
|
||||
All `cargo tauri` scripts in `app/package.json` (`yarn dev:app`, `yarn macos:build:*`, etc.) run [`scripts/ensure-tauri-cli.sh`](../scripts/ensure-tauri-cli.sh) first, which installs the vendored CLI into `~/.cargo/bin/cargo-tauri` on first use. If you ever overwrite it (e.g. `npm i -g @tauri-apps/cli` or `cargo install tauri-cli`), re-run:
|
||||
All `cargo tauri` scripts in `app/package.json` (`yarn dev:app`, `yarn macos:build:*`, etc.) run [`scripts/ensure-tauri-cli.sh`](../scripts/ensure-tauri-cli.sh) first, which installs the vendored CLI into `~/.cargo/bin/cargo-tauri` on first use. Those scripts also `export CEF_PATH="$HOME/Library/Caches/tauri-cef"` so that **every** `cef-dll-sys` invocation — the main app's and the inner `cargo build` that `tauri-bundler`'s `build.rs` runs to produce the embedded `cef-helper` — resolves to the same CEF binary distribution. Without this, the embedded helper ends up with bindings from a *different* downloaded CEF than the framework loaded at runtime, and helper processes abort with `FATAL: CefApp_0_CToCpp called with invalid version -1`.
|
||||
|
||||
If you ever overwrite `cargo-tauri` (e.g. `npm i -g @tauri-apps/cli` or `cargo install tauri-cli`), or switch CEF versions, reinstall with `CEF_PATH` set and force a bundler rebuild (touch forces `tauri-bundler/build.rs` to recompile the embedded cef-helper):
|
||||
|
||||
```bash
|
||||
cargo install --locked --path app/src-tauri/vendor/tauri-cef/crates/tauri-cli
|
||||
export CEF_PATH="$HOME/Library/Caches/tauri-cef"
|
||||
touch app/src-tauri/vendor/tauri-cef/cef-helper/src/*.rs
|
||||
cargo install --force --locked --path app/src-tauri/vendor/tauri-cef/crates/tauri-cli
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user