Inbox
The Inbox is the workspace → user push channel. When an agent working inside a Workspace has something the user should see — a finished analysis, a question back, a blocked task, a status check-in — it calls the inbox_push MCP tool. The entry surfaces in a dedicated Inbox tab in the Web UI.
Linear-style sidebar on the left (entries grouped by date), a detail pane on the right that renders any pointed-to documents live from the workspace folder, with the agent's markdown commentary below.
inbox_push
inbox_push is a workspace-scoped MCP tool exposed only at the per-workspace endpoint /mcp/:wsId. The agent never sees its own workspaceId — the MCP router fills that in from the URL path, making forgery impossible.
inbox_push({
docs?: Array<{ path: string }>, // paths relative to workspace root
comments?: string // markdown — your voice to the user
})
At least one of docs or comments must be present.
docs— pointers to files in the workspace. Rendered live in the inbox UI when the user opens the entry; no snapshot is taken. Later edits to the file are reflected on subsequent reads. The workspace folder is the source of truth.comments— markdown body. The agent's voice to the user about what it did or wants to ask. Renders below docs in the detail pane.
The split is deliberate. The atomic unit here is the workspace (the work product is the folder's files), not a one-shot comment authored at notification time. Pointer-only on docs avoids creating a stale parallel copy in the inbox; commentary is the message body that wouldn't otherwise have a place to live.
UI
The Inbox tab follows Linear's inbox pattern:
- Sidebar — entries grouped by date, newest first. Per-entry read state (a row dims once you've opened it) and a trash button for hard-delete (also accessible via Delete keyboard shortcut).
- Detail pane — workspace label header → live-rendered
docs(Markdown / text / code; binary files surface as a download link) →commentsmarkdown below. - Reply bar — at the bottom of the detail pane, a Linear-style reply bar that jumps back into the workspace's session. Clicking it focuses the workspace's terminal so you can continue the conversation in the agent's native CLI.
Persistence
Inbox entries are stored as append-only JSONL at data/inbox/entries.jsonl. One line per entry:
{
"id": "uuid-v4",
"ts": 1715789012345,
"workspaceId": "ws-abc123",
"workspaceLabel": "Macro thesis 2026-05",
"docs": [{ "path": "research/macro-2026-05-14.md" }],
"comments": "Drafted the FOMC piece. Want a take on the rates section before I extend."
}
Deletes rewrite the file atomically (tmp + rename) so a crash mid-write can't leave a half-truncated JSONL. Unparseable lines are preserved on delete so malformed entries can't be used to accidentally wipe the file.
HTTP route
GET /api/inbox/history?limit=&before=&workspaceId= paginated, newest-first
POST /api/inbox/seed dev-only: append an entry
The Web UI polls /history every 20 seconds. The seed route is intended for development and lets you append entries without running an agent — useful for iterating on the Inbox UI.
Inbox vs Notifications
Inbox is distinct from the legacy Notifications surface, which is now demoted into the Chat sidebar's Traditional section. The two serve different paths:
| Inbox | Notifications | |
|---|---|---|
| Origin | Workspace agent via inbox_push | Legacy automation: heartbeat, cron jobs calling notify_user |
| Body | Live-rendered docs + markdown commentary | One-shot message snapshotted at fire time |
| Reply | Jumps back into the workspace's session | Replies into the legacy chat |
| Status | Active, recommended for new workflows | Legacy, retained for pre-Workspace automation paths |
notify_user is marked deprecated for workspace contexts — inside a workspace, use inbox_push instead. Legacy heartbeat / cron flows still use notify_user until they migrate to workspace-resident execution; see Async Lifecycle for the two-layer execution model.
When to push
The tool description encourages agents to push when there's something the user should see, not on every operation. Heuristics:
- Finished a piece of work the user asked for → push with a
docspointer to the report. - Blocked on a decision the user has to make → push with
commentsasking the question. - Status check-in on a long-running job → optional
commentssummary plus any artifacts asdocs. - Internal tool calls, intermediate scratch, debugging output → don't push. That's what the workspace terminal is for.