Repository navigation
feat: deliver owner messages to bots through the inbox (M3, part 1) - #3
Merged
Merged
Conversation
The message line of the Claude Code inbox is not documented, so it was tested with Claude Code 2.1.283 before the courier depends on it: the line in 9.2 is delivered and opens a turn, a missing or wrong auth line closes the connection, and Claude Code never replies. The same run confirmed --continue resuming a real conversation. Also settles what the courier does: in order and one at a time per bot, a 10 s send timeout, deterministic retry backoff, archived bots' queues dropped, and an English envelope with a relative deadline.
Protocol types for spec 9 and 11 (Message, Delivery, Task, their enums and the messages.*/deliveries.* params), exported to TypeScript, plus the envelope a bot reads when a message arrives and validation for message bodies.
Migration 0002 creates the three tables of spec 12. A message is saved with its delivery and the task it creates in one transaction. Delivery transitions are single conditional UPDATEs; due_deliveries hands out only each bot's oldest pending delivery, and only while none of that bot's deliveries is being sent, so messages arrive in order.
Opens the bot's named pipe as a client, retries for up to 2 s while it is busy, writes and closes. The address comes from a bot's hook, so only plain names under \\.\pipe\ are accepted: a ".." segment could make CreateFile open a real file.
The courier moves pending deliveries into the bots' inboxes: it waits without counting attempts while a bot is not ready, retries failures with backoff until max_attempts, recovers expired leases, drops the queue of archived bots and does not blame a delivery when the bot restarted during the send. Times come from an injectable clock, so the tests drive retries with a manual clock and a fake inbox. New RPC methods messages.send, messages.list, deliveries.list and deliveries.retry, the message.created and delivery.changed notifications, and the delivery backlog in system.status. Also fixes the test daemon's botloftd path, which had backspace characters in it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First slice of M3 (spec 17): durable owner messages delivered to bots through the Claude Code inbox. Bot-to-bot messages, MCP tools and tasks come in the next PR.
What changes
{"type":"user","message":{"role":"user","content":...}}is delivered and opens a turn. A missing or wrong auth line closes the connection. Claude Code never replies, and closing right after the write is safe. Three connections in a row were all delivered. The same run confirmed--continueresuming a real conversation.Message,Delivery,Taskand their enums, themessages.*/deliveries.*params, and the envelope the bot reads (English, with a relative deadline). All exported toprotocol.gen.ts.0002_messages.sql(messages, deliveries, tasks). A message is saved with its delivery in one transaction. Each delivery transition is a single conditional UPDATE.due_deliveriesreturns only each bot's oldest pending delivery, and only while none of that bot's deliveries is being sent.\\.\pipe\names are accepted, since the address comes from a bot's hook.idle/busy/needs_approvalor has no inbox.max_attempts, then marks the deliverydead.messages.send,messages.list,deliveries.listanddeliveries.retry.message.createdanddelivery.changed.system.status.deliveriesnow reports the backlog.botloftdpath, which had backspace characters in it (left over from M2).Tests
tests/courier.rs, over RPC withFakeRuntimeandFakeInbox:deadanddeliveries.retry;pnpm check.Manual test with real Claude Code (done, 2.1.283)
BOTLOFT_HOME=.dev\home) with an existing bot whose folder is already trusted.idle, callmessages.send {botId, body: "... reply only with: COURIER-OK"}.deliveries.listshowssentwithattempts: 0a few ms later, and the bot's terminal shows[botloft] from the owner · crew ...and the replyCOURIER-OK.Result: as expected. The debug log shows the inbox token as
<redacted>, and no message body appears in the log.Still pending with real Claude Code (next PR)
${BOTLOFT_BOT_TOKEN}expansion in themcp.jsonheaders (needs/mcp).