Skip to content
Quickstart

Quickstart

Durable addressed agent mail over generic Watchdog work

This sends work from a parent agent to a child agent, settles it, and delivers the reply. The outbox uses Carrier’s deterministic memory helper, and post prints the reply instead of sending it anywhere.

quickstart.ts
import { BirdDog, type BirdDogMail } from "@fungi.computer/bird-dog";
import { createMemoryOutbox } from "@fungi.computer/carrier/testing";
const parent = {
kind: "agent",
agentId: "parent",
sessionId: "parent-session",
} as const;
const child = {
kind: "agent",
agentId: "child",
sessionId: "child-session",
} as const;
const room = await BirdDog.make({
outbox: createMemoryOutbox(),
post: async (mail: BirdDogMail) => {
console.log(`to ${mail.to.agentId}:\n${mail.content}`);
return true; // the recipient accepted it
},
scheduleWake: async (at) => {
console.log(`wake me at ${at}`);
},
now: () => Date.now(),
});
// 1. Turn accepted work into a Watchdog job. Hand this to Watchdog's ingest.
const job = BirdDog.agentJob({
work: {
jobId: "job-1",
sessionId: child.sessionId,
commandFactId: "command-1",
},
mail: {
key: "delegation-1",
to: child,
from: parent,
replyTo: [parent],
content: "Find out why the build is red",
createdAt: Date.now(),
},
});
// 2. Your Watchdog executor decodes the payload and runs the child's work.
const payload = BirdDog.decodeAgentJob(job);
console.log(payload.work.commandFactId); // "command-1"
// 3. When Watchdog settles the job, queue the replies, then deliver them.
await room.fanOut({
job: { ...job, state: "settled", outcome: "completed" },
content: "A test fixture was missing. Fixed it.",
});
await room.drainOutbox();

In a real host, the settled job comes from Watchdog’s tick() result and drainOutbox() runs from the wake you scheduled.