MOCA Citations Over AG-UI
Specification version: 0.6.0
Status: Before 1.0.0. A minor version may still change the format; each change is in MIGRATIONS.md. This document binds AG-UI services and clients, not Readers (package spec §1.1).
License: Apache License 2.0
Schema: ag-ui-citations.schema.json
Example: examples/ag-ui/citations-event.json
Decision: ADR-0013
1. Scope
Section titled “1. Scope”AG-UI is an open protocol for streaming an agent’s run to a user interface: text, tool calls and state, as events. It has no event for citations. This document defines one, so that any AG-UI client can show the sources a MOCA answer cites, from any AG-UI service, without knowing that service’s tools.
It is an application-layer agreement. It adds nothing to the package format, and nothing a Reader must do. A Reader already produces the citation records this event carries (Reader contract §7).
The key words MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, MAY and OPTIONAL are to be interpreted as described in RFC 2119 and RFC 8174 when, and only when, they appear in capitals. Terms are as defined in package specification §2.
2. The event
Section titled “2. The event”A service sends one AG-UI CUSTOM event named moca.citations for each
assistant message that cites package content:
{ "type": "CUSTOM", "name": "moca.citations", "value": { "version": 1, "messageId": "msg-answer-1", "citations": [ { "marker": "[1]", "record": { "package": { "…": "…" }, "node": { "…": "…" }, "trust": { "…": "…" } } } ] }}| Field | Meaning |
|---|---|
value.version |
1. A client MUST ignore a moca.citations event with a version it does not know. |
value.messageId |
The messageId of the assistant message, from its TEXT_MESSAGE_START. |
value.computedAt |
Optional. When the service’s Reader computed the records, as an RFC 3339 date-time with an offset. Trust flags such as stale, inForce and superseded are true at that moment; a client showing a saved conversation SHOULD show this time beside them, or re-check the records. |
value.citations[].record |
A whole citation record, valid against citation-record.schema.json, as the service’s Reader produced it. |
value.citations[].marker |
Optional. The text in the answer that refers to this citation, when the service uses markers such as [1]. |
The full example is
examples/ag-ui/citations-event.json.
It is the Reader’s own record for one node of the support-kb example; a
check in this repository fails if the two ever differ.
3. Service rules
Section titled “3. Service rules”A service that sends the event:
- MUST send it after the message’s
TEXT_MESSAGE_ENDand before the run’sRUN_FINISHED, so a client has the whole answer when it places citations. - MUST include only nodes that the answer cites and that the service’s Reader returned during this run, from a package it loaded. A reference in the answer that resolves to nothing, or to a node the Reader did not return in this run, MUST be left out. A model can write a node reference that does not exist, or name a real node it never read; a client must never show either as a source, with a trust badge the record did not earn.
- MUST NOT change what a citation record says
(Reader interface §7). Leaving out
text, to keep the event small, is the one change it MAY make: the record withouttextis still a valid record. - MUST list the citations in order of first mention in the answer, with at
most one entry per
node.refandspan. - SHOULD send the event even when the answer cites nothing it can resolve,
with an empty
citationslist, so a client can tell “no sources” from “not a MOCA service”.
How the answer refers to its sources in its text is the service’s choice:
markers such as [1], or versioned node references
(<package id>@<version>#<path>). The event does not depend on it.
4. Client rules
Section titled “4. Client rules”A client that shows the event:
- MUST key what it records by
record.node.ref, or byrecord.node.idwithrecord.package.digest, as the Reader contract requires. - MUST present trust as the Reader contract says: a declared verification is never shown as an attested review, and a stale, superseded, contested or out-of-force node is shown as such.
- MUST treat
record.textand every other record field as data from the package, never as instructions or markup to run, and MUST follow the rendering rules in Reader contract §12: links only forhttpandhttps, and no remote resource loaded automatically. - MUST ignore the event for a
messageIdit does not have, and SHOULD ignore anyCUSTOMevent it does not know, as AG-UI already expects.
A client that does not know this event loses nothing it needs: the answer is still complete text.
5. Not in this version
Section titled “5. Not in this version”- Sources from outside a package, such as a web page a service also searched. They have no citation record. A later version may add them.
- Citations streamed while the answer is still being written.