Message labels are a first-class API concept with no read path in the dashboard. You can set them, filter on them, and scope webhooks by them — but you cannot see them.
Current state
PATCH /v1/agents/{email}/messages/{id} applies a labels delta (add_labels / remove_labels), and list_messages supports both the flat labels parameter and the AIP-160 filter grammar in docs/filtering.md (label:urgent, label:a OR label:b, NOT label:newsletter).
In web/, the message components render none of it. src/components/messages/ThreadList.tsx, ThreadRow.tsx, ThreadDetail.tsx, and AgentHeader.tsx contain no reference to the field. The only occurrence in the app is src/lib/webhooks.ts:233, where describeScope() summarizes a webhook subscription's filters as labels: a, b on /webhooks and /webhooks/detail — that describes event routing, not any message.
Why it matters
An agent that tags its own inbox over MCP produces state a human operator cannot review. Label a message spam or needs-review, open the dashboard, and the message looks identical to every other one. The categorization is real and queryable, just invisible to the person the triage was meant to inform.
It also makes the filter grammar hard to reach for. There is no way to discover which labels exist on an inbox from the UI, so label: queries require knowing the vocabulary in advance.
Proposed
- Label chips on
ThreadRow — render message.labels in the list row. e2a:-prefixed system labels probably want distinct styling from user labels.
- Labels in
ThreadDetail — full set on the opened message, ideally with add/remove wired to the existing PATCH endpoint.
- Filter affordance — a label filter in the messages view backed by
list_messages. Clicking a chip filtering to that label would be the cheap version.
(1) is the minimum that closes the gap; (2) and (3) are the natural follow-ons.
Notes
The frontend Message type may need the labels field threaded through — worth checking whether it is dropped at the API-client boundary or just unused.
Message labels are a first-class API concept with no read path in the dashboard. You can set them, filter on them, and scope webhooks by them — but you cannot see them.
Current state
PATCH /v1/agents/{email}/messages/{id}applies a labels delta (add_labels/remove_labels), andlist_messagessupports both the flatlabelsparameter and the AIP-160 filter grammar indocs/filtering.md(label:urgent,label:a OR label:b,NOT label:newsletter).In
web/, the message components render none of it.src/components/messages/ThreadList.tsx,ThreadRow.tsx,ThreadDetail.tsx, andAgentHeader.tsxcontain no reference to the field. The only occurrence in the app issrc/lib/webhooks.ts:233, wheredescribeScope()summarizes a webhook subscription's filters aslabels: a, bon/webhooksand/webhooks/detail— that describes event routing, not any message.Why it matters
An agent that tags its own inbox over MCP produces state a human operator cannot review. Label a message
spamorneeds-review, open the dashboard, and the message looks identical to every other one. The categorization is real and queryable, just invisible to the person the triage was meant to inform.It also makes the filter grammar hard to reach for. There is no way to discover which labels exist on an inbox from the UI, so
label:queries require knowing the vocabulary in advance.Proposed
ThreadRow— rendermessage.labelsin the list row.e2a:-prefixed system labels probably want distinct styling from user labels.ThreadDetail— full set on the opened message, ideally with add/remove wired to the existing PATCH endpoint.list_messages. Clicking a chip filtering to that label would be the cheap version.(1) is the minimum that closes the gap; (2) and (3) are the natural follow-ons.
Notes
The frontend
Messagetype may need thelabelsfield threaded through — worth checking whether it is dropped at the API-client boundary or just unused.