Tools
Agent-to-Agent Messages
Let one agent hand work to another agent without waiting for it.
A tool can send another agent a message. Here a coder agent hands a finished branch to the reviewer for that repository and keeps working.
import { Type } from "@earendil-works/pi-ai";
import { defineTool } from "@earendil-works/pi-coding-agent";
import { pi } from "@rivet-dev/pi";
import { e2bProvider } from "@rivet-dev/sandbox-adapter/e2b";
import { createClient } from "rivetkit/client";
import type { registry } from "./server";
const requestReview = defineTool({
name: "request_review",
label: "Request review",
description: "Send a finished branch to the repository's reviewer. Don't wait for the review.",
parameters: Type.Object({ repo: Type.String(), branch: Type.String(), summary: Type.String() }),
async execute(_toolCallId, { repo, branch, summary }) {
await client.reviewer.getOrCreate([repo]).requestReview(`Review the ${branch} branch of https://github.com/${repo}: ${summary}`);
return { content: [{ type: "text", text: `Sent ${branch} for review.` }], details: undefined };
},
});
export const coder = pi({
model: "anthropic/claude-opus-5-5",
sandbox: e2bProvider(),
customTools: [requestReview],
});
const client = createClient<typeof registry>();
import { pi } from "@rivet-dev/pi";
import { e2bProvider } from "@rivet-dev/sandbox-adapter/e2b";
import { setup } from "rivetkit";
import { coder } from "./coder";
const reviewer = pi({
model: "openai/gpt-5.5",
sandbox: e2bProvider(),
actions: {
requestReview: async (c, request: string) => {
await c.schedule.after(0, "prompt", request, { streamingBehavior: "followUp" });
},
},
});
export const registry = setup({ use: { coder, reviewer } });
registry.start();
import { createClient } from "rivetkit/client";
import type { registry } from "./server";
const client = createClient<typeof registry>();
const coder = client.coder.getOrCreate(["acme/app", "fix-flaky-checkout-test"]);
await coder.prompt(
"Clone https://github.com/acme/app, fix the flaky checkout test on a new fix-flaky-checkout-test branch, push it, and request a review.",
);
console.log(await coder.getLastAssistantText());
- The reviewer is keyed by repository, so every coder working on the same repository reaches the same reviewer, and its session remembers past reviews.
- The
requestReviewaction schedules the reviewer’spromptand returns, so the coder’s tool doesn’t wait for the review. The schedule is saved with the reviewer, so the review runs even if the coder’s request goes away. - Passing
streamingBehavior: "followUp"queues the request when the reviewer is busy with another review. Use it withprompt, not thefollowUpaction, which only queues on an idle agent and never starts a run. - The reviewer runs on a different model family than the coder.
To wait for the other agent’s answer instead, see Subagents. See Schedules for more on c.schedule.