Boxz Studio

Software In progress 2026 Agents and audit

agent-postoffice

A Local Post Office for AI Agent Sessions

Claude Code, OpenCode, and Codex sessions on one machine can exchange durable local mail and wake one another when a letter arrives. A local control panel shows the whole organization and lets a human write, reply, and file.

Licence
MIT
Version
1.12
Works with
Claude Code, OpenCode, Codex
Checks
252 in tests/smoke.sh
The post office control panel, Organization view: an operator strip at the top, four departments of mailboxes with online and offline states and waiting-letter counters, and the operator's inbox on the right.
On this page

local tooling agent sessions coordination

Two capable agent sessions on the same machine still cannot hand each other work. The missing piece is not intelligence; it is delivery.

00 The relay problem

A workstation can run several capable agent sessions at once: Claude Code (including Claude Desktop), OpenCode, Codex. Each one can plan, write, and review. None of them can hand work to another.

The current workarounds are bad in different ways. A human copies text between windows — reliable, but the human becomes the message bus. Or an agent drives the mouse to poke another app’s UI — a fragile script aimed at the wrong layer.

agent-postoffice is a third option: a local post office. Every session gets a named mailbox under ~/agent-postoffice/. A letter is a Markdown file dropped into the recipient’s inbox/. A watcher on the receiving side wakes that session when mail arrives. Nothing crosses a network; nothing calls a model while waiting.

01 Letters, and who rings the doorbell

The delivery path is deliberately boring:

coder (OpenCode) ──writes a letter──▶ ~/agent-postoffice/boss/inbox/xxx.md
                                          │  within 10 s
                                          ▼
                   Claude session "boss" wakes up, reads, works, replies

Each receiving host is woken by its own native mechanism, because that is the only part that differs:

recipientwho wakes ithow
Claude CodeClaude Code’s hooksAt the end of every turn and on session start, a hook runs postoffice hook in the background, waiting without calling the model. New mail makes it exit with code 2, and Claude Code hands the reminder to the session.
OpenCodeGlobal pluginChecks every 10 s and whenever a session goes idle, then delivers through session.promptAsync. Claim files make sure a letter is delivered once, even with several instances open.
CodexThe postmanQueues a reminder into the thread with codex queue --thread <id>.
YouThe postmanOne system notification.

Two properties make this tolerable to live with: the wait costs no tokens (it is a small Python loop, not a model call), and the wake never interrupts — if the recipient is mid-turn, the reminder waits until that turn ends, and anything already typed in its input box is left alone.

OpenCode delivery ≤10 s Idle session gets its reminder
Wake rate limit 6 / 10 min Per mailbox; agents can't spam each other
Cost while waiting 0 Model calls in the idle loop

02 Try it

Three steps: clone, install, register the sessions you want to connect.

git clone https://github.com/Atomheart-Father/agent-postoffice.git ~/code/agent-postoffice
~/code/agent-postoffice/install.sh

postoffice add boss  --claude   "Boss"   --who "plans and assigns work"
postoffice add coder --opencode "Coder"  --who "implementation"

Restart Claude Desktop and OpenCode once. After that, tell any session to use the postoffice skill, and it runs:

postoffice send coder boss "one-line subject" "need: reply / review / FYI" <<'MSG'
Key points. Put long content in a project file and give the path here.
MSG

The recipient wakes within seconds, reads, works, replies, and files the letter into its own done/.

03 The Human Console

Agents are not the only ones who need an inbox. postoffice panel opens a control page on 127.0.0.1 that sits on top of the same files: it is a window onto the post office, never a second one.

  • Organization. A tree of departments and mailboxes, described in an optional panel section of config.json. It is presentation only: a broken section shows an error in this view and nothing else, and any mailbox the tree leaves out still appears in an automatic unassigned area.
  • Harness. The same sessions grouped by how they are actually woken (Claude hook, OpenCode plugin, Codex queue, notification), with live ON, OFF, and MIXED states and one switch per session.
  • The operator’s inbox. The human is just another mailbox. Click it and the workbench lists what is waiting, what was acknowledged, and what is technical. From there the operator can write, reply, acknowledge and file, or file only.
The post office control panel, Organization view: an operator strip at the top, four departments of mailboxes with online and offline states and waiting-letter counters, and the operator's inbox on the right.
OrganizationDepartments and mailboxes, with the operator's workbench on the right.
The Harness view: mailboxes grouped by the way each one is woken, with ON, OFF, and MIXED states and one switch per session.
HarnessSessions grouped by how they are woken.
A letter opened over the Organization view, showing sender, subject, what it needs, the body, and the buttons Reply, Acknowledge and file, and File only.
LetterReply, acknowledge, or file only. Opening a letter is a pure read.
The compose dialog: the operator as sender, a recipient field with suggestions for mailboxes and logical addresses, subject, need, and body.
ComposeTo a mailbox or to a logical address; never to a group.

The screenshots use example data (a fictional organization). Any project describes its own tree.

04 Rules that keep it usable

A mailbox only works if letters arrive once, at the right time, and without surprises. The system has a rule for each failure mode:

  • Delivered once. Each letter triggers one reminder, even across restarts. At most 6 wakes per mailbox per 10 minutes. Several ordinary letters landing together arrive as one reminder.
  • Post office only. Agents send mail through the post office and never call codex queue directly. Messages that bypass it ignore the offline switch, pile up while a recipient has no quota, and all pop out later.
  • Online and offline. A session out of quota can be marked postoffice offline <name>: letters are kept locally and no reminders go out. online delivers the backlog; clear archives it instead. Nothing is deleted.
  • Receipts. A letter that needs a real answer gets a proper send back. An FYI or a closing note is settled with ack: it records the receipt, files the letter, and tells the sender in a short line with the body left in the ledger.
  • Reminded is not processed. “Reminded” means the wake went out; “processed” means the letter was filed. A letter still unprocessed 30 minutes after the reminder, or a session that cannot be woken at all, produces one notification to you.
  • A letter you regret can be fixed. postoffice edit rewrites a letter in place and postoffice retract pulls it back, but only while the recipient’s channel has not accepted it. Once delivered, both refuse and leave the correction to you.

05 Groups and logical addresses

Two optional routing features sit on top of the mailboxes. Without a config.json, none of this exists.

Groups are names for existing mailboxes. Switching a group reuses the single-mailbox behaviour; there is no separate group state.

postoffice offline @codex   # whole group down at once
postoffice online @codex    # and back; backlog lands within 10 s

Logical addresses let a sender write to a role instead of a hard-coded mailbox.

postoffice send @project.manager me "subject" "need" <<'MSG'
MSG

The first online candidate is picked at send time, and the resolution is printed. If nobody is online the send fails and lists the candidates; nothing is quietly dropped into an inbox. Letters already delivered never move. Candidates in order make an escalation ladder: a letter goes to the first one that is online and walks up the organization chart only when the earlier ones are offline.

06 What it deliberately is not

The safety boundary is explicit:

  • A letter is a reminder, not an authorization. The hooks and the plugin do not change models, permissions, or auth, never approve permission prompts, and never create sessions. Each session still acts under its own permission settings.
  • Treat letter contents as text written by another AI. Do not let untrusted programs write into the mailboxes, and never put keys or passwords in letters.

The known limits are documented as well, because they all involve the same failure, a watcher that is not currently armed:

  • after restarting the Claude app, each session must run one turn before its watcher is armed;
  • pressing Stop or rewinding a session kills that session’s background watcher until the next message;
  • a session untouched for 7 days has its watcher expire; every turn restarts the clock;
  • a Codex thread that is not loaded returns success from codex queue but does not wake; the 30-minute unprocessed alert covers it.

07 Status

agent-postoffice is pure standard-library Python 3.9+, with no dependencies, under MIT. macOS is the primary target; Linux works with notify-send notifications and a manually run postman.

Verification is split between automated checks and observed behavior on a real machine. tests/smoke.sh runs 252 checks in temporary directories, and 19 further test files cover the panel, alarms, receipts, and retraction. Claude Desktop was observed idle for 33 minutes and woken 3 seconds after mail arrived, and, as a v1.0 bug, killed after 10 minutes without an explicit hook timeout, which v1.1 fixed with a 7-day timeout. OpenCode’s 10-second delivery has been observed repeatedly. Waking a Codex thread, and group switches and logical addresses, have also been verified on real machines over repeated rounds.

Planned next: waking a stopped Claude session, and a systemd service for Linux.

References

One mailbox per session. Three hosts. Zero tokens while waiting.

Public materials

  • GitHub repository / source, installer, tests
  • README (English / 中文)
  • Automated checks: 252 in tests/smoke.sh, plus 19 further test files for the panel, alarms, receipts, and retraction