Table of Contents

Agent as Tool

Last updated:

Agent-as-tool lets one agent call another exposed agent as a specialized helper. The caller receives a normal AI function. Invoking that function starts a separate run of the called agent.

Writing Assistant editor with instructions, Ollama model selection, and behavior controls.

Note

Agent as tool means exposing a specialist agent so another agent can call it like a tool for focused work.

Definition and discovery contract

Two definition properties control delegation:

  • AgentDefinition.IsExposedAsTool makes a custom agent eligible to be called.
  • AgentDefinition.AgentToolIds lists the exposed agent IDs a caller may use.

Both conditions are required. AgentOrchestrator resolves IDs only from the definitions visible to the current user and builds a reference only when the target still has IsExposedAsTool enabled. A missing, hidden, or no-longer-exposed target is omitted, and a stale cached instance is removed.

The build walk starts with the requested agent ID in a HashSet<string>. Repeated IDs, self-references, and cycles therefore stop at their first visit instead of expanding indefinitely. There is no separate numeric delegation-depth setting: the finite definition graph and this build guard bound construction.

Important

AgentAsToolProvider itself preserves the supplied ID order and does not remove duplicate IDs. Host editors should persist unique selections, and callers that construct the public provider directly should deduplicate their input.

Callable name and function contract

AgentAsToolProvider creates an AI function for each resolved ChatClientAgent. Oscova sets the function name by normalizing the agent display name: each run of characters outside ASCII letters and digits becomes _. The agent description remains the function description.

Agent saves reject normalized-name collisions between non-main agents whose user audiences overlap. This prevents two visible agents such as Release Notes and Release-Notes from both becoming Release_Notes.

The resulting function contract is:

  • Input: one required query string and one optional conversation string.
  • Output: the called agent's final AgentResponse.Text.
  • Cancellation: the function passes its cancellation token through to the called run.
  • Failure: child-run exceptions are not converted into a success string. They propagate through the caller's function-invocation pipeline.
  • Session: a repeated conversation name resumes that child conversation inside the same parent session. A missing or blank name uses the default conversation for that child.

Framework run-option AdditionalProperties from the current function-invocation context are copied into the child run. This lets host run metadata continue across the delegation boundary.

Conversation lifecycle

Each parent session stores one child session for every called agent and conversation name. The child agent ID is the stable identity. Conversation names are trimmed and compared without case sensitivity, so Research and research continue the same line of work.

The child session is saved only after a successful call. A failed or cancelled call leaves the last successful snapshot unchanged. If a framework caller invokes the provider without a parent session, every delegated call remains stateless and starts with a fresh child session.

The child keeps its configured compaction behavior. When compaction reduces its history, the reduced conversation is the history that remains available. Function invocation for this provider must stay serial because two calls must not mutate the same child session concurrently.

The Host presents stored child messages as read-only chat tabs after the parent run finishes. The tab shows user and assistant text, not the child's tool activity or private reasoning. Incognito parent chats keep the same continuity in memory, but their parent and child sessions are not written to disk.

Runtime flow

The host prepares an AgentBuildRequest with the requested definition, definitions visible to the user, model-client and context-provider factories, workspace resolution, and optional run middleware. AgentOrchestrator.GetOrCreateAgentAsync() then:

  1. Walks AgentToolIds and builds nested exposed dependencies before their parents.
  2. Builds every exposed agent from that agent's definition.
  3. Adds built exposed agents to AIAgentCollection.
  4. Builds the requested agent.
  5. Lets AgentAsToolProvider capture the already-built child instances and expose them as functions.

That last step is a snapshot. A provider does not perform a new collection lookup on every request. This is also important for public use: populate AIAgentCollection before constructing AgentAsToolProvider.

Called-agent runtime shape

The called agent does not inherit the caller's entire context. Oscova deliberately rebuilds a restricted sub-agent shape:

Concern Delegated behavior
Model and model options Uses the called definition's default model provider, or the app default, plus that provider's chat options.
Standing instruction Uses the called definition's system instruction and its resolved variables.
Ordinary tools Uses the called definition's allowlist and tool policies.
Nested agent tools Uses the called definition's AgentToolIds, subject to the same exposure and cycle rules.
Workspaces Resolves the called definition's own allowed directories.
Compaction Uses the called definition's compaction setting and reducer.
Local-only posture Inherits the initiating run's air gap and can tighten it with the called definition's IsLocalOnly. It cannot loosen the caller's posture.
Memory and skills Not attached to delegated sub-agent builds, even when enabled or assigned on the called definition.
Private notes, Library, task list, and extra request providers Attached only to the active requested agent, not the delegated build.

Calling that same definition directly makes it the active requested agent, so its enabled memory, assigned skills, and other active-agent providers apply normally.

When to use it

  • A support agent delegates billing lookup to a billing specialist.
  • A writing assistant asks a research agent to gather source notes.
  • A project coordinator asks a scheduler agent to prepare task instructions.
Caution

Avoid circular delegation patterns. The builder guard prevents infinite construction, but there is no global runtime delegation-depth counter. Each nested agent run receives its own function-loop limits, so do not use the construction guard as a runtime recursion policy.

Governance and failure limits

The caller's permission to use an agent tool is not permission to bypass the called agent's policies. AgentToolProvider or tool search resolves the called definition's ordinary tools independently:

  • Deny tools are omitted.
  • RequireApproval tools are also omitted for delegated runs because AsAIFunction() has no route to the interactive approval loop.
  • Air-gapped runs omit tools classified as network egress.
  • Default and auto-approved tools remain available when otherwise allowed.

When the host supplies agent-specific runtime middleware, it is installed on every built agent. Tool hooks, quotas, workspace validation, repeated-call refusal, and accounting therefore record the called agent's identity rather than treating its work as a tool call by the parent.

Every built agent also has the framework function loop capped at 40 iterations per request and three consecutive tool errors, with detailed exception text excluded from model-visible tool results. A nested child invocation is its own request and receives the same per-agent caps.

Caching and lifetimes

Exposed agents normally live in AgentOrchestrator.AIAgents and are reused across builds. The requested agent is cached only when it is not itself exposed, has no request customizers, and the request does not bypass the cache.

Run-specific general builder and options customizers apply only to the requested agent. The per-agent builder customizer applies to exposed agents as well. When present, exposed dependencies are rebuilt and replace their collection entries so host middleware matches the current run. Providers that already captured an earlier child keep that captured instance for their run.

Evict the affected agent or invalidate the agent cache after changing an exposed definition, provider, tools, context, or execution policy. Host save and settings flows perform the relevant invalidation. Integrations that use the public runtime types directly must do so themselves.