Delegate
Call a subagent as a tool from an orchestrator, with the result validated and transformed through a handoff.
Your orchestrator agent needs to call a subagent as a tool and trust what comes back. A delegate does that in one step: it wraps a handoff contract with an execution function, so when the orchestrating agent calls the tool, the subagent runs and the result is validated and transformed through the handoff automatically.
import { delegate } from "@use-crux/core/agent";
import { z } from "zod";
const researchDelegate = delegate({
id: "delegate-research",
argsSchema: z.object({ query: z.string() }),
handoff: researchToWriter,
execute: async (args) => await runResearchSubagent(args.query),
});Using as a tool
For AI SDK-compatible tools where no framework context is needed, .asTools() returns focused tools:
const agent = prompt({
id: "orchestrator",
tools: researchDelegate.asTools(),
});When the agent calls the research tool, the delegate:
- Validates the tool args against
argsSchema - Runs the subagent via
execute() - Passes the result through the handoff's
prepare()for validation and transform - Returns the transformed data as a JSON string
Each delegation validates data at three points: the LLM's tool args, the subagent's return value, and the final transformed output. See the API reference for the schema-by-schema breakdown.
Direct execution
Use .run() to execute a delegate programmatically:
const = await .(
{ : "cloud migration" },
,
);
.; // 'delegate-research'
.data;.; // LLM-generated summary, if the handoff configured one
.; // execution timeTyped context
When your subagent needs framework-specific data (action context, user IDs, project IDs, etc.), type the delegate's context so execute and .run() both enforce it:
import { delegate } from "@use-crux/core/agent";
import { z } from "zod";
type DelegateCtx = {
actionCtx: ActionCtx;
projectId: string;
userId?: string;
};
const researchDelegate = delegate({
id: "delegate-research",
argsSchema: z.object({ query: z.string() }),
handoff: researchToWriter,
execute: async (args, ctx: DelegateCtx) => {
// ctx is fully typed, access framework-specific context
return await ctx.actionCtx.runAction(runResearch, {
projectId: ctx.projectId,
query: args.query,
});
},
});See the API reference for the TCtx generic mechanics.
Using with framework-specific tool factories
For frameworks that have their own tool format, use .run() directly instead of .asTools(). In Convex Agent, prefer createTool() from @use-crux/convex/agent so nested delegate work stays observable:
import { createTool } from "@use-crux/convex/agent";
const research = createTool({
description: "Delegate research to a specialist",
inputSchema: researchDelegate.argsSchema,
execute: async (toolCtx, args, options) => {
const result = await researchDelegate.run(args, {
actionCtx: ctx,
projectId,
userId,
});
return result.data;
},
});This gives full control over how the framework context maps to the delegate context. See the Convex Agent guide for the full Convex setup.
All delegate executions are instrumented automatically: delegate:start and
delegate:complete events appear in
Devtools with input/output payload
snapshots and duration.