Skip to content
Changelog

Report a bug

Hand Off Work to a Managed Agent

Code is fast, cheap, and predictable. AI is better for work that needs judgment, for example a summary, or a decision about an unusual case. A code automation can do both: code does the fixed steps, then it gives the rest to the managed agent.

Set mode and agent in the deployment:

export const deployment = {
mode: "code_with_optional_agent",
agent: { instructions: "You triage failed deploys for the platform team. Be brief." },
subscriptions: { /* ... */ },
} satisfies AutomationDeployment;
  • agent.instructions apply to every handoff.
  • With mode: "code" (the default), handoffs fail the run.
export default class {
async onFailure(ctx: AutomationContext, event: IntegrationEvent) {
const logs = await readLogs(ctx, event);
if (logs.isErr()) return err(logs.error);
if (logs.value.knownFlake) return ctx.setOutput({ action: "ignored", reason: "known flake" });
const staged = await ctx.agent.run({
effectKey: `triage:${event.id}`,
instructions: "Find the cause of this deploy failure. Post a short summary in the Space.",
context: { failedStep: logs.value.step, lastLines: logs.value.tail },
});
if (staged.isErr()) return err(staged.error);
return ctx.setOutput({ action: "handed_off" });
}
}
  • Type starts the AI only after the method succeeds. If the method fails, no handoff starts.
  • Type gives the AI the full source event. Put other evidence in context.
  • The AI works in the Space, with the Space’s connections and skills.
  • A staged handoff is not a delivered result. The run output shows that you staged it.

ctx.agent.evaluate stages a handoff with a question. After the method succeeds, a model answers it:

await ctx.agent.evaluate({
effectKey: `review:${event.id}`,
instructions: "Review this customer message and draft a reply.",
context: { message: text },
evaluation: {
question: "Does this message need a reply from the team?",
criteria: {
skip: "Thanks, automatic replies, or messages that need no action.",
invoke_agent: "Questions, complaints, or requests that need a person.",
},
},
});
  • If the model is confident (0.8 or more) that the answer is skip, no AI starts.
  • In all other cases, the AI starts.
  • Your code does not get the answer. For answers in code, use AI questions in a matcher.

Add tools to limit the handoff to tools that you choose:

await ctx.agent.run({
effectKey: `issue:${event.id}`,
instructions: "Create one Linear issue for this failure.",
tools: [{ service: "linear", name: "create_issue", organizationIntegrationId: "orgint_abc123" }],
});

You can list at most 16 tools.

  • effectKey: 1 to 500 characters. Use the same key for the same logical handoff.
  • instructions: 1 to 10,000 characters.
  • At most 100 handoffs in a run.
  • The same effectKey with the same input counts one time. The same key with different input fails the run.

Use mode: "code" for reads, filters, comparisons, text formats, checkpoints, and writes that have clear rules. mode: "code" turns off handoffs to the managed agent. It does not turn off AI filters: a matcher with AI questions still calls a model and uses AI. Without handoffs and AI filters, a run uses no model, costs less, and gives the same result each time. Add a handoff only for the part that needs judgment.