480 lines
11 KiB
Markdown
480 lines
11 KiB
Markdown
# Inbox CLI
|
|
|
|
## Purpose
|
|
|
|
`inbox` is the durable coordination bus for agent-to-agent communication. It is not the scheduler. It stores threads, messages, leases, and artifacts so workers and leaders can coordinate through reliable state instead of ad hoc multi-turn chat.
|
|
|
|
In normal operation:
|
|
|
|
- workers use `inbox` directly
|
|
- leaders use `inbox` mainly for inspection or manual override
|
|
- leader-side task planning and dispatch should happen through `orch`
|
|
|
|
## Responsibilities
|
|
|
|
`inbox` is responsible for:
|
|
|
|
- creating and updating communication threads
|
|
- sending directed messages between agents
|
|
- allowing a worker to claim one thread at a time through leases
|
|
- recording progress, blocked questions, results, and failures
|
|
- attaching artifact references such as files, logs, or patches
|
|
- listing, watching, waiting, and showing thread history
|
|
|
|
## Non-Responsibilities
|
|
|
|
`inbox` should not decide:
|
|
|
|
- how a large goal is decomposed into tasks
|
|
- whether a task is ready based on dependencies
|
|
- which worker should receive a task by policy
|
|
- when a failed task should be retried
|
|
|
|
Those decisions belong to `orch`.
|
|
|
|
## Core Objects
|
|
|
|
- `thread`: the durable container for one work conversation
|
|
- `message`: one event inside a thread
|
|
- `lease`: an exclusive worker claim for a thread
|
|
- `artifact`: a path or file reference attached to a message
|
|
- `event`: a monotonic record used to wake blocking waiters
|
|
|
|
## Required Fields
|
|
|
|
### Thread Fields
|
|
|
|
- `thread_id`
|
|
- `run_id`
|
|
- `task_id`
|
|
- `subject`
|
|
- `created_by`
|
|
- `assigned_to`
|
|
- `status`
|
|
- `priority`
|
|
- `created_at`
|
|
- `updated_at`
|
|
|
|
### Message Fields
|
|
|
|
- `message_id`
|
|
- `thread_id`
|
|
- `from_agent`
|
|
- `to_agent`
|
|
- `kind`
|
|
- `summary`
|
|
- `body`
|
|
- `payload_json`
|
|
- `created_at`
|
|
|
|
## Message Kinds
|
|
|
|
- `task`
|
|
- `progress`
|
|
- `question`
|
|
- `answer`
|
|
- `result`
|
|
- `control`
|
|
- `event`
|
|
|
|
## Thread Status Values
|
|
|
|
- `pending`
|
|
- `claimed`
|
|
- `in_progress`
|
|
- `blocked`
|
|
- `done`
|
|
- `failed`
|
|
- `cancelled`
|
|
|
|
## Worker Protocol
|
|
|
|
The normal worker flow is:
|
|
|
|
1. `fetch` candidate threads
|
|
2. `claim` one thread
|
|
3. `update --status in_progress`
|
|
4. continue with `update` messages as needed
|
|
5. if blocked, set `blocked` and ask a precise question
|
|
6. wait for a reply with `inbox wait-reply`
|
|
7. finish with `done` or `fail`
|
|
|
|
Rules:
|
|
|
|
- `fetch` does not grant ownership
|
|
- only `claim` grants ownership
|
|
- only one active lease may exist per thread
|
|
- blocked messages must say exactly what is missing
|
|
- terminal messages should include result or failure summary
|
|
- a blocked worker should wait on a reply event rather than sleeping blindly
|
|
|
|
## CLI Surface
|
|
|
|
The binary name is `inbox`.
|
|
|
|
### Global Flags
|
|
|
|
- `--db PATH`
|
|
- `--json`
|
|
- `--agent NAME`
|
|
|
|
### `inbox init`
|
|
|
|
Initialize the communication schema and SQLite pragmas.
|
|
|
|
Example:
|
|
|
|
```bash
|
|
inbox init --db .agents/coord.db
|
|
```
|
|
|
|
### `inbox send`
|
|
|
|
Create a new thread or append a message to an existing one.
|
|
|
|
Suggested flags:
|
|
|
|
- `--from AGENT`
|
|
- `--to AGENT`
|
|
- `--subject TEXT`
|
|
- `--thread THREAD_ID`
|
|
- `--run RUN_ID`
|
|
- `--task TASK_ID`
|
|
- `--kind task|question|answer|progress|result|control|event`
|
|
- `--summary TEXT`
|
|
- `--body TEXT`
|
|
- `--body-file PATH`
|
|
- `--payload-json STRING`
|
|
- `--priority low|normal|high`
|
|
|
|
### `inbox fetch`
|
|
|
|
List candidate threads for an agent without claiming them.
|
|
|
|
Suggested flags:
|
|
|
|
- `--agent AGENT`
|
|
- `--status pending,blocked`
|
|
- `--limit N`
|
|
- `--unread`
|
|
|
|
### `inbox claim`
|
|
|
|
Acquire a lease on a thread.
|
|
|
|
Suggested flags:
|
|
|
|
- `--agent AGENT`
|
|
- `--thread THREAD_ID`
|
|
- `--lease-seconds N`
|
|
|
|
### `inbox renew`
|
|
|
|
Extend an existing lease.
|
|
|
|
Suggested flags:
|
|
|
|
- `--agent AGENT`
|
|
- `--thread THREAD_ID`
|
|
- `--lease-seconds N`
|
|
|
|
### `inbox update`
|
|
|
|
Append a progress or blocked update.
|
|
|
|
Suggested flags:
|
|
|
|
- `--agent AGENT`
|
|
- `--thread THREAD_ID`
|
|
- `--status in_progress|blocked`
|
|
- `--summary TEXT`
|
|
- `--body TEXT`
|
|
- `--body-file PATH`
|
|
- `--payload-json STRING`
|
|
|
|
### `inbox reply`
|
|
|
|
Reply inside an existing thread.
|
|
|
|
Suggested flags:
|
|
|
|
- `--from AGENT`
|
|
- `--to AGENT`
|
|
- `--thread THREAD_ID`
|
|
- `--kind answer|question|progress|control`
|
|
- `--summary TEXT`
|
|
- `--body TEXT`
|
|
- `--body-file PATH`
|
|
- `--payload-json STRING`
|
|
|
|
### `inbox done`
|
|
|
|
Mark a thread complete and attach the final result.
|
|
|
|
Suggested flags:
|
|
|
|
- `--agent AGENT`
|
|
- `--thread THREAD_ID`
|
|
- `--summary TEXT`
|
|
- `--body TEXT`
|
|
- `--body-file PATH`
|
|
- `--payload-json STRING`
|
|
|
|
### `inbox fail`
|
|
|
|
Mark a thread failed.
|
|
|
|
Suggested flags:
|
|
|
|
- `--agent AGENT`
|
|
- `--thread THREAD_ID`
|
|
- `--summary TEXT`
|
|
- `--body TEXT`
|
|
- `--payload-json STRING`
|
|
|
|
### `inbox cancel`
|
|
|
|
Cancel a thread.
|
|
|
|
Suggested flags:
|
|
|
|
- `--agent AGENT`
|
|
- `--thread THREAD_ID`
|
|
- `--reason TEXT`
|
|
|
|
### `inbox list`
|
|
|
|
List threads with filters.
|
|
|
|
Suggested flags:
|
|
|
|
- `--agent AGENT`
|
|
- `--status pending,claimed,in_progress,blocked,done,failed,cancelled`
|
|
- `--created-by AGENT`
|
|
- `--assigned-to AGENT`
|
|
- `--limit N`
|
|
|
|
### `inbox show`
|
|
|
|
Show one thread with full message history.
|
|
|
|
Suggested flags:
|
|
|
|
- `--thread THREAD_ID`
|
|
- `--json`
|
|
|
|
### `inbox watch`
|
|
|
|
Block until new matching activity appears.
|
|
|
|
Suggested flags:
|
|
|
|
- `--agent AGENT`
|
|
- `--status pending,blocked,done,failed`
|
|
- `--timeout-seconds N`
|
|
|
|
### `inbox wait-reply`
|
|
|
|
Block until a new reply-like message appears for one thread.
|
|
|
|
This is the normal wait primitive for a blocked worker.
|
|
|
|
Suggested flags:
|
|
|
|
- `--thread THREAD_ID`
|
|
- `--after-message MESSAGE_ID`
|
|
- `--after-event EVENT_ID`
|
|
- `--kinds answer|control|result`
|
|
- `--timeout-seconds N`
|
|
|
|
Behavior:
|
|
|
|
- waits until a later matching message exists in the thread
|
|
- returns the new message and associated event cursor
|
|
- avoids blind `sleep` loops in worker logic
|
|
|
|
## JSON Contract
|
|
|
|
Every command should support `--json`.
|
|
|
|
Suggested success shape:
|
|
|
|
```json
|
|
{
|
|
"ok": true,
|
|
"command": "claim",
|
|
"thread": {
|
|
"thread_id": "thr_123",
|
|
"task_id": "T4",
|
|
"status": "claimed",
|
|
"assigned_to": "backend-worker"
|
|
}
|
|
}
|
|
```
|
|
|
|
Suggested error shape:
|
|
|
|
```json
|
|
{
|
|
"ok": false,
|
|
"error": {
|
|
"code": "lease_conflict",
|
|
"message": "thread already claimed by another worker"
|
|
}
|
|
}
|
|
```
|
|
|
|
Suggested `wait-reply` wake shape:
|
|
|
|
```json
|
|
{
|
|
"ok": true,
|
|
"command": "wait-reply",
|
|
"woke": true,
|
|
"next_event_id": 127,
|
|
"message": {
|
|
"message_id": "msg_901",
|
|
"thread_id": "thr_123",
|
|
"kind": "answer",
|
|
"summary": "Use email/password for MVP",
|
|
"body": "Use a simple credential flow for the first iteration."
|
|
}
|
|
}
|
|
```
|
|
|
|
## Exit Codes
|
|
|
|
- `0`: success
|
|
- `10`: no matching work
|
|
- `20`: conflict such as lease contention
|
|
- `30`: invalid input or invalid state transition
|
|
- `40`: not found
|
|
- `50`: storage or internal error
|
|
|
|
## SQLite Schema Draft
|
|
|
|
```sql
|
|
CREATE TABLE IF NOT EXISTS threads (
|
|
thread_id TEXT PRIMARY KEY,
|
|
run_id TEXT NOT NULL,
|
|
task_id TEXT NOT NULL,
|
|
subject TEXT NOT NULL,
|
|
created_by TEXT NOT NULL,
|
|
assigned_to TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
priority TEXT NOT NULL DEFAULT 'normal',
|
|
latest_message_id TEXT,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS messages (
|
|
message_id TEXT PRIMARY KEY,
|
|
thread_id TEXT NOT NULL,
|
|
from_agent TEXT NOT NULL,
|
|
to_agent TEXT NOT NULL,
|
|
kind TEXT NOT NULL,
|
|
summary TEXT NOT NULL,
|
|
body TEXT NOT NULL DEFAULT '',
|
|
payload_json TEXT NOT NULL DEFAULT '{}',
|
|
created_at TEXT NOT NULL,
|
|
FOREIGN KEY(thread_id) REFERENCES threads(thread_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS leases (
|
|
thread_id TEXT PRIMARY KEY,
|
|
agent_id TEXT NOT NULL,
|
|
lease_token TEXT NOT NULL,
|
|
claimed_at TEXT NOT NULL,
|
|
expires_at TEXT NOT NULL,
|
|
released_at TEXT
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS artifacts (
|
|
artifact_id TEXT PRIMARY KEY,
|
|
message_id TEXT NOT NULL,
|
|
path TEXT NOT NULL,
|
|
kind TEXT NOT NULL,
|
|
metadata_json TEXT NOT NULL DEFAULT '{}',
|
|
created_at TEXT NOT NULL,
|
|
FOREIGN KEY(message_id) REFERENCES messages(message_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS events (
|
|
event_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
run_id TEXT NOT NULL,
|
|
task_id TEXT NOT NULL,
|
|
thread_id TEXT NOT NULL,
|
|
source TEXT NOT NULL,
|
|
event_type TEXT NOT NULL,
|
|
message_id TEXT,
|
|
summary TEXT NOT NULL DEFAULT '',
|
|
payload_json TEXT NOT NULL DEFAULT '{}',
|
|
created_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_threads_status_assigned
|
|
ON threads(status, assigned_to, updated_at);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_messages_thread_created
|
|
ON messages(thread_id, created_at);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_events_thread_event
|
|
ON events(thread_id, event_id);
|
|
```
|
|
|
|
## Concurrency Notes
|
|
|
|
- use `PRAGMA journal_mode=WAL`
|
|
- keep write transactions short
|
|
- make `claim` atomic
|
|
- never mutate state during `fetch`
|
|
- implement `watch` and `wait-reply` as blocking queries over message or event cursors, not as user-managed `sleep`
|
|
|
|
## Embedded Skill Draft
|
|
|
|
The following block is a draft `SKILL.md` for the `inbox` skill.
|
|
|
|
````markdown
|
|
```markdown
|
|
---
|
|
name: inbox
|
|
description: Use this skill when an agent needs durable communication through the local inbox CLI. It is for fetching work, claiming a thread, sending progress updates, raising blocked questions, waiting for replies, replying inside a thread, returning results, and watching inbox activity. Do not use it for task decomposition or scheduling decisions; use the orchestrator skill for that.
|
|
---
|
|
|
|
# Inbox
|
|
|
|
Use this skill when you need to communicate through the `inbox` CLI and its SQLite-backed thread store.
|
|
|
|
## When To Use
|
|
|
|
- a worker needs to fetch and claim work
|
|
- a worker needs to report progress
|
|
- a worker is blocked and must ask for clarification
|
|
- a leader needs to inspect or manually reply inside a thread
|
|
- an agent needs durable, machine-readable coordination instead of ad hoc chat
|
|
|
|
## Rules
|
|
|
|
- Prefer `--json` when another agent will consume the output.
|
|
- Never treat `fetch` as ownership; only `claim` grants ownership.
|
|
- Do not start work without a valid lease.
|
|
- When blocked, say exactly what is missing.
|
|
- If blocked, prefer `inbox wait-reply` instead of manual sleep loops.
|
|
- Use `result` for final output and `fail` for terminal failure.
|
|
- Keep scheduling decisions out of this layer.
|
|
|
|
## Typical Commands
|
|
|
|
```bash
|
|
inbox fetch --agent backend-worker --status pending --json
|
|
inbox claim --agent backend-worker --thread thr_123 --lease-seconds 900 --json
|
|
inbox update --agent backend-worker --thread thr_123 --status in_progress --summary "Implementing post CRUD routes" --json
|
|
inbox update --agent backend-worker --thread thr_123 --status blocked --summary "Need auth decision" --payload-json '{"question":"Should admin auth use email/password in MVP?"}' --json
|
|
inbox wait-reply --thread thr_123 --after-event 51 --timeout-seconds 1800 --json
|
|
inbox reply --from leader --to backend-worker --thread thr_123 --kind answer --summary "Use email/password for MVP" --body "Use a simple credential flow for the first iteration." --json
|
|
inbox done --agent backend-worker --thread thr_123 --summary "Post CRUD implemented" --body-file result.md --json
|
|
```
|
|
```
|
|
````
|