Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 96 additions & 8 deletions SPECIFICATION.md

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@
"type": "string",
"enum": ["retrieval", "grounding", "citation"],
"description": "Conformance level advertised by this participant's own emitter(s). Informational. (Sections 8.5, 5.7)"
},
"ctx_resolution": {
"type": "string",
"format": "uri",
"pattern": "^https://",
"description": "HTTPS URL of the click-token resolution endpoint operated by or for this participant. Valid on agent and platform manifests. Destinations resolve ctx_iss to this manifest and present the token here. (Section 7.4)"
}
},
"description": "Telemetry endpoint declaration. (Section 8.5)"
Expand Down
25 changes: 23 additions & 2 deletions telemetry-event-batch.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
},
"ctx_token": {
"type": "string",
"description": "Opaque click-token issued by the originating agent, carried in place of session_id on batches of content_engaged events emitted from a landing page after a click-out. Applies to every event in the batch and is resolved by the telemetry consumer to the owning session. See section 7.1. An event MUST carry either session_id or ctx_token at Grounding conformance and above."
"pattern": "^ct_[A-Za-z0-9_-]{8,240}$",
"description": "Opaque click token issued by the originating agent, carried in place of session_id on batches of content_engaged events emitted from a landing page after a click-out. Applies to every event in the batch and is resolved by the telemetry consumer to the click context (section 7.4). An event MUST carry either session_id or ctx_token at Grounding conformance and above."
},
"agent_id": {
"type": "string",
Expand All @@ -47,5 +48,25 @@
},
"description": "The telemetry events in the batch"
}
}
},
"allOf": [
{
"if": {
"not": { "required": ["ctx_token"] }
},
"then": {
"properties": {
"events": {
"items": {
"if": {
"properties": { "type": { "const": "content_engaged" } },
"required": ["type"]
},
"then": { "required": ["presentation_id"] }
}
}
}
}
}
]
}
24 changes: 22 additions & 2 deletions telemetry-event.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
},
"ctx_token": {
"type": "string",
"description": "Opaque click-token issued by the originating agent, carried on content_engaged events emitted from a landing page after a click-out in place of session_id. Resolved by the telemetry consumer to the owning session (the click manifest). See section 7.1. An event MUST carry either session_id or ctx_token at Grounding conformance and above."
"pattern": "^ct_[A-Za-z0-9_-]{8,240}$",
"description": "Opaque click token issued by the originating agent, carried on content_engaged events emitted from a landing page after a click-out in place of session_id. Resolved by the telemetry consumer to the click context (section 7.4). An event MUST carry either session_id or ctx_token at Grounding conformance and above."
},
"agent_id": {
"type": "string",
Expand All @@ -43,5 +44,24 @@
"$ref": "telemetry-session.json#/$defs/TelemetryEvent",
"description": "The telemetry event"
}
}
},
"allOf": [
{
"if": {
"required": ["event"],
"properties": {
"event": {
"properties": { "type": { "const": "content_engaged" } },
"required": ["type"]
}
},
"not": { "required": ["ctx_token"] }
},
"then": {
"properties": {
"event": { "required": ["presentation_id"] }
}
}
}
]
}
21 changes: 17 additions & 4 deletions telemetry-session.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,18 @@
"events": {
"type": "array",
"items": {
"$ref": "#/$defs/TelemetryEvent"
"allOf": [
{ "$ref": "#/$defs/TelemetryEvent" },
{
"if": {
"properties": { "type": { "const": "content_engaged" } },
"required": ["type"]
},
"then": { "required": ["presentation_id"] }
}
]
},
"description": "Ordered list of events in the session (chronological by timestamp)"
"description": "Ordered list of events in the session (chronological by timestamp). Session documents are agent-reported, so content_engaged events here always carry presentation_id; the ctx_token relaxation applies only to standalone/batch envelopes (section 7.4)."
}
},
"$defs": {
Expand Down Expand Up @@ -103,7 +112,12 @@
"presentation_id": {
"type": "string",
"format": "uuid",
"description": "The id of the exact content_presented event on which the engagement occurred. REQUIRED on content_engaged events."
"description": "The id of the exact content_presented event on which the engagement occurred. REQUIRED on agent-reported content_engaged events. Destination-reported events carrying ctx_token omit it; the consumer restores the binding at resolution (section 7.4)."
},
"ctx_token": {
"type": "string",
"pattern": "^ct_[A-Za-z0-9_-]{8,240}$",
"description": "The click token the agent minted for this engagement's presentation, recorded so the consumer can join destination-reported events to it. Valid only on content_engaged events. See section 7.4."
},
"source_role": {
"$ref": "#/$defs/SourceRole",
Expand Down Expand Up @@ -240,7 +254,6 @@
"required": ["type"]
},
"then": {
"required": ["presentation_id"],
"properties": {
"data": {
"properties": {
Expand Down
12 changes: 12 additions & 0 deletions tests/invalid/ctx-token-bad-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"_test_description": "ctx_token failing the value pattern: tokens are opaque but MUST match ^ct_[A-Za-z0-9_-]{8,240}$ so they survive URL carriage and are recognisable in logs (section 7.4). This value has no ct_ prefix and contains reserved characters.",
"document_type": "event",
"schema_version": "0.1",
"ctx_token": "session=660e8400!",
"event": {
"type": "content_engaged",
"timestamp": "2026-08-10T11:20:00Z",
"content_url": "https://news.example.com/markets/rate-decision",
"data": { "engagement_type": "link_click" }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"_test_description": "Agent-reported standalone content_engaged with session_id but no presentation_id. The presentation_id relaxation applies only to envelopes carrying ctx_token; an emitter that knows the session knows the presentation and MUST bind to it (sections 6.8, 7.4).",
"document_type": "event",
"schema_version": "0.1",
"session_id": "660e8400-e29b-41d4-a716-446655440230",
"agent_id": "assistant-v2",
"started_at": "2026-08-10T12:00:00Z",
"event": {
"type": "content_engaged",
"timestamp": "2026-08-10T12:00:30Z",
"content_url": "https://news.example.com/markets/rate-decision",
"data": { "engagement_type": "link_click" }
}
}
3 changes: 1 addition & 2 deletions tests/valid/event-standalone-engaged-ctx-token.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
{
"_test_description": "Standalone content_engaged (link_click) reported from a landing page after a click-out. Carries ctx_token in place of session_id (section 7.1): the destination corroborates the click without receiving the session UUID. The telemetry consumer resolves ctx_token to the originating session's click manifest.",
"_test_description": "Standalone content_engaged (link_click) reported from a landing page after a click-out. Carries ctx_token in place of session_id and no presentation_id: the destination cannot know the presentation UUID, and the consumer restores the token's presentation binding at resolution (section 7.4).",
"document_type": "event",
"schema_version": "0.1",
"ctx_token": "ct_9f3a1c7e2b8d4a06",
"event": {
"type": "content_engaged",
"timestamp": "2026-03-28T14:06:00Z",
"presentation_id": "990e8400-e29b-41d4-a716-446655440064",
"content_url": "https://www.example-review.com/headphones/best-noise-cancelling",
"data": {
"engagement_type": "link_click"
Expand Down
14 changes: 14 additions & 0 deletions tests/valid/event-standalone-engaged-redirect.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"_test_description": "Destination-reported click after a redirect chain: the presented short link redirected to the canonical URL, and the ctx_token and ctx_iss query parameters were propagated through the same-domain hops (section 7.4). The destination reports the canonical URL it serves; correlation runs through the token, not the URL.",
"document_type": "event",
"schema_version": "0.1",
"ctx_token": "ct_77b41f0ac93e5d28",
"event": {
"type": "content_engaged",
"timestamp": "2026-08-10T11:15:42Z",
"content_url": "https://shop.example.com/products/anc-headphones-x9",
"data": {
"engagement_type": "link_click"
}
}
}
12 changes: 12 additions & 0 deletions tests/valid/manifest-agent-ctx-resolution.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"_test_description": "Agent manifest declaring a click-token resolution endpoint in telemetry.ctx_resolution. A destination receiving ctx_iss=searchco.com/agents/web-search resolves this manifest and presents the token at the declared endpoint (sections 7.4, 8.5).",
"schema_version": "0.1",
"id": "https://searchco.com/agents/web-search/.well-known/content-telemetry.json",
"roles": ["agent"],
"operator": { "name": "SearchCo" },
"telemetry": {
"endpoint": "https://telemetry.example.com/v1/events",
"conformance_level": "citation",
"ctx_resolution": "https://telemetry.example.com/v1/ctx/resolve"
}
}
53 changes: 53 additions & 0 deletions tests/valid/session-earlier-turn-click.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"_test_description": "A click in a later turn on a presentation from an earlier turn: the content_engaged event carries the turn of the click but binds by presentation_id to the turn-1 presentation. Resolution returns the clicked content's lineage across turns by content identity, not a turn or timestamp cut (section 7.4).",
"schema_version": "0.1",
"session_id": "660e8400-e29b-41d4-a716-446655440220",
"agent_id": "assistant-v2",
"started_at": "2026-08-10T10:00:00Z",
"events": [
{
"type": "content_grounded",
"timestamp": "2026-08-10T10:00:01Z",
"turn_id": "1",
"content_id": "publisher:feature:512",
"data": { "scope": "session", "chars_ingested": 15200 }
},
{
"id": "770e8400-e29b-41d4-a716-446655440221",
"type": "content_cited",
"timestamp": "2026-08-10T10:00:05Z",
"turn_id": "1",
"output_id": "response:1",
"content_id": "publisher:feature:512",
"data": { "citation_type": "reference", "position": "primary" }
},
{
"id": "880e8400-e29b-41d4-a716-446655440222",
"type": "content_presented",
"timestamp": "2026-08-10T10:00:05Z",
"turn_id": "1",
"output_id": "response:1",
"citation_id": "770e8400-e29b-41d4-a716-446655440221",
"content_id": "publisher:feature:512",
"content_url": "https://publisher.example.com/features/512",
"data": { "presentation_kind": "source_reference", "presentation_type": "link" }
},
{
"type": "content_grounded",
"timestamp": "2026-08-10T10:03:00Z",
"turn_id": "3",
"content_id": "otherpub:brief:77",
"data": { "scope": "turn", "chars_ingested": 2100 }
},
{
"type": "content_engaged",
"timestamp": "2026-08-10T10:04:12Z",
"turn_id": "3",
"presentation_id": "880e8400-e29b-41d4-a716-446655440222",
"ctx_token": "ct_e5a0b6d92c17f480",
"content_id": "publisher:feature:512",
"content_url": "https://publisher.example.com/features/512",
"data": { "engagement_type": "link_click" }
}
]
}
54 changes: 54 additions & 0 deletions tests/valid/session-repeated-link-engagement.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"_test_description": "The same URL presented twice in one session: two content_presented events with distinct ids and distinct minted click tokens, and a content_engaged bound by presentation_id to the second occurrence. Matching on URL alone could not tell the two presentations apart (sections 6.8, 7.4).",
"schema_version": "0.1",
"session_id": "660e8400-e29b-41d4-a716-446655440210",
"agent_id": "assistant-v2",
"started_at": "2026-08-10T09:00:00Z",
"events": [
{
"type": "content_grounded",
"timestamp": "2026-08-10T09:00:01Z",
"turn_id": "1",
"content_url": "https://news.example.com/markets/rate-decision",
"content_id": "newsex:article:8841",
"data": { "scope": "session", "chars_ingested": 9400 }
},
{
"id": "770e8400-e29b-41d4-a716-446655440211",
"type": "content_cited",
"timestamp": "2026-08-10T09:00:04Z",
"turn_id": "1",
"output_id": "response:1",
"content_url": "https://news.example.com/markets/rate-decision",
"data": { "citation_type": "paraphrase", "position": "primary" }
},
{
"id": "880e8400-e29b-41d4-a716-446655440212",
"type": "content_presented",
"timestamp": "2026-08-10T09:00:04Z",
"turn_id": "1",
"output_id": "response:1",
"citation_id": "770e8400-e29b-41d4-a716-446655440211",
"content_url": "https://news.example.com/markets/rate-decision",
"data": { "presentation_kind": "source_reference", "presentation_type": "link" }
},
{
"id": "880e8400-e29b-41d4-a716-446655440213",
"type": "content_presented",
"timestamp": "2026-08-10T09:02:10Z",
"turn_id": "2",
"output_id": "response:2",
"content_url": "https://news.example.com/markets/rate-decision",
"data": { "presentation_kind": "source_reference", "presentation_type": "link" }
},
{
"type": "content_engaged",
"timestamp": "2026-08-10T09:02:31Z",
"turn_id": "2",
"presentation_id": "880e8400-e29b-41d4-a716-446655440213",
"ctx_token": "ct_r2d1a9c44be07f31",
"content_url": "https://news.example.com/markets/rate-decision",
"data": { "engagement_type": "link_click" }
}
]
}
Loading