A sub-agent is a PluginSubAgent the runtime auto-wraps as a tool (e.g. weather_planner_agent). Use one when a task needs more than one tool call in sequence or its own prompt.
Build the inner tools the sub-agent will call
Sub-agents own a private tool list. Reuse plugin tools or define new ones in the same file.
import { tool, z, type PluginTool } from '@ixo/oracle-runtime';
function buildRecommendOutfitTool(): PluginTool {
return tool(
async (args) => {
const { temp_c, conditions } = z
.object({ temp_c: z.number(), conditions: z.string() })
.parse(args);
const wet = ['rain', 'snow'].some((k) => conditions.toLowerCase().includes(k));
const layer = temp_c >= 18 ? 'a t-shirt' : 'a jacket';
return `Wear ${layer}${wet ? ' and bring an umbrella' : ''}.`;
},
{
name: 'recommend_outfit',
description: 'Return a one-line outfit suggestion.',
schema: z.object({ temp_c: z.number(), conditions: z.string() }),
},
);
}Canonical source: weather-sub-agent.ts.
Define the PluginSubAgent
Give the sub-agent a name (the tool the main agent sees), a description that reads like a tool description, a systemPrompt, and its tools.
import type { PluginSubAgent } from '@ixo/oracle-runtime';
export function buildWeatherPlannerSubAgent(): PluginSubAgent {
return {
name: 'weather_planner_agent',
description:
'Combines a forecast lookup with an outfit recommendation. Use for "should I bring a jacket to X tomorrow?".',
systemPrompt: [
'You are the Weather Planner Agent.',
'1. Call get_weather_forecast with the city.',
'2. Pick the most relevant day.',
'3. Call recommend_outfit with that day\'s max temp and conditions.',
'4. Reply with ONE sentence combining forecast and advice.',
].join('\n'),
tools: [buildForecastTool(), buildRecommendOutfitTool()],
model: 'subagent',
forwardTools: true,
};
}systemPrompt and tools can both be functions of PluginContext for late binding. See buildWeatherPlannerSubAgent in weather-sub-agent.ts.
Return it from getSubAgents(ctx)
The runtime wraps each sub-agent as a tool named <name> and exposes it to the main agent.
import { OraclePlugin, type PluginContext, type PluginSubAgent } from '@ixo/oracle-runtime';
export class WeatherPlugin extends OraclePlugin {
override getSubAgents(ctx: PluginContext): PluginSubAgent[] {
return [buildWeatherPlannerSubAgent()];
}
}See getSubAgents in weather.plugin.ts.
Set forwardTools to surface inner work
By default, the main agent only sees the sub-agent's final string. Forward inner tool calls into the main chat so the UI renders them.
{
// ...
forwardTools: true, // forward all
// forwardTools: ['get_weather_forecast'], // or a specific list
// forwardTools: false, // or hide everything (default)
}Runtime-injected passthrough tools (e.g. memory CRUD) are never forwarded.
Use getRequestSubAgents when live state matters
Use the request-time variant when whether or how to build the sub-agent depends on the user, session, or state.
override getRequestSubAgents(rtCtx: RuntimeContext): PluginSubAgent[] {
// ReadonlyState's index signature types arbitrary keys as `unknown`,
// so narrow before reading `.length`.
const agActions = rtCtx.history.state.agActions;
if (!Array.isArray(agActions) || agActions.length === 0) return [];
return [buildActionAwareSubAgent(agActions)];
}state.agActions (live AG-UI actions) is the canonical request-time field — reading it through rtCtx.history.state returns unknown, so validate with Array.isArray(...) before use. Boot-time getSubAgents and request-time getRequestSubAgents outputs merge.
What to know before shipping
- The sub-agent's
namemust be unique across all loaded plugins — same namespace as regular tools. Prefix with the agent role (e.g.weather_planner_agent). modeldefaults to'subagent'. Use'main'for a top-tier model,'utility'for a cheaper one.- Sub-agent middleware (the
middlewaresfield) only runs inside the sub-agent's loop, not the main agent's chain. onComplete(result, ctx)fires after the last turn — use it for follow-up events.- If sub-agent init throws, the runtime logs and skips that contribution for that request; the rest of the agent build continues.