Subagents
Let one agent delegate a job to another agent on a child thread.
A subagent is another agent in the same createPai app. The parent exposes a tool; the model picks a child and a task; PAI runs that child on a hidden thread and writes the result back.
Use this when one specialist should not share the user's full transcript: research, analysis, a read-only explorer.
import { defineSubagentTool } from "@pai/core";
import { explorerAgent } from "../explorer-agent";
export const research = defineSubagentTool({
id: "research",
description: "Delegate research to a specialist in this workspace",
agents: {
explorer: {
agent: explorerAgent,
description: "Read-only research over workspace documents",
},
},
});The model calls research with { agent: "explorer", task: "..." }. You do not write inputSchema / execute yourself.
Register the parent and the child:
createPai({
agents: {
main: defineAgent({
tools: { research },
// ...
}),
explorer: explorerAgent,
},
scopeKey: (identity) => identity.workspaceId,
});On the HTTP receiver, keep specialists from being started by the browser:
createPaiExpressReceiver({
pai,
resolveIdentity,
access: { explorer: "read" },
});The parent still spawns them in-process. The client can watch the child thread if you want a nested transcript:
const Explorer = Pai.agent("explorer");
function ExplorerCard({
tool,
}: {
tool: ToolRenderProps<AssistantPai["agents"]["main"], "research">;
}) {
const subagent = Assistant.useSubagent(tool);
if (!subagent) return null;
return (
<Explorer.ThreadProvider threadId={subagent.threadId}>
<ChildChat />
</Explorer.ThreadProvider>
);
}useSubagent(tool) reads { agent, threadId, status } from the parent card. It does not fetch. Mount the child binding's ThreadProvider with that threadId. See useSubagent().