Skip to content

Admin — Inbox

The Inbox is the approval queue for agent-requested human-in-the-loop actions. Agents can be configured to request human approval before performing sensitive operations — configuration changes, knowledge-base uploads, destructive tool calls — and those requests land here in PENDING state.

This is the seam between automation and oversight: agents do work autonomously until they hit something that a human needs to confirm, and the Inbox is where that confirmation happens.

Route: /inbox
File: apps/admin/src/routes/_authed/_tenant/inbox/index.tsx

The list is split into two tabs: Pending and Resolved. Each row shows:

ColumnWhat it shows
RequestType icon, title, and an optional revision-version badge
TypeA badge for the request category (config change, knowledge upload, tool invocation, etc.)
StatusIcon + label — PENDING, APPROVED, REJECTED, REVISION_REQUESTED
RequestedRelative time since the agent opened the request

Title search filters both tabs. The counts in the tab headers show pending + resolved counts without firing additional queries — they’re computed from the same InboxItemsListQuery response and split client-side.

Revisions are tracked explicitly: if an approver asked for changes and the agent resubmitted, the version number ticks and appears as a badge so the reviewer can see the history.

Route: /inbox/:inboxItemId
File: apps/admin/src/routes/_authed/_tenant/inbox/$inboxItemId.tsx

Clicking an item opens the full review screen. The layout is a wide main column (lg:col-span-2) with the request details plus a narrow sidebar.

  • Item details card — type, revision number, description, the full payload rendered by a type-specific component (InboxItemPayloadRenderer), plus a “Raw JSON” toggle for the underlying payload and any prior review notes
  • Linked threads — threads that this request is associated with (typically the thread that triggered the agent to open the request)
  • Comments — a simple comment log. Approvers can post review context that’s visible to future reviewers and to the agent on resubmission
  • Activity timeline — a chronological log of state transitions, populated by ActivityLogQuery

The right sidebar carries the item’s metadata: status, type, revision, entity type, decided date, expires date, created date. The status is not editable from the sidebar — actions live in the header button group.

Four buttons appear in the item header when the item is actionable (PENDING or REVISION_REQUESTED):

ButtonEffect
ApproveApproveInboxItemMutation — the agent’s action proceeds
RejectRejectInboxItemMutation — the agent is notified and the action does not execute
Request RevisionOpens a prompt for review notes, then fires RequestRevisionMutation
ResubmitOnly visible on a revision path — ResubmitInboxItemMutation

Each mutation triggers a refetch of the item detail so the UI reflects the new state immediately.

PENDING ──► APPROVED
├──► REJECTED
└──► REVISION_REQUESTED ──► PENDING (on resubmit)
└──► REJECTED
└──► APPROVED
  • A PENDING item can move to APPROVED, REJECTED, or REVISION_REQUESTED
  • A REVISION_REQUESTED item comes back to PENDING when the agent resubmits with changes, and can still go directly to APPROVED or REJECTED from the revision state
  • APPROVED and REJECTED are terminal — they move the item to the Resolved tab and the action buttons disappear

Different request types have different payload shapes, so the admin app uses InboxItemPayloadRenderer — a type-dispatch component that picks the right child renderer based on the item’s type field. Examples:

  • Config change requests render the field-level diff between current and proposed configuration
  • Knowledge upload requests render the file list and a preview of the first document
  • Tool invocation requests render the tool name, input arguments, and any pre-computed risk assessment
  • Workspace review requests (type='workspace_review') render the review file body, target path, and a preview of the first three proposed changes — see the next section

Unknown or unhandled types fall back to a raw JSON view with the same “Raw JSON” toggle as the known-type renderers.

Inbox is also the home for system-agent workspace reviews — pauses from agents that have no human pair (eval test runtimes, platform-managed system agents, or runs whose parent_agent_id chain didn’t terminate at a paired human). These appear automatically as inbox items with type='workspace_review' whenever a system run pauses; no operator action is required to populate the queue.

Paired-human reviews do NOT appear here. They live on the responsible human’s mobile threads. The chain-walk classification happens server-side; the Inbox only sees system and unrouted runs.

When the workspace event processor handles a review.requested event:

  1. It classifies the run by walking agents.parent_agent_id from the run’s agent.
  2. If the chain resolves to a paired human, nothing is materialized in Inbox — that review surfaces on the human’s mobile.
  3. If the chain terminates at source='system' (or hits the depth cap / orphan), an inbox_items row is inserted with type='workspace_review', entity_type='agent_workspace_run', entity_id=<run id>, and config carrying the review body, proposed changes, and target path.

The materialization is idempotent: replaying the same review.requested event for the same run does not produce duplicate inbox items.

When an operator decides on a type='workspace_review' inbox item, the inbox mutation dispatches the matching workspace review action against the linked run. The inbox item’s status updates and the run’s lifecycle advances in the same call:

Inbox actionWorkspace effect
ApproveacceptAgentWorkspaceReview — run resumes with approval; workspace_event wakeup is enqueued.
RejectcancelAgentWorkspaceReview — run is marked cancelled; no wakeup.
Request revisionresumeAgentWorkspaceRun with the review notes carried as responseMarkdown — run resumes; agent reads the notes and addresses them in its next turn.

Operators only call the inbox mutations; the dispatch happens server-side. The legacy requester-agent wakeup that fires for other inbox types is skipped for workspace_review because the workspace review action handles run state directly.

Reviews whose chain has neither a human pair nor a source='system' terminator (orphan agents, cycles, depth-cap hits) materialize as inbox items too, but the title is prefixed Workspace review (unrouted): … and the payload renderer shows a warning marker. These usually indicate bad data — a human_pair_id that should be set, a parent_agent_id cycle, or a chain too deep to walk. Treat them as a signal to investigate the agent record before acting.

Inbox decisions are operator-driven today. The underlying mutations (approveInboxItem, rejectInboxItem, requestRevisionInboxItem) and the workspace review actions they bridge to (acceptAgentWorkspaceReview, cancelAgentWorkspaceReview, resumeAgentWorkspaceRun) live on the GraphQL surface and can be called by trusted services with a tenant API key — but there is no first-class agent tool, MCP server, or skill that wraps them yet. An agent that wants to drain its tenant’s system Inbox (a triage agent, for example) does so today by calling the GraphQL surface directly through the same authentication path used for any other tenant-scoped query. A vetted Strands tool for review resolution is plausible follow-up work.

The CLI scaffolds thinkwork inbox list / get / approve / reject / request-revision under apps/cli/src/commands/inbox.ts, but each subcommand is currently a stub returning notYetImplemented. Use the GraphQL surface or the admin UI until the CLI lands.

  • The cutover backfill script (one-shot, idempotent) lives at packages/api/scripts/backfill-system-reviews-to-inbox.ts and was used to materialize inbox items for any system runs that pre-dated the routing refactor.
  • The underlying routing model — chain walk, mobile vs Inbox split, sub-agent surfacing — is documented in Workspace Orchestration → Human review flow.
  1. Open /inbox
  2. Filter to Pending (default tab)
  3. Click the row
  4. Read the payload’s diff renderer
  5. If it looks right, click Approve
  6. The item moves to Resolved and the agent’s config change executes
  1. Click a pending item
  2. Click Request Revision
  3. Enter review notes in the prompt dialog
  4. The item moves to REVISION_REQUESTED with your notes attached
  5. The agent receives the feedback, makes changes, and resubmits — the item returns to PENDING with an incremented revision number
  1. Go to the Resolved tab
  2. Filter by title or scroll to the item
  3. Click through to see the final state, all comments, and the full activity timeline
  4. The linked threads section shows which threads triggered the request
  • Review notes prompt is a window.prompt(). The Request Revision flow uses the browser’s native prompt dialog rather than a styled modal. Functional, not polished.
  • No filtering by type. The list supports search by title but doesn’t have type-faceted filtering.
  • Type-specific renderers are opt-in. Agent types that haven’t shipped a dedicated payload renderer fall back to raw JSON. Operators can still approve them, but the presentation isn’t as clean.
  • Threads — inbox items usually link back to a parent thread
  • Agents — configure which actions require approval per agent template
  • Security Center — policy and guardrail enforcement
  • Control (concept) — the product model for approvals and controls