Advanced
Subagents and automation
Subagents are child executions, not a second storage system. A parent thread creates a child with an explicit role, model, tool allowlist, seed history, limits, and mailbox. The child reports events and a final result to the parent.
Define a bounded child task
const child = await coordinator.spawn({
parentThreadId: trusted.threadId,
role: "researcher",
agentId: "research",
model: {
provider: "openai",
model: "gpt-5.6-mini",
},
mode: "ask",
seedTurns: [
{
role: "user",
content: "Find the current refund policy in the approved sources.",
},
],
limits: {
maxSteps: 8,
maxToolCalls: 12,
maxDurationMs: 90_000,
},
});
Pass zero seed turns when the child needs only a task statement. Pass a small number of recent turns when context is necessary. Do not copy an entire conversation by default.
Parent and child communication
The parent can wait for a child, send a follow-up, or read the child result through the coordinator. Child tools use the same mode, capability, approval, tenant, and connection checks as a normal thread.
await coordinator.send(child.id, {
type: "instruction",
content: "Compare the two sources and return one recommendation.",
});
for await (const event of coordinator.observe(child.id)) {
publishToParent(event);
}
Use bounded fan-out for independent research. Keep writes in the parent or serialize them by workspace resource. A child must not receive a stronger credential or tenant scope than its parent.
Tool and cost limits
Set limits on every child:
- Maximum steps and model calls.
- Maximum tool calls and parallel calls.
- Maximum duration.
- Maximum output or total tokens.
- Maximum cost when the provider reports cost.
- Allowed tools and mode.
The parent remains responsible for accepting a child result. Do not import untrusted child output into a write tool without validation and, when needed, approval.
Scheduled and webhook work
Flary exports Cloudflare adapters for scheduled jobs, webhooks, and queue workers. The host authenticates a webhook or schedule, creates a run with a stable idempotency key, and returns quickly.
export default {
async scheduled(controller, env, execution) {
const run = await env.RUNS.create({
tenantId: "system",
applicationId: "reports",
agentId: "weekly-report",
request: {
requestId: "weekly:" + controller.scheduledTime,
channelId: "weekly-report",
input: { week: new Date(controller.scheduledTime).toISOString() },
idempotencyKey: "weekly:" + controller.scheduledTime,
},
});
execution.waitUntil(env.RUNS.execute(run.runId));
},
};
For a long job, the accepted run is the durable record. The webhook response does not stay open while the model, tools, or child agents work.
Common patterns
| Pattern | Use |
|---|---|
| Fan-out research | Several ask-mode children read independent sources |
| Review gate | A review child checks a build result before the parent requests approval |
| Background sync | A schedule starts a run with an idempotent timestamp key |
| Reactive support | A signed webhook starts a support run for a new ticket |
| Build pipeline | A build run invokes Sandbox only for explicit test or deploy jobs |
Subagents share Flary’s event, usage, history, and tenant contracts. The host still decides retention, quotas, and whether a child is visible to the end user.