Skip to content

Concepts

Addresses. An agent address is { kind: "agent", agentId, sessionId }. IDs are 1 to 256 characters. There is no special kind for subagents.

Mail. A BirdDogMail has a stable key, a to and from address, a replyTo list, string content and a createdAt time in milliseconds. The key must identify the request across retries. BirdDog derives every reply key from it.

Agent jobs. BirdDog.agentJob({ work, mail? }) validates the input and returns a queued Watchdog job. The job’s queue is the target sessionId, its lane is default, its priority is 0, and its payload carries the sessionId, the commandFactId and the optional mail. work.recovery passes straight through to Watchdog’s recovery policy. Because it is synchronous, you can call it inside an owner transaction and pass the result to Watchdog’s transactional.enqueueAcceptedJob. BirdDog.decodeAgentJob(job) parses the payload back on the executor side. Watchdog never reads it.

Fan-out. fanOut({ job, content }) takes a settled job and the content owed to correspondents. It writes one reply per replyTo address into the outbox, keyed outbox:<mail key>:reply:<index>, then schedules one wake for the earliest. Each reply starts with a small header, then your content:

---
outcome: completed
agentId: child
sessionId: child-session
---
A test fixture was missing. Fixed it.

A completed job reports outcome: completed. A failed, outcome_unknown or interrupted job reports outcome: interrupted. Cancellation is quiet: a cancelled job sends no replies. A job without mail sends nothing.

Draining. drainOutbox() reads one page of up to 64 outbox rows and calls post for each due reply. post returns true when the recipient accepts the mail. A false or a throw schedules a retry. If more rows remain, BirdDog schedules an immediate wake and continues from the same place on the next drain.

Receipts. A delivered reply’s row is replaced by a compact completion receipt under the same key. Replaying the same settlement after a restart finds the receipt and does not post the reply again. Keep those receipts as long as source settlements can replay. BirdDog does not expire them.

Host ports. BirdDog.make takes four things:

  • outbox: string rows with put, read, ordered list(limit, afterKey?) and delete.
  • post(mail): delivers one reply and reports acceptance.
  • scheduleWake(at): arms a durable timer for drainOutbox.
  • now(): the current time in milliseconds.