Sessions
An agent session is identified by a pair:agent (claude, codex, hermes,
openclaw) and key (the agent’s own session id). That pair is unique — registering the
same pair again refreshes the session rather than creating a second one, and revives it if
it had ended.
pinbox init installs a SessionStart hook that runs it with the
agent’s hook payload on stdin, so sessions register themselves. A session records
registeredAt, lastSeenAt, cwd, and endedAt. pinbox session list shows them.
Sticky routing
When an event needs delivering, the hub picks a target:- If the pin already has
agentSession, that binding wins — the hub looks the session up (registering it if it has since been forgotten) and delivers there. - Otherwise it takes the most recently seen session that has not ended, and writes that binding into the pin.
- If no session exists at all, the row is queued unassigned. The next session to register or pull picks it up.
--as agent does not bounce back into the agent’s own context.
Delivery paths
Delivery runs in the local daemon. Adapters are tried in order; the first one that reports the session reachable takes the event.In-session context injection (pull)
In-session context injection (pull)
For agents with a hook system — Claude Code, Codex, Hermes. Nothing is pushed. The
delivery row simply sits pending, and the agent’s own hooks pull it:
UserPromptSubmitrunspinbox session inject --hook, which returns every open pin as context for the next turn. Re-injecting every turn is the delivery model — it is also why attachments carry a path or URL and never inline bytes, which would be paid for again on every turn.Stoprunspinbox session pending --hook. If pins arrived mid-turn and are still open, it emits them and holds the agent instead of letting it stop. Zero pending emits nothing at all.
Push to a running agent gateway
Push to a running agent gateway
For OpenClaw, which runs a gateway that can accept work for a session key. Pinbox
queues the payload to join the session’s next heartbeat rather than interrupting
whatever it is doing.
Waking an ended session (resume)
Waking an ended session (resume)
If a session has ended, pinbox restarts it under the same session key from its
recorded Requires a recorded
cwd, so the agent comes back with its history:cwd and the agent binary on PATH. If the resume is refused or
impossible, the delivery fails terminally with E_SESSION_GONE — pinbox does not
silently start a fresh session and pretend it is the old one.Signed webhook
Signed webhook
The catch-all: anything the other paths cannot reach, including hosts that resume
their own sessions. Set
PINBOX_WEBHOOK_URL and PINBOX_WEBHOOK_SECRET on the daemon
and pinbox POSTs the event with:To verify: compute an HMAC-SHA256 over
`${timestamp}.${rawBody}` with your secret
and compare against the signature. A non-2xx response is a failure and gets retried.PINBOX_HOOKS_ESCALATE_MS). Nothing is lost when the daemon exits — the queue is durable,
and boot replays from its cursor.
Resolving from a commit
The most honest signal that a pin is fixed is the commit that fixed it.pinbox init
installs a post-commit hook that reads the message and resolves any pin named by a
trailer:
Fixes, Resolves, and Closes all work, case-insensitively, with or without the colon
(Fixes pin_njnwyw6r8d is fine). The full commit SHA is attached to the resolution, so
pinbox show tells you exactly which commit closed the pin.
It is idempotent by design: already-resolved and unknown ids skip silently, so amending or
rebasing re-fires the hook without complaint. And a broken hub never blocks a commit.
CLI reference
pinbox session, pinbox resolve, and the rest of the surface.