openvibers/openvibe.contracts Commits

Agent Streams S5: the digest, the poll and the four controls (0.303.0) (#313)

OP OpenVibers committed 6 days ago · c86fa1cc64 · 73 files, +4306 −104

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 22919b90..b9ffeecd 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -1,5 +1,15 @@
# Changelog
++## 0.303.0 — 2026-11-06
++
++- Add `chat.agent-digest@1` (Agent Streams S5): what a Co-pilot room said to an agent between two of its steps. The agent never reads the raw room: it asks Chat for a digest and gets at most ten items and 1,500 characters. Each item names who (the display name and the handle), what (cut to 200 characters), its kind (`question`, `suggestion` or `reaction`) and its upvotes; a repeated line is one item with the number behind it. Chat chooses what goes in (repeats grouped, greetings and spam dropped, the most-upvoted questions and suggestions first) and who is left out (a person who is timed out or banned, whose account is younger than a day, or who set 'Agents don't read my messages'). A link is kept as its domain only; the code and the commands stay as text.
++- Add `chat.agent-poll@1`: the poll an agent asks a Co-pilot room to run, and the result it gets back — the question and the two to four options the agent wrote, the window it is open for, the state (`open`, `closed` or `vetoed`) and the counts. An open poll carries no result, a closed one carries its counts and its winner, a vetoed one names who refused it.
++- Add `live.agent-stream-control@1`: one of the four controls the owner and the channel's moderators have over a live agent stream — `pause`, `curtain`, `mute` or `end` — with who pressed it (the handle and the role: owner or moderator) and when, and whose pause is in force.
++- Add the capabilities `chat.agent_digest.read` and `chat.agent_poll.write` (first-party), and name both in Chat's service manifest. The caller is Codes or Actor, the task's service, acting for the stream's owner (a node token `on_behalf_of`), so a digest is the owner's room and nobody else's.
++- `chat.preferences` is v2: it gains `agents_read_messages` ('Agents don't read my messages'). A v1 record upgrades with it true — joining a Co-pilot room and writing in it is the choice, and the person can take it back.
++- `live.agent_stream.publish` now names the controls (`POST /api/streams/agent/:id/control`, `GET /api/streams/agent/:id/controls`) and the step credit: a step may name the suggestion it took.
++- Additive: nothing that exists changes. Fixtures cover a digest over the ten-item cap, an unknown kind, a room that is not an agent room, a poll with one option and with five, an open poll carrying a result, an unknown control, a control with nobody behind it and a role that is neither owner nor moderator.
++
## 0.302.0 — 2026-11-06
- Add the capabilities `media.avatar.adopt` and `media.avatar.release` (internal), and name both in Media's service manifest. After the 2026-11-06 incident (eleven avatars were soft-deleted with the public screenshot copies), Media records which avatar object each profile slot points at, and its delete refuses an object a working profile still shows. A product (Network for `users.avatar_url`, Live for its own copy and the channel banner) records a slot with `POST /internal/avatar-adopt` and drops it with `POST /internal/avatar-release`.
ddiff --git a/STATUS.json b/STATUS.json
iindex 049d82a3..6b37df3a 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -11,6 +11,8 @@
"deployment": "none of its own: a library and artifact repository published as release tags; services pin a tag and deploy it with their own releases",
"contracts": "openvibe-contracts (this package: package.json's version, released as the matching tag)",
"features": [
++ "chat.agent-digest@1, chat.agent-poll@1 and live.agent-stream-control@1 (the Agent Streams plan, S5): the digest a Co-pilot room gives an agent, the poll an agent asks that room to run, and the four controls the owner and the moderators have over a live agent stream",
++ "the capabilities chat.agent_digest.read and chat.agent_poll.write, for Codes and Actor acting for the stream's owner",
"798 JSON Schemas with valid and invalid fixtures (at v0.210.0)",
"45 service manifests with lifecycle declarations (39 alpha, 1 beta, 4 stable, 1 retired)",
"348 capability manifests (306 active, 27 planned, 15 retired) and the staff capability map",
@@@ -36,11 +38,11 @@
"OpenVibe.Food, OpenVibe.Help and OpenVibe.Work become services (plan T19); OpenVibe.Quest and OpenVibe.Rent become services",
"the forum returns to Community; Deals goes live, with product copy for the new sites",
"OpenVibe.MediaHub goes live (openvibe.download as a private drive); services declare the account events they consume (ADR-033, SDK 0.37.0)",
-- "ADR-054 — the OpenVibe inventory authority (kinds, definitions, instances, equipped and the ledger), its planned service, capabilities, schemas and events (plan T21 step 1)",
++ "ADR-054 \u2014 the OpenVibe inventory authority (kinds, definitions, instances, equipped and the ledger), its planned service, capabilities, schemas and events (plan T21 step 1)",
"OpenVibe.Inventory goes live (inventory.openvibe.network, site position 30) with six capabilities active (ADR-054, plan T21 step 2); a definition may name the services or apps that may grant it",
"the Workshop v1 (ADR-054 section 6): Workshop kinds, network.badge, staff review (inventory.review-request@1, inventory.definition.review active) and media_id on equipped slots",
"News, Reviews, Trade, Coupons and OpenRestream go live on their own domains (plan T11); the product at openre.stream is OpenRestream, with OpenVibe Live on by default (live.openre.slot.bind)",
-- "ADR-055 (proposed): money and the inventory — primary sales first, gifting next; trading and a market stay closed, and chance is never paid for",
++ "ADR-055 (proposed): money and the inventory \u2014 primary sales first, gifting next; trading and a market stay closed, and chance is never paid for",
"bench.benchmark@1 and bench.run@1 with the capabilities bench.benchmark.write, bench.run.publish and bench.run.read: the benchmark registry and the results API (spec 2026-10-30, stage B1)"
],
"updated": "2026-10-30"
ddiff --git a/contracts/catalog.json b/contracts/catalog.json
iindex be3abdce..aacaaf58 100644
---- a/contracts/catalog.json
++++ b/contracts/catalog.json
@@@ -9511,6 +9511,33 @@
"compatibility": "backward",
"status": "active"
},
++ {
++ "id": "chat.agent-digest",
++ "version": "1.0.0",
++ "owner": "chat",
++ "schema": "chat/agent-digest.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
++ },
++ {
++ "id": "chat.agent-poll",
++ "version": "1.0.0",
++ "owner": "chat",
++ "schema": "chat/agent-poll.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
++ },
++ {
++ "id": "live.agent-stream-control",
++ "version": "1.0.0",
++ "owner": "live",
++ "schema": "live/agent-stream-control.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
++ },
{
"id": "live.agent_stream.changed",
"version": "1.0.0",
ddiff --git a/contracts/chat/agent-digest.v1.json b/contracts/chat/agent-digest.v1.json
nnew file mode 100644
iindex 00000000..10c170ed
---- /dev/null
++++ b/contracts/chat/agent-digest.v1.json
@@@ -0,0 +1,105 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/chat/agent-digest.v1.json",
++ "title": "ChatAgentDigest",
++ "description": "chat.agent-digest@1 (the Agent Streams plan, S5): what a chat room said to an agent between two of its steps, GET /api/v1/rooms/agent:<id>/digest?since= on OpenVibe.Chat. The agent never reads the raw room: it asks for a digest and gets at most ten items and 1,500 characters. Each item names who (the display name and the handle), what (cut to 200 characters), its kind (a question, a suggestion or a reaction) and its upvotes (a thumbs up on the message counts). Chat chooses what goes in: repeats are grouped, greetings and spam are dropped, and the most-upvoted questions and suggestions come first. Nobody is left out by accident: a person who is timed out or banned, whose account is younger than a day, or who set 'Agents don't read my messages' is never in the digest. A link is kept as its domain only ('a link to github.com'), because the agent never follows a link from chat. The code and the commands stay as text: the agent's own tools and approvals decide what may run, never chat.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "room",
++ "since",
++ "until",
++ "items"
++ ],
++ "properties": {
++ "room": {
++ "type": "string",
++ "pattern": "^agent:ags_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The room the digest came from: an agent stream's room, `agent:<ags_id>`."
++ },
++ "since": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "The start of the window: the moment the agent last read, or null for the first read."
++ },
++ "until": {
++ "type": "string",
++ "format": "date-time",
++ "description": "The end of the window: the moment this digest was made."
++ },
++ "items": {
++ "type": "array",
++ "maxItems": 10,
++ "description": "What chat said, at most ten items. The order is the one Chat chose: the most-upvoted questions and suggestions first, the reactions last.",
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "who",
++ "what",
++ "kind",
++ "upvotes"
++ ],
++ "properties": {
++ "who": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "display_name",
++ "handle"
++ ],
++ "description": "Who said it: the display name and the handle. Never an id, never an email, never anything else about them.",
++ "properties": {
++ "display_name": {
++ "type": "string",
++ "maxLength": 80,
++ "description": "The name the room shows."
++ },
++ "handle": {
++ "type": "string",
++ "pattern": "^[a-z0-9_]{2,32}$",
++ "description": "The handle, so the agent can answer this person by name."
++ }
++ }
++ },
++ "what": {
++ "type": "string",
++ "maxLength": 200,
++ "description": "What they said, cut to 200 characters. A link is kept as its domain only."
++ },
++ "kind": {
++ "enum": [
++ "question",
++ "suggestion",
++ "reaction"
++ ],
++ "description": "What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do."
++ },
++ "upvotes": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many thumbs up the message carries. The most-upvoted items come first."
++ },
++ "repeated": {
++ "type": "integer",
++ "minimum": 1,
++ "description": "How many people said this same thing. Chat groups repeats into one item; this is the number behind it."
++ }
++ }
++ }
++ },
++ "chars": {
++ "type": "integer",
++ "minimum": 0,
++ "maximum": 1500,
++ "description": "How many characters the items use together, at most 1,500."
++ },
++ "truncated": {
++ "type": "boolean",
++ "description": "True when Chat left something out because of the ten-item or 1,500-character cap."
++ }
++ }
++}
ddiff --git a/contracts/chat/agent-poll.v1.json b/contracts/chat/agent-poll.v1.json
nnew file mode 100644
iindex 00000000..1aff3f72
---- /dev/null
++++ b/contracts/chat/agent-poll.v1.json
@@@ -0,0 +1,221 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/chat/agent-poll.v1.json",
++ "title": "ChatAgentPoll",
++ "description": "chat.agent-poll@1 (the Agent Streams plan, S5): a poll the agent asks a Co-pilot room to run, and the result it gets back. The agent writes the question and two to four options ('Which name: parseRows or readTable?') and the poll is open for 30 to 120 seconds. Chat draws it as a poll card with buttons; `!1` to `!4` vote too. One vote per person, one poll at a time, and at most one poll every five minutes. The owner (or a moderator) can close a poll early or veto its result. A poll can only choose between the options the agent wrote: Chat never adds one. The result carries the counts, nothing else about who voted.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "poll_id",
++ "room",
++ "question",
++ "options",
++ "opened_at",
++ "closes_at",
++ "state"
++ ],
++ "properties": {
++ "poll_id": {
++ "type": "string",
++ "pattern": "^pol_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The poll's id (ULID, Crockford base32)."
++ },
++ "room": {
++ "type": "string",
++ "pattern": "^agent:ags_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The room the poll is in: an agent stream's room, `agent:<ags_id>`."
++ },
++ "question": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "The question the agent wrote."
++ },
++ "options": {
++ "type": "array",
++ "minItems": 2,
++ "maxItems": 4,
++ "description": "The options the agent wrote, two to four of them. Chat never adds an option of its own.",
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "label"
++ ],
++ "properties": {
++ "id": {
++ "type": "string",
++ "pattern": "^[1-4]$",
++ "description": "The option's number: `!1` to `!4` vote for it."
++ },
++ "label": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 80,
++ "description": "The option's text, as the agent wrote it."
++ },
++ "votes": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many people chose it. The count is in the result; it says nothing about who."
++ }
++ }
++ }
++ },
++ "opened_at": {
++ "type": "string",
++ "format": "date-time",
++ "description": "When the poll opened."
++ },
++ "closes_at": {
++ "type": "string",
++ "format": "date-time",
++ "description": "When the poll closes on its own: 30 to 120 seconds after it opened."
++ },
++ "closed_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When the poll was closed early by the owner or a moderator, or null when it ran its time."
++ },
++ "state": {
++ "enum": [
++ "open",
++ "closed",
++ "vetoed"
++ ],
++ "description": "Where the poll stands: open, closed (the counts are final), or vetoed (the owner or a moderator refused the result)."
++ },
++ "result": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": false,
++ "description": "The result, once the poll is closed: the counts and the winning option. Null while the poll is open.",
++ "required": [
++ "counts",
++ "total"
++ ],
++ "properties": {
++ "counts": {
++ "type": "array",
++ "minItems": 2,
++ "maxItems": 4,
++ "description": "How many votes each option got, in the order the options were written.",
++ "items": {
++ "type": "integer",
++ "minimum": 0
++ }
++ },
++ "total": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many people voted."
++ },
++ "winner": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "pattern": "^[1-4]$",
++ "description": "The option that won, or null when there was no vote or no winner."
++ }
++ }
++ },
++ "vetoed_by": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "description": "Who vetoed the result (the owner or a moderator), or null. Never a person's id: the handle only."
++ }
++ },
++ "allOf": [
++ {
++ "if": {
++ "properties": {
++ "state": {
++ "const": "open"
++ }
++ },
++ "required": [
++ "state"
++ ]
++ },
++ "then": {
++ "properties": {
++ "result": {
++ "type": "null"
++ },
++ "closed_at": {
++ "type": "null"
++ },
++ "vetoed_by": {
++ "type": "null"
++ }
++ }
++ }
++ },
++ {
++ "if": {
++ "properties": {
++ "state": {
++ "const": "closed"
++ }
++ },
++ "required": [
++ "state"
++ ]
++ },
++ "then": {
++ "required": [
++ "closed_at",
++ "result"
++ ],
++ "properties": {
++ "result": {
++ "type": "object"
++ },
++ "closed_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "vetoed_by": {
++ "type": "null"
++ }
++ }
++ }
++ },
++ {
++ "if": {
++ "properties": {
++ "state": {
++ "const": "vetoed"
++ }
++ },
++ "required": [
++ "state"
++ ]
++ },
++ "then": {
++ "required": [
++ "closed_at",
++ "vetoed_by"
++ ],
++ "properties": {
++ "closed_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "vetoed_by": {
++ "type": "string"
++ }
++ }
++ }
++ }
++ ]
++}
ddiff --git a/contracts/live/agent-stream-control.v1.json b/contracts/live/agent-stream-control.v1.json
nnew file mode 100644
iindex 00000000..2ec12ad5
---- /dev/null
++++ b/contracts/live/agent-stream-control.v1.json
@@@ -0,0 +1,64 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/live/agent-stream-control.v1.json",
++ "title": "LiveAgentStreamControl",
++ "description": "live.agent-stream-control@1 (the Agent Streams plan, S5): one of the four controls the owner and the channel's moderators have over a live agent stream, with who pressed it. PAUSE tells the task's service (Codes or Actor) to pause at the next safe point; the overlay then says 'Paused by @mod'. CURTAIN is the S1 curtain, raised from the viewer side. MUTE is the S4 mute: the agent voice stops. END ends the stream and the room's Co-pilot reading, while the task itself keeps running off camera unless it was paused. A moderator may press all four, but may not resume a pause the owner set and may not change the mode or the task. Every press is logged: who, which control, when.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "control",
++ "by",
++ "at"
++ ],
++ "properties": {
++ "control": {
++ "enum": [
++ "pause",
++ "curtain",
++ "mute",
++ "end"
++ ],
++ "description": "Which control was pressed."
++ },
++ "by": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "handle",
++ "role"
++ ],
++ "description": "Who pressed it: the handle and the role they hold. Never an id, never anything else about them.",
++ "properties": {
++ "handle": {
++ "type": "string",
++ "pattern": "^[a-z0-9_]{2,32}$",
++ "description": "The handle of the person who pressed the control."
++ },
++ "role": {
++ "enum": [
++ "owner",
++ "moderator"
++ ],
++ "description": "What they are on this stream: the owner, or one of the channel's moderators."
++ }
++ }
++ },
++ "at": {
++ "type": "string",
++ "format": "date-time",
++ "description": "When the control was pressed."
++ },
++ "stream": {
++ "type": "string",
++ "pattern": "^ags_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The agent stream the control was pressed on."
++ },
++ "paused_by": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "description": "The handle of the person whose pause is in force, or null when the stream is not paused. A moderator cannot resume the owner's pause: this says whose it is."
++ }
++ }
++}
ddiff --git a/fixtures/chat.agent-digest/invalid/eleven-items.json b/fixtures/chat.agent-digest/invalid/eleven-items.json
nnew file mode 100644
iindex 00000000..08168a26
---- /dev/null
++++ b/fixtures/chat.agent-digest/invalid/eleven-items.json
@@@ -0,0 +1,106 @@
++{
++ "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "since": null,
++ "until": "2026-11-06T10:05:00.000Z",
++ "items": [
++ {
++ "who": {
++ "display_name": "Mira",
++ "handle": "mira"
++ },
++ "what": "hi",
++ "kind": "reaction",
++ "upvotes": 0
++ },
++ {
++ "who": {
++ "display_name": "Mira",
++ "handle": "mira"
++ },
++ "what": "hi",
++ "kind": "reaction",
++ "upvotes": 0
++ },
++ {
++ "who": {
++ "display_name": "Mira",
++ "handle": "mira"
++ },
++ "what": "hi",
++ "kind": "reaction",
++ "upvotes": 0
++ },
++ {
++ "who": {
++ "display_name": "Mira",
++ "handle": "mira"
++ },
++ "what": "hi",
++ "kind": "reaction",
++ "upvotes": 0
++ },
++ {
++ "who": {
++ "display_name": "Mira",
++ "handle": "mira"
++ },
++ "what": "hi",
++ "kind": "reaction",
++ "upvotes": 0
++ },
++ {
++ "who": {
++ "display_name": "Mira",
++ "handle": "mira"
++ },
++ "what": "hi",
++ "kind": "reaction",
++ "upvotes": 0
++ },
++ {
++ "who": {
++ "display_name": "Mira",
++ "handle": "mira"
++ },
++ "what": "hi",
++ "kind": "reaction",
++ "upvotes": 0
++ },
++ {
++ "who": {
++ "display_name": "Mira",
++ "handle": "mira"
++ },
++ "what": "hi",
++ "kind": "reaction",
++ "upvotes": 0
++ },
++ {
++ "who": {
++ "display_name": "Mira",
++ "handle": "mira"
++ },
++ "what": "hi",
++ "kind": "reaction",
++ "upvotes": 0
++ },
++ {
++ "who": {
++ "display_name": "Mira",
++ "handle": "mira"
++ },
++ "what": "hi",
++ "kind": "reaction",
++ "upvotes": 0
++ },
++ {
++ "who": {
++ "display_name": "Mira",
++ "handle": "mira"
++ },
++ "what": "hi",
++ "kind": "reaction",
++ "upvotes": 0
++ }
++ ]
++}
ddiff --git a/fixtures/chat.agent-digest/invalid/room-not-an-agent-room.json b/fixtures/chat.agent-digest/invalid/room-not-an-agent-room.json
nnew file mode 100644
iindex 00000000..a5336c40
---- /dev/null
++++ b/fixtures/chat.agent-digest/invalid/room-not-an-agent-room.json
@@@ -0,0 +1,6 @@
++{
++ "room": "chat:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "since": null,
++ "until": "2026-11-06T10:05:00.000Z",
++ "items": []
++}
ddiff --git a/fixtures/chat.agent-digest/invalid/unknown-kind.json b/fixtures/chat.agent-digest/invalid/unknown-kind.json
nnew file mode 100644
iindex 00000000..9133b7de
---- /dev/null
++++ b/fixtures/chat.agent-digest/invalid/unknown-kind.json
@@@ -0,0 +1,16 @@
++{
++ "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "since": null,
++ "until": "2026-11-06T10:05:00.000Z",
++ "items": [
++ {
++ "who": {
++ "display_name": "Mira",
++ "handle": "mira"
++ },
++ "what": "hi",
++ "kind": "greeting",
++ "upvotes": 0
++ }
++ ]
++}
ddiff --git a/fixtures/chat.agent-digest/valid/empty.json b/fixtures/chat.agent-digest/valid/empty.json
nnew file mode 100644
iindex 00000000..137bd703
---- /dev/null
++++ b/fixtures/chat.agent-digest/valid/empty.json
@@@ -0,0 +1,8 @@
++{
++ "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "since": null,
++ "until": "2026-11-06T10:05:00.000Z",
++ "chars": 0,
++ "truncated": false,
++ "items": []
++}
ddiff --git a/fixtures/chat.agent-digest/valid/full.json b/fixtures/chat.agent-digest/valid/full.json
nnew file mode 100644
iindex 00000000..35892f47
---- /dev/null
++++ b/fixtures/chat.agent-digest/valid/full.json
@@@ -0,0 +1,28 @@
++{
++ "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "since": "2026-11-06T10:00:00.000Z",
++ "until": "2026-11-06T10:05:00.000Z",
++ "chars": 120,
++ "truncated": false,
++ "items": [
++ {
++ "who": {
++ "display_name": "Mira",
++ "handle": "mira"
++ },
++ "what": "why is the test flaky?",
++ "kind": "question",
++ "upvotes": 3
++ },
++ {
++ "who": {
++ "display_name": "Sam",
++ "handle": "sam"
++ },
++ "what": "a link to github.com",
++ "kind": "suggestion",
++ "upvotes": 2,
++ "repeated": 2
++ }
++ ]
++}
ddiff --git a/fixtures/chat.agent-poll/invalid/five-options.json b/fixtures/chat.agent-poll/invalid/five-options.json
nnew file mode 100644
iindex 00000000..cb9e8977
---- /dev/null
++++ b/fixtures/chat.agent-poll/invalid/five-options.json
@@@ -0,0 +1,35 @@
++{
++ "poll_id": "pol_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "question": "Which name?",
++ "options": [
++ {
++ "id": "1",
++ "label": "a",
++ "votes": 0
++ },
++ {
++ "id": "2",
++ "label": "b",
++ "votes": 0
++ },
++ {
++ "id": "3",
++ "label": "c",
++ "votes": 0
++ },
++ {
++ "id": "4",
++ "label": "d",
++ "votes": 0
++ },
++ {
++ "id": "5",
++ "label": "e",
++ "votes": 0
++ }
++ ],
++ "opened_at": "2026-11-06T10:00:00.000Z",
++ "closes_at": "2026-11-06T10:01:00.000Z",
++ "state": "open"
++}
ddiff --git a/fixtures/chat.agent-poll/invalid/one-option.json b/fixtures/chat.agent-poll/invalid/one-option.json
nnew file mode 100644
iindex 00000000..5d49c355
---- /dev/null
++++ b/fixtures/chat.agent-poll/invalid/one-option.json
@@@ -0,0 +1,15 @@
++{
++ "poll_id": "pol_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "question": "Which name?",
++ "options": [
++ {
++ "id": "1",
++ "label": "parseRows",
++ "votes": 0
++ }
++ ],
++ "opened_at": "2026-11-06T10:00:00.000Z",
++ "closes_at": "2026-11-06T10:01:00.000Z",
++ "state": "open"
++}
ddiff --git a/fixtures/chat.agent-poll/invalid/open-poll-with-a-result.json b/fixtures/chat.agent-poll/invalid/open-poll-with-a-result.json
nnew file mode 100644
iindex 00000000..37717943
---- /dev/null
++++ b/fixtures/chat.agent-poll/invalid/open-poll-with-a-result.json
@@@ -0,0 +1,28 @@
++{
++ "poll_id": "pol_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "question": "Which name?",
++ "options": [
++ {
++ "id": "1",
++ "label": "a",
++ "votes": 0
++ },
++ {
++ "id": "2",
++ "label": "b",
++ "votes": 0
++ }
++ ],
++ "opened_at": "2026-11-06T10:00:00.000Z",
++ "closes_at": "2026-11-06T10:01:00.000Z",
++ "state": "open",
++ "result": {
++ "counts": [
++ 1,
++ 0
++ ],
++ "total": 1,
++ "winner": "1"
++ }
++}
ddiff --git a/fixtures/chat.agent-poll/valid/closed.json b/fixtures/chat.agent-poll/valid/closed.json
nnew file mode 100644
iindex 00000000..3b672c34
---- /dev/null
++++ b/fixtures/chat.agent-poll/valid/closed.json
@@@ -0,0 +1,30 @@
++{
++ "poll_id": "pol_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "question": "Which name: parseRows or readTable?",
++ "options": [
++ {
++ "id": "1",
++ "label": "parseRows",
++ "votes": 4
++ },
++ {
++ "id": "2",
++ "label": "readTable",
++ "votes": 1
++ }
++ ],
++ "opened_at": "2026-11-06T10:00:00.000Z",
++ "closes_at": "2026-11-06T10:01:00.000Z",
++ "closed_at": "2026-11-06T10:01:00.000Z",
++ "state": "closed",
++ "result": {
++ "counts": [
++ 4,
++ 1
++ ],
++ "total": 5,
++ "winner": "1"
++ },
++ "vetoed_by": null
++}
ddiff --git a/fixtures/chat.agent-poll/valid/open.json b/fixtures/chat.agent-poll/valid/open.json
nnew file mode 100644
iindex 00000000..ca183ff7
---- /dev/null
++++ b/fixtures/chat.agent-poll/valid/open.json
@@@ -0,0 +1,23 @@
++{
++ "poll_id": "pol_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "question": "Which name: parseRows or readTable?",
++ "options": [
++ {
++ "id": "1",
++ "label": "parseRows",
++ "votes": 0
++ },
++ {
++ "id": "2",
++ "label": "readTable",
++ "votes": 0
++ }
++ ],
++ "opened_at": "2026-11-06T10:00:00.000Z",
++ "closes_at": "2026-11-06T10:01:00.000Z",
++ "closed_at": null,
++ "state": "open",
++ "result": null,
++ "vetoed_by": null
++}
ddiff --git a/fixtures/live.agent-stream-control/invalid/no-one.json b/fixtures/live.agent-stream-control/invalid/no-one.json
nnew file mode 100644
iindex 00000000..0f0ea56d
---- /dev/null
++++ b/fixtures/live.agent-stream-control/invalid/no-one.json
@@@ -0,0 +1,4 @@
++{
++ "control": "pause",
++ "at": "2026-11-06T10:00:00.000Z"
++}
ddiff --git a/fixtures/live.agent-stream-control/invalid/role-is-not-one-of-the-two.json b/fixtures/live.agent-stream-control/invalid/role-is-not-one-of-the-two.json
nnew file mode 100644
iindex 00000000..aef2c1ab
---- /dev/null
++++ b/fixtures/live.agent-stream-control/invalid/role-is-not-one-of-the-two.json
@@@ -0,0 +1,8 @@
++{
++ "control": "pause",
++ "by": {
++ "handle": "mod",
++ "role": "viewer"
++ },
++ "at": "2026-11-06T10:00:00.000Z"
++}
ddiff --git a/fixtures/live.agent-stream-control/invalid/unknown-control.json b/fixtures/live.agent-stream-control/invalid/unknown-control.json
nnew file mode 100644
iindex 00000000..ede3c0eb
---- /dev/null
++++ b/fixtures/live.agent-stream-control/invalid/unknown-control.json
@@@ -0,0 +1,8 @@
++{
++ "control": "nuke",
++ "by": {
++ "handle": "mod",
++ "role": "moderator"
++ },
++ "at": "2026-11-06T10:00:00.000Z"
++}
ddiff --git a/fixtures/live.agent-stream-control/valid/end-by-the-owner.json b/fixtures/live.agent-stream-control/valid/end-by-the-owner.json
nnew file mode 100644
iindex 00000000..474e365b
---- /dev/null
++++ b/fixtures/live.agent-stream-control/valid/end-by-the-owner.json
@@@ -0,0 +1,10 @@
++{
++ "control": "end",
++ "by": {
++ "handle": "owner",
++ "role": "owner"
++ },
++ "at": "2026-11-06T10:00:00.000Z",
++ "stream": "ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "paused_by": null
++}
ddiff --git a/fixtures/live.agent-stream-control/valid/pause.json b/fixtures/live.agent-stream-control/valid/pause.json
nnew file mode 100644
iindex 00000000..bb1f153d
---- /dev/null
++++ b/fixtures/live.agent-stream-control/valid/pause.json
@@@ -0,0 +1,10 @@
++{
++ "control": "pause",
++ "by": {
++ "handle": "mod",
++ "role": "moderator"
++ },
++ "at": "2026-11-06T10:00:00.000Z",
++ "stream": "ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "paused_by": "mod"
++}
ddiff --git a/generated/json-schema/bundle.json b/generated/json-schema/bundle.json
iindex 7a1953c0..9443a38e 100644
---- a/generated/json-schema/bundle.json
++++ b/generated/json-schema/bundle.json
@@@ -104271,6 +104271,411 @@
}
}
},
++ "chat.agent-digest": {
++ "version": "1.0.0",
++ "owner": "chat",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/chat/agent-digest.v1.json",
++ "title": "ChatAgentDigest",
++ "description": "chat.agent-digest@1 (the Agent Streams plan, S5): what a chat room said to an agent between two of its steps, GET /api/v1/rooms/agent:<id>/digest?since= on OpenVibe.Chat. The agent never reads the raw room: it asks for a digest and gets at most ten items and 1,500 characters. Each item names who (the display name and the handle), what (cut to 200 characters), its kind (a question, a suggestion or a reaction) and its upvotes (a thumbs up on the message counts). Chat chooses what goes in: repeats are grouped, greetings and spam are dropped, and the most-upvoted questions and suggestions come first. Nobody is left out by accident: a person who is timed out or banned, whose account is younger than a day, or who set 'Agents don't read my messages' is never in the digest. A link is kept as its domain only ('a link to github.com'), because the agent never follows a link from chat. The code and the commands stay as text: the agent's own tools and approvals decide what may run, never chat.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "room",
++ "since",
++ "until",
++ "items"
++ ],
++ "properties": {
++ "room": {
++ "type": "string",
++ "pattern": "^agent:ags_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The room the digest came from: an agent stream's room, `agent:<ags_id>`."
++ },
++ "since": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "The start of the window: the moment the agent last read, or null for the first read."
++ },
++ "until": {
++ "type": "string",
++ "format": "date-time",
++ "description": "The end of the window: the moment this digest was made."
++ },
++ "items": {
++ "type": "array",
++ "maxItems": 10,
++ "description": "What chat said, at most ten items. The order is the one Chat chose: the most-upvoted questions and suggestions first, the reactions last.",
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "who",
++ "what",
++ "kind",
++ "upvotes"
++ ],
++ "properties": {
++ "who": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "display_name",
++ "handle"
++ ],
++ "description": "Who said it: the display name and the handle. Never an id, never an email, never anything else about them.",
++ "properties": {
++ "display_name": {
++ "type": "string",
++ "maxLength": 80,
++ "description": "The name the room shows."
++ },
++ "handle": {
++ "type": "string",
++ "pattern": "^[a-z0-9_]{2,32}$",
++ "description": "The handle, so the agent can answer this person by name."
++ }
++ }
++ },
++ "what": {
++ "type": "string",
++ "maxLength": 200,
++ "description": "What they said, cut to 200 characters. A link is kept as its domain only."
++ },
++ "kind": {
++ "enum": [
++ "question",
++ "suggestion",
++ "reaction"
++ ],
++ "description": "What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do."
++ },
++ "upvotes": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many thumbs up the message carries. The most-upvoted items come first."
++ },
++ "repeated": {
++ "type": "integer",
++ "minimum": 1,
++ "description": "How many people said this same thing. Chat groups repeats into one item; this is the number behind it."
++ }
++ }
++ }
++ },
++ "chars": {
++ "type": "integer",
++ "minimum": 0,
++ "maximum": 1500,
++ "description": "How many characters the items use together, at most 1,500."
++ },
++ "truncated": {
++ "type": "boolean",
++ "description": "True when Chat left something out because of the ten-item or 1,500-character cap."
++ }
++ }
++ }
++ },
++ "chat.agent-poll": {
++ "version": "1.0.0",
++ "owner": "chat",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/chat/agent-poll.v1.json",
++ "title": "ChatAgentPoll",
++ "description": "chat.agent-poll@1 (the Agent Streams plan, S5): a poll the agent asks a Co-pilot room to run, and the result it gets back. The agent writes the question and two to four options ('Which name: parseRows or readTable?') and the poll is open for 30 to 120 seconds. Chat draws it as a poll card with buttons; `!1` to `!4` vote too. One vote per person, one poll at a time, and at most one poll every five minutes. The owner (or a moderator) can close a poll early or veto its result. A poll can only choose between the options the agent wrote: Chat never adds one. The result carries the counts, nothing else about who voted.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "poll_id",
++ "room",
++ "question",
++ "options",
++ "opened_at",
++ "closes_at",
++ "state"
++ ],
++ "properties": {
++ "poll_id": {
++ "type": "string",
++ "pattern": "^pol_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The poll's id (ULID, Crockford base32)."
++ },
++ "room": {
++ "type": "string",
++ "pattern": "^agent:ags_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The room the poll is in: an agent stream's room, `agent:<ags_id>`."
++ },
++ "question": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "The question the agent wrote."
++ },
++ "options": {
++ "type": "array",
++ "minItems": 2,
++ "maxItems": 4,
++ "description": "The options the agent wrote, two to four of them. Chat never adds an option of its own.",
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "label"
++ ],
++ "properties": {
++ "id": {
++ "type": "string",
++ "pattern": "^[1-4]$",
++ "description": "The option's number: `!1` to `!4` vote for it."
++ },
++ "label": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 80,
++ "description": "The option's text, as the agent wrote it."
++ },
++ "votes": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many people chose it. The count is in the result; it says nothing about who."
++ }
++ }
++ }
++ },
++ "opened_at": {
++ "type": "string",
++ "format": "date-time",
++ "description": "When the poll opened."
++ },
++ "closes_at": {
++ "type": "string",
++ "format": "date-time",
++ "description": "When the poll closes on its own: 30 to 120 seconds after it opened."
++ },
++ "closed_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When the poll was closed early by the owner or a moderator, or null when it ran its time."
++ },
++ "state": {
++ "enum": [
++ "open",
++ "closed",
++ "vetoed"
++ ],
++ "description": "Where the poll stands: open, closed (the counts are final), or vetoed (the owner or a moderator refused the result)."
++ },
++ "result": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": false,
++ "description": "The result, once the poll is closed: the counts and the winning option. Null while the poll is open.",
++ "required": [
++ "counts",
++ "total"
++ ],
++ "properties": {
++ "counts": {
++ "type": "array",
++ "minItems": 2,
++ "maxItems": 4,
++ "description": "How many votes each option got, in the order the options were written.",
++ "items": {
++ "type": "integer",
++ "minimum": 0
++ }
++ },
++ "total": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many people voted."
++ },
++ "winner": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "pattern": "^[1-4]$",
++ "description": "The option that won, or null when there was no vote or no winner."
++ }
++ }
++ },
++ "vetoed_by": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "description": "Who vetoed the result (the owner or a moderator), or null. Never a person's id: the handle only."
++ }
++ },
++ "allOf": [
++ {
++ "if": {
++ "properties": {
++ "state": {
++ "const": "open"
++ }
++ },
++ "required": [
++ "state"
++ ]
++ },
++ "then": {
++ "properties": {
++ "result": {
++ "type": "null"
++ },
++ "closed_at": {
++ "type": "null"
++ },
++ "vetoed_by": {
++ "type": "null"
++ }
++ }
++ }
++ },
++ {
++ "if": {
++ "properties": {
++ "state": {
++ "const": "closed"
++ }
++ },
++ "required": [
++ "state"
++ ]
++ },
++ "then": {
++ "required": [
++ "closed_at",
++ "result"
++ ],
++ "properties": {
++ "result": {
++ "type": "object"
++ },
++ "closed_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "vetoed_by": {
++ "type": "null"
++ }
++ }
++ }
++ },
++ {
++ "if": {
++ "properties": {
++ "state": {
++ "const": "vetoed"
++ }
++ },
++ "required": [
++ "state"
++ ]
++ },
++ "then": {
++ "required": [
++ "closed_at",
++ "vetoed_by"
++ ],
++ "properties": {
++ "closed_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "vetoed_by": {
++ "type": "string"
++ }
++ }
++ }
++ }
++ ]
++ }
++ },
++ "live.agent-stream-control": {
++ "version": "1.0.0",
++ "owner": "live",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/live/agent-stream-control.v1.json",
++ "title": "LiveAgentStreamControl",
++ "description": "live.agent-stream-control@1 (the Agent Streams plan, S5): one of the four controls the owner and the channel's moderators have over a live agent stream, with who pressed it. PAUSE tells the task's service (Codes or Actor) to pause at the next safe point; the overlay then says 'Paused by @mod'. CURTAIN is the S1 curtain, raised from the viewer side. MUTE is the S4 mute: the agent voice stops. END ends the stream and the room's Co-pilot reading, while the task itself keeps running off camera unless it was paused. A moderator may press all four, but may not resume a pause the owner set and may not change the mode or the task. Every press is logged: who, which control, when.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "control",
++ "by",
++ "at"
++ ],
++ "properties": {
++ "control": {
++ "enum": [
++ "pause",
++ "curtain",
++ "mute",
++ "end"
++ ],
++ "description": "Which control was pressed."
++ },
++ "by": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "handle",
++ "role"
++ ],
++ "description": "Who pressed it: the handle and the role they hold. Never an id, never anything else about them.",
++ "properties": {
++ "handle": {
++ "type": "string",
++ "pattern": "^[a-z0-9_]{2,32}$",
++ "description": "The handle of the person who pressed the control."
++ },
++ "role": {
++ "enum": [
++ "owner",
++ "moderator"
++ ],
++ "description": "What they are on this stream: the owner, or one of the channel's moderators."
++ }
++ }
++ },
++ "at": {
++ "type": "string",
++ "format": "date-time",
++ "description": "When the control was pressed."
++ },
++ "stream": {
++ "type": "string",
++ "pattern": "^ags_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The agent stream the control was pressed on."
++ },
++ "paused_by": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "description": "The handle of the person whose pause is in force, or null when the stream is not paused. A moderator cannot resume the owner's pause: this says whose it is."
++ }
++ }
++ }
++ },
"live.agent_stream.changed": {
"version": "1.0.0",
"owner": "live",
ddiff --git a/generated/openapi/actor.json b/generated/openapi/actor.json
iindex 6daed289..f7bfa666 100644
---- a/generated/openapi/actor.json
++++ b/generated/openapi/actor.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Actor API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/ai.json b/generated/openapi/ai.json
iindex 85afb4bd..cd0bbc5a 100644
---- a/generated/openapi/ai.json
++++ b/generated/openapi/ai.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.AI API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/bench.json b/generated/openapi/bench.json
iindex 6e0718ab..fd613f54 100644
---- a/generated/openapi/bench.json
++++ b/generated/openapi/bench.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Bench API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/billing.json b/generated/openapi/billing.json
iindex 0a7efb29..94039a1d 100644
---- a/generated/openapi/billing.json
++++ b/generated/openapi/billing.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Billing API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/blog.json b/generated/openapi/blog.json
iindex c2bf39c4..90c34718 100644
---- a/generated/openapi/blog.json
++++ b/generated/openapi/blog.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Blog API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/bot.json b/generated/openapi/bot.json
iindex 92ebd4bc..3a5676cd 100644
---- a/generated/openapi/bot.json
++++ b/generated/openapi/bot.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Bot API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/chat.json b/generated/openapi/chat.json
iindex e7569cb2..64f4e797 100644
---- a/generated/openapi/chat.json
++++ b/generated/openapi/chat.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Chat API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
@@@ -3709,6 +3709,491 @@
}
}
},
++ "/internal/chat/rooms/agent{digest}": {
++ "get": {
++ "operationId": "get_internal_chat_rooms_agentdigest",
++ "summary": "Read what a Co-pilot room said, as a digest, on OpenVibe.Chat (the Agent Streams plan, S5).",
++ "description": "**chat.agent_digest.read** (first-party): Read what a Co-pilot room said, as a digest, on OpenVibe.Chat (the Agent Streams plan, S5). GET /internal/chat/rooms/agent:digest?since= answers chat.agent-digest@1: at most ten items and 1,500 characters, each with who (the display name and the handle), what (cut to 200 characters), its kind (a question, a suggestion or a reaction) and its upvotes. The caller is Codes or Actor, the task's service, acting for the stream's owner (a node token on_behalf_of), so the digest is the owner's room and nobody else's. Chat chooses what goes in and who is left out: a person who is timed out or banned, whose account is younger than a day, or who set 'Agents don't read my messages' is never in the digest, and a link is kept as its domain only. The agent never reads the raw room, and one read per step at most, twenty seconds apart.",
++ "tags": [
++ "chat.agent_digest"
++ ],
++ "x-openvibe-capabilities": [
++ "chat.agent_digest.read"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "internal-read",
++ "security": [
++ {
++ "openvibeToken": [
++ "chat.agent_digest.read"
++ ]
++ }
++ ],
++ "x-openvibe-input": {
++ "$ref": "#/components/schemas/chat.agent-digest.v1"
++ },
++ "parameters": [
++ {
++ "name": "digest",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ },
++ {
++ "name": "room",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "string",
++ "pattern": "^agent:ags_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The room the digest came from: an agent stream's room, `agent:<ags_id>`."
++ },
++ "description": "The room the digest came from: an agent stream's room, `agent:<ags_id>`."
++ },
++ {
++ "name": "since",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "The start of the window: the moment the agent last read, or null for the first read."
++ },
++ "description": "The start of the window: the moment the agent last read, or null for the first read."
++ },
++ {
++ "name": "until",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "string",
++ "format": "date-time",
++ "description": "The end of the window: the moment this digest was made."
++ },
++ "description": "The end of the window: the moment this digest was made."
++ },
++ {
++ "name": "items",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "array",
++ "maxItems": 10,
++ "description": "What chat said, at most ten items. The order is the one Chat chose: the most-upvoted questions and suggestions first, the reactions last.",
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "who",
++ "what",
++ "kind",
++ "upvotes"
++ ],
++ "properties": {
++ "who": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "display_name",
++ "handle"
++ ],
++ "description": "Who said it: the display name and the handle. Never an id, never an email, never anything else about them.",
++ "properties": {
++ "display_name": {
++ "type": "string",
++ "maxLength": 80,
++ "description": "The name the room shows."
++ },
++ "handle": {
++ "type": "string",
++ "pattern": "^[a-z0-9_]{2,32}$",
++ "description": "The handle, so the agent can answer this person by name."
++ }
++ }
++ },
++ "what": {
++ "type": "string",
++ "maxLength": 200,
++ "description": "What they said, cut to 200 characters. A link is kept as its domain only."
++ },
++ "kind": {
++ "enum": [
++ "question",
++ "suggestion",
++ "reaction"
++ ],
++ "description": "What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do."
++ },
++ "upvotes": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many thumbs up the message carries. The most-upvoted items come first."
++ },
++ "repeated": {
++ "type": "integer",
++ "minimum": 1,
++ "description": "How many people said this same thing. Chat groups repeats into one item; this is the number behind it."
++ }
++ }
++ }
++ },
++ "description": "What chat said, at most ten items. The order is the one Chat chose: the most-upvoted questions and suggestions first, the reactions last."
++ },
++ {
++ "name": "chars",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": "integer",
++ "minimum": 0,
++ "maximum": 1500,
++ "description": "How many characters the items use together, at most 1,500."
++ },
++ "description": "How many characters the items use together, at most 1,500."
++ },
++ {
++ "name": "truncated",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": "boolean",
++ "description": "True when Chat left something out because of the ten-item or 1,500-character cap."
++ },
++ "description": "True when Chat left something out because of the ten-item or 1,500-character cap."
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/chat.agent-digest.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/internal/chat/rooms/agent{poll}": {
++ "get": {
++ "operationId": "get_internal_chat_rooms_agentpoll",
++ "summary": "Run a poll in a Co-pilot room on OpenVibe.Chat, and read its result (the Agent Streams plan, S5).",
++ "description": "**chat.agent_poll.write** (first-party): Run a poll in a Co-pilot room on OpenVibe.Chat, and read its result (the Agent Streams plan, S5). POST /internal/chat/rooms/agent:poll takes chat.agent-poll@1: the question and two to four options the agent wrote, open for 30 to 120 seconds; Chat draws the poll card and `!1` to `!4` vote too. One vote per person, one poll at a time, and at most one poll every five minutes. The owner or a moderator can close a poll early or veto its result. The caller is Codes or Actor, the task's service, acting for the stream's owner (a node token on_behalf_of). Chat never adds an option of its own: a poll can only choose between the options the agent wrote.",
++ "tags": [
++ "chat.agent_poll"
++ ],
++ "x-openvibe-capabilities": [
++ "chat.agent_poll.write"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "internal-write",
++ "security": [
++ {
++ "openvibeToken": [
++ "chat.agent_poll.write"
++ ]
++ }
++ ],
++ "x-openvibe-input": {
++ "$ref": "#/components/schemas/chat.agent-poll.v1"
++ },
++ "parameters": [
++ {
++ "name": "poll",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ },
++ {
++ "name": "poll_id",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "string",
++ "pattern": "^pol_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The poll's id (ULID, Crockford base32)."
++ },
++ "description": "The poll's id (ULID, Crockford base32)."
++ },
++ {
++ "name": "room",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "string",
++ "pattern": "^agent:ags_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The room the poll is in: an agent stream's room, `agent:<ags_id>`."
++ },
++ "description": "The room the poll is in: an agent stream's room, `agent:<ags_id>`."
++ },
++ {
++ "name": "question",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "The question the agent wrote."
++ },
++ "description": "The question the agent wrote."
++ },
++ {
++ "name": "options",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "array",
++ "minItems": 2,
++ "maxItems": 4,
++ "description": "The options the agent wrote, two to four of them. Chat never adds an option of its own.",
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "label"
++ ],
++ "properties": {
++ "id": {
++ "type": "string",
++ "pattern": "^[1-4]$",
++ "description": "The option's number: `!1` to `!4` vote for it."
++ },
++ "label": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 80,
++ "description": "The option's text, as the agent wrote it."
++ },
++ "votes": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many people chose it. The count is in the result; it says nothing about who."
++ }
++ }
++ }
++ },
++ "description": "The options the agent wrote, two to four of them. Chat never adds an option of its own."
++ },
++ {
++ "name": "opened_at",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "string",
++ "format": "date-time",
++ "description": "When the poll opened."
++ },
++ "description": "When the poll opened."
++ },
++ {
++ "name": "closes_at",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "string",
++ "format": "date-time",
++ "description": "When the poll closes on its own: 30 to 120 seconds after it opened."
++ },
++ "description": "When the poll closes on its own: 30 to 120 seconds after it opened."
++ },
++ {
++ "name": "closed_at",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When the poll was closed early by the owner or a moderator, or null when it ran its time."
++ },
++ "description": "When the poll was closed early by the owner or a moderator, or null when it ran its time."
++ },
++ {
++ "name": "state",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "enum": [
++ "open",
++ "closed",
++ "vetoed"
++ ],
++ "description": "Where the poll stands: open, closed (the counts are final), or vetoed (the owner or a moderator refused the result)."
++ },
++ "description": "Where the poll stands: open, closed (the counts are final), or vetoed (the owner or a moderator refused the result)."
++ },
++ {
++ "name": "result",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": false,
++ "description": "The result, once the poll is closed: the counts and the winning option. Null while the poll is open.",
++ "required": [
++ "counts",
++ "total"
++ ],
++ "properties": {
++ "counts": {
++ "type": "array",
++ "minItems": 2,
++ "maxItems": 4,
++ "description": "How many votes each option got, in the order the options were written.",
++ "items": {
++ "type": "integer",
++ "minimum": 0
++ }
++ },
++ "total": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many people voted."
++ },
++ "winner": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "pattern": "^[1-4]$",
++ "description": "The option that won, or null when there was no vote or no winner."
++ }
++ }
++ },
++ "description": "The result, once the poll is closed: the counts and the winning option. Null while the poll is open."
++ },
++ {
++ "name": "vetoed_by",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "description": "Who vetoed the result (the owner or a moderator), or null. Never a person's id: the handle only."
++ },
++ "description": "Who vetoed the result (the owner or a moderator), or null. Never a person's id: the handle only."
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/chat.agent-poll.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ },
++ "post": {
++ "operationId": "post_internal_chat_rooms_agentpoll",
++ "summary": "Run a poll in a Co-pilot room on OpenVibe.Chat, and read its result (the Agent Streams plan, S5).",
++ "description": "**chat.agent_poll.write** (first-party): Run a poll in a Co-pilot room on OpenVibe.Chat, and read its result (the Agent Streams plan, S5). POST /internal/chat/rooms/agent:poll takes chat.agent-poll@1: the question and two to four options the agent wrote, open for 30 to 120 seconds; Chat draws the poll card and `!1` to `!4` vote too. One vote per person, one poll at a time, and at most one poll every five minutes. The owner or a moderator can close a poll early or veto its result. The caller is Codes or Actor, the task's service, acting for the stream's owner (a node token on_behalf_of). Chat never adds an option of its own: a poll can only choose between the options the agent wrote.",
++ "tags": [
++ "chat.agent_poll"
++ ],
++ "x-openvibe-capabilities": [
++ "chat.agent_poll.write"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "internal-write",
++ "security": [
++ {
++ "openvibeToken": [
++ "chat.agent_poll.write"
++ ]
++ }
++ ],
++ "requestBody": {
++ "required": false,
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/chat.agent-poll.v1"
++ }
++ }
++ }
++ },
++ "parameters": [
++ {
++ "name": "poll",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/chat.agent-poll.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
"/internal/chat/sounds": {
"get": {
"operationId": "get_internal_chat_sounds",
@@@ -4478,6 +4963,328 @@
},
"components": {
"schemas": {
++ "chat.agent-digest.v1": {
++ "title": "ChatAgentDigest",
++ "description": "chat.agent-digest@1 (the Agent Streams plan, S5): what a chat room said to an agent between two of its steps, GET /api/v1/rooms/agent:<id>/digest?since= on OpenVibe.Chat. The agent never reads the raw room: it asks for a digest and gets at most ten items and 1,500 characters. Each item names who (the display name and the handle), what (cut to 200 characters), its kind (a question, a suggestion or a reaction) and its upvotes (a thumbs up on the message counts). Chat chooses what goes in: repeats are grouped, greetings and spam are dropped, and the most-upvoted questions and suggestions come first. Nobody is left out by accident: a person who is timed out or banned, whose account is younger than a day, or who set 'Agents don't read my messages' is never in the digest. A link is kept as its domain only ('a link to github.com'), because the agent never follows a link from chat. The code and the commands stay as text: the agent's own tools and approvals decide what may run, never chat.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "room",
++ "since",
++ "until",
++ "items"
++ ],
++ "properties": {
++ "room": {
++ "type": "string",
++ "pattern": "^agent:ags_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The room the digest came from: an agent stream's room, `agent:<ags_id>`."
++ },
++ "since": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "The start of the window: the moment the agent last read, or null for the first read."
++ },
++ "until": {
++ "type": "string",
++ "format": "date-time",
++ "description": "The end of the window: the moment this digest was made."
++ },
++ "items": {
++ "type": "array",
++ "maxItems": 10,
++ "description": "What chat said, at most ten items. The order is the one Chat chose: the most-upvoted questions and suggestions first, the reactions last.",
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "who",
++ "what",
++ "kind",
++ "upvotes"
++ ],
++ "properties": {
++ "who": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "display_name",
++ "handle"
++ ],
++ "description": "Who said it: the display name and the handle. Never an id, never an email, never anything else about them.",
++ "properties": {
++ "display_name": {
++ "type": "string",
++ "maxLength": 80,
++ "description": "The name the room shows."
++ },
++ "handle": {
++ "type": "string",
++ "pattern": "^[a-z0-9_]{2,32}$",
++ "description": "The handle, so the agent can answer this person by name."
++ }
++ }
++ },
++ "what": {
++ "type": "string",
++ "maxLength": 200,
++ "description": "What they said, cut to 200 characters. A link is kept as its domain only."
++ },
++ "kind": {
++ "enum": [
++ "question",
++ "suggestion",
++ "reaction"
++ ],
++ "description": "What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do."
++ },
++ "upvotes": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many thumbs up the message carries. The most-upvoted items come first."
++ },
++ "repeated": {
++ "type": "integer",
++ "minimum": 1,
++ "description": "How many people said this same thing. Chat groups repeats into one item; this is the number behind it."
++ }
++ }
++ }
++ },
++ "chars": {
++ "type": "integer",
++ "minimum": 0,
++ "maximum": 1500,
++ "description": "How many characters the items use together, at most 1,500."
++ },
++ "truncated": {
++ "type": "boolean",
++ "description": "True when Chat left something out because of the ten-item or 1,500-character cap."
++ }
++ }
++ },
++ "chat.agent-poll.v1": {
++ "title": "ChatAgentPoll",
++ "description": "chat.agent-poll@1 (the Agent Streams plan, S5): a poll the agent asks a Co-pilot room to run, and the result it gets back. The agent writes the question and two to four options ('Which name: parseRows or readTable?') and the poll is open for 30 to 120 seconds. Chat draws it as a poll card with buttons; `!1` to `!4` vote too. One vote per person, one poll at a time, and at most one poll every five minutes. The owner (or a moderator) can close a poll early or veto its result. A poll can only choose between the options the agent wrote: Chat never adds one. The result carries the counts, nothing else about who voted.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "poll_id",
++ "room",
++ "question",
++ "options",
++ "opened_at",
++ "closes_at",
++ "state"
++ ],
++ "properties": {
++ "poll_id": {
++ "type": "string",
++ "pattern": "^pol_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The poll's id (ULID, Crockford base32)."
++ },
++ "room": {
++ "type": "string",
++ "pattern": "^agent:ags_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The room the poll is in: an agent stream's room, `agent:<ags_id>`."
++ },
++ "question": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "The question the agent wrote."
++ },
++ "options": {
++ "type": "array",
++ "minItems": 2,
++ "maxItems": 4,
++ "description": "The options the agent wrote, two to four of them. Chat never adds an option of its own.",
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "label"
++ ],
++ "properties": {
++ "id": {
++ "type": "string",
++ "pattern": "^[1-4]$",
++ "description": "The option's number: `!1` to `!4` vote for it."
++ },
++ "label": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 80,
++ "description": "The option's text, as the agent wrote it."
++ },
++ "votes": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many people chose it. The count is in the result; it says nothing about who."
++ }
++ }
++ }
++ },
++ "opened_at": {
++ "type": "string",
++ "format": "date-time",
++ "description": "When the poll opened."
++ },
++ "closes_at": {
++ "type": "string",
++ "format": "date-time",
++ "description": "When the poll closes on its own: 30 to 120 seconds after it opened."
++ },
++ "closed_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When the poll was closed early by the owner or a moderator, or null when it ran its time."
++ },
++ "state": {
++ "enum": [
++ "open",
++ "closed",
++ "vetoed"
++ ],
++ "description": "Where the poll stands: open, closed (the counts are final), or vetoed (the owner or a moderator refused the result)."
++ },
++ "result": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": false,
++ "description": "The result, once the poll is closed: the counts and the winning option. Null while the poll is open.",
++ "required": [
++ "counts",
++ "total"
++ ],
++ "properties": {
++ "counts": {
++ "type": "array",
++ "minItems": 2,
++ "maxItems": 4,
++ "description": "How many votes each option got, in the order the options were written.",
++ "items": {
++ "type": "integer",
++ "minimum": 0
++ }
++ },
++ "total": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many people voted."
++ },
++ "winner": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "pattern": "^[1-4]$",
++ "description": "The option that won, or null when there was no vote or no winner."
++ }
++ }
++ },
++ "vetoed_by": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "description": "Who vetoed the result (the owner or a moderator), or null. Never a person's id: the handle only."
++ }
++ },
++ "allOf": [
++ {
++ "if": {
++ "properties": {
++ "state": {
++ "const": "open"
++ }
++ },
++ "required": [
++ "state"
++ ]
++ },
++ "then": {
++ "properties": {
++ "result": {
++ "type": "null"
++ },
++ "closed_at": {
++ "type": "null"
++ },
++ "vetoed_by": {
++ "type": "null"
++ }
++ }
++ }
++ },
++ {
++ "if": {
++ "properties": {
++ "state": {
++ "const": "closed"
++ }
++ },
++ "required": [
++ "state"
++ ]
++ },
++ "then": {
++ "required": [
++ "closed_at",
++ "result"
++ ],
++ "properties": {
++ "result": {
++ "type": "object"
++ },
++ "closed_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "vetoed_by": {
++ "type": "null"
++ }
++ }
++ }
++ },
++ {
++ "if": {
++ "properties": {
++ "state": {
++ "const": "vetoed"
++ }
++ },
++ "required": [
++ "state"
++ ]
++ },
++ "then": {
++ "required": [
++ "closed_at",
++ "vetoed_by"
++ ],
++ "properties": {
++ "closed_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "vetoed_by": {
++ "type": "string"
++ }
++ }
++ }
++ }
++ ]
++ },
"chat.channel-moderation-result.v1": {
"title": "ChatChannelModerationResult",
"description": "chat.channel-moderation-result@1: the answer of GET /internal/moderation/channels/:channelId on OpenVibe.Chat (capability chat.moderation.read): { ok: true, settings, moderator_ids } — one channel's moderation settings and the ids of the people who moderate it, for OpenVibe.Live's stream chat. settings carries every channel_moderation_settings column (Live's own defaults when the channel has no row yet, database.js getChannelModerationSettings); the switches are 0/1 as the table stores them. moderator_ids are Live user ids (channel_moderators.user_id), never names. A failure is { ok: false, error }; a caller without the grant gets 401 { ok: false }.",
ddiff --git a/generated/openapi/codes.json b/generated/openapi/codes.json
iindex 2f8fd401..a476e828 100644
---- a/generated/openapi/codes.json
++++ b/generated/openapi/codes.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Codes API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/community.json b/generated/openapi/community.json
iindex d19ab1a0..42b5c15f 100644
---- a/generated/openapi/community.json
++++ b/generated/openapi/community.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Community API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/coupons.json b/generated/openapi/coupons.json
iindex fe491e91..55b30c9d 100644
---- a/generated/openapi/coupons.json
++++ b/generated/openapi/coupons.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Coupons API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/deals.json b/generated/openapi/deals.json
iindex 1a48f811..1e531982 100644
---- a/generated/openapi/deals.json
++++ b/generated/openapi/deals.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Deals API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/domains.json b/generated/openapi/domains.json
iindex c4cd4716..f6bb2698 100644
---- a/generated/openapi/domains.json
++++ b/generated/openapi/domains.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Domains API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/events.json b/generated/openapi/events.json
iindex 6bba89ef..62bf9012 100644
---- a/generated/openapi/events.json
++++ b/generated/openapi/events.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Events API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/food.json b/generated/openapi/food.json
iindex eb0e7d93..a8e98237 100644
---- a/generated/openapi/food.json
++++ b/generated/openapi/food.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Food API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/games.json b/generated/openapi/games.json
iindex 85c29ff6..5e249f12 100644
---- a/generated/openapi/games.json
++++ b/generated/openapi/games.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Games API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/host.json b/generated/openapi/host.json
iindex 75a2f2b2..446db208 100644
---- a/generated/openapi/host.json
++++ b/generated/openapi/host.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Host API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/index.json b/generated/openapi/index.json
iindex e6660806..2fcca6e7 100644
---- a/generated/openapi/index.json
++++ b/generated/openapi/index.json
@@@ -47,8 +47,8 @@
"service": "chat",
"name": "OpenVibe.Chat",
"origin": "https://openvibe.chat",
-- "operations": 55,
-- "capabilities": 20
++ "operations": 58,
++ "capabilities": 22
},
{
"service": "codes",
@@@ -124,7 +124,7 @@
"service": "live",
"name": "OpenVibe.Live",
"origin": "https://openvibe.live",
-- "operations": 38,
++ "operations": 40,
"capabilities": 11
},
{
ddiff --git a/generated/openapi/inventory.json b/generated/openapi/inventory.json
iindex c9af34a5..02eb193d 100644
---- a/generated/openapi/inventory.json
++++ b/generated/openapi/inventory.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Inventory API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/live.json b/generated/openapi/live.json
iindex 50d8d4e7..665b4b01 100644
---- a/generated/openapi/live.json
++++ b/generated/openapi/live.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Live API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
@@@ -694,7 +694,7 @@
"post": {
"operationId": "post_api_streams_agent",
"summary": "Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1).",
-- "description": "**live.agent_stream.publish** (public): Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode. The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
++ "description": "**live.agent_stream.publish** (public): Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode, and POST /api/streams/agent/:id/control is one of the four controls (pause, curtain, mute, end) with the moderator rules of the Agent Streams plan, S5. The publish capability also covers the step credit: a step may name the suggestion it took (\"Suggested by @mira\"). The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
"tags": [
"live.agent_stream"
],
@@@ -746,11 +746,181 @@
}
}
},
++ "/api/streams/agent/{id}/control": {
++ "post": {
++ "operationId": "post_api_streams_agent_id_control",
++ "summary": "Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1).",
++ "description": "**live.agent_stream.publish** (public): Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode, and POST /api/streams/agent/:id/control is one of the four controls (pause, curtain, mute, end) with the moderator rules of the Agent Streams plan, S5. The publish capability also covers the step credit: a step may name the suggestion it took (\"Suggested by @mira\"). The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
++ "tags": [
++ "live.agent_stream"
++ ],
++ "x-openvibe-capabilities": [
++ "live.agent_stream.publish"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "standard",
++ "security": [
++ {
++ "openvibeToken": [
++ "live.agent_stream.publish"
++ ]
++ }
++ ],
++ "requestBody": {
++ "required": false,
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/live.agent-stream-create-request.v1"
++ }
++ }
++ }
++ },
++ "parameters": [
++ {
++ "name": "id",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/live.agent-stream-publish.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/api/streams/agent/{id}/controls": {
++ "get": {
++ "operationId": "get_api_streams_agent_id_controls",
++ "summary": "Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1).",
++ "description": "**live.agent_stream.publish** (public): Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode, and POST /api/streams/agent/:id/control is one of the four controls (pause, curtain, mute, end) with the moderator rules of the Agent Streams plan, S5. The publish capability also covers the step credit: a step may name the suggestion it took (\"Suggested by @mira\"). The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
++ "tags": [
++ "live.agent_stream"
++ ],
++ "x-openvibe-capabilities": [
++ "live.agent_stream.publish"
++ ],
++ "x-openvibe-visibility": [
++ "public"
++ ],
++ "x-openvibe-quota-class": "standard",
++ "security": [
++ {
++ "openvibeToken": [
++ "live.agent_stream.publish"
++ ]
++ }
++ ],
++ "x-openvibe-input": {
++ "$ref": "#/components/schemas/live.agent-stream-create-request.v1"
++ },
++ "parameters": [
++ {
++ "name": "id",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ },
++ {
++ "name": "title",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200
++ }
++ },
++ {
++ "name": "task",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "$ref": "#/components/schemas/live.agent-stream.v1/$defs/task"
++ }
++ },
++ {
++ "name": "visibility",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "enum": [
++ "private",
++ "unlisted",
++ "public"
++ ]
++ }
++ },
++ {
++ "name": "allow_sites",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": "array",
++ "maxItems": 50,
++ "items": {
++ "type": "string",
++ "format": "uri"
++ },
++ "description": "The hosts the browser desk may visit. Absent means no limit."
++ },
++ "description": "The hosts the browser desk may visit. Absent means no limit."
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/live.agent-stream-publish.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
"/api/streams/agent/{id}/curtain": {
"post": {
"operationId": "post_api_streams_agent_id_curtain",
"summary": "Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1).",
-- "description": "**live.agent_stream.publish** (public): Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode. The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
++ "description": "**live.agent_stream.publish** (public): Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode, and POST /api/streams/agent/:id/control is one of the four controls (pause, curtain, mute, end) with the moderator rules of the Agent Streams plan, S5. The publish capability also covers the step credit: a step may name the suggestion it took (\"Suggested by @mira\"). The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
"tags": [
"live.agent_stream"
],
@@@ -816,7 +986,7 @@
"post": {
"operationId": "post_api_streams_agent_id_end",
"summary": "Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1).",
-- "description": "**live.agent_stream.publish** (public): Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode. The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
++ "description": "**live.agent_stream.publish** (public): Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode, and POST /api/streams/agent/:id/control is one of the four controls (pause, curtain, mute, end) with the moderator rules of the Agent Streams plan, S5. The publish capability also covers the step credit: a step may name the suggestion it took (\"Suggested by @mira\"). The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
"tags": [
"live.agent_stream"
],
@@@ -882,7 +1052,7 @@
"put": {
"operationId": "put_api_streams_agent_id_frame",
"summary": "Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1).",
-- "description": "**live.agent_stream.publish** (public): Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode. The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
++ "description": "**live.agent_stream.publish** (public): Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode, and POST /api/streams/agent/:id/control is one of the four controls (pause, curtain, mute, end) with the moderator rules of the Agent Streams plan, S5. The publish capability also covers the step credit: a step may name the suggestion it took (\"Suggested by @mira\"). The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
"tags": [
"live.agent_stream"
],
@@@ -948,7 +1118,7 @@
"get": {
"operationId": "get_api_streams_agent_id_state",
"summary": "Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1).",
-- "description": "**live.agent_stream.publish** (public): Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode. The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
++ "description": "**live.agent_stream.publish** (public): Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode, and POST /api/streams/agent/:id/control is one of the four controls (pause, curtain, mute, end) with the moderator rules of the Agent Streams plan, S5. The publish capability also covers the step credit: a step may name the suggestion it took (\"Suggested by @mira\"). The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
"tags": [
"live.agent_stream"
],
@@@ -1052,7 +1222,7 @@
"delete": {
"operationId": "delete_api_streams_agent_id_ticket",
"summary": "Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1).",
-- "description": "**live.agent_stream.publish** (public): Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode. The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
++ "description": "**live.agent_stream.publish** (public): Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode, and POST /api/streams/agent/:id/control is one of the four controls (pause, curtain, mute, end) with the moderator rules of the Agent Streams plan, S5. The publish capability also covers the step credit: a step may name the suggestion it took (\"Suggested by @mira\"). The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
"tags": [
"live.agent_stream"
],
@@@ -1154,7 +1324,7 @@
"post": {
"operationId": "post_api_streams_agent_id_ticket",
"summary": "Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1).",
-- "description": "**live.agent_stream.publish** (public): Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode. The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
++ "description": "**live.agent_stream.publish** (public): Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode, and POST /api/streams/agent/:id/control is one of the four controls (pause, curtain, mute, end) with the moderator rules of the Agent Streams plan, S5. The publish capability also covers the step credit: a step may name the suggestion it took (\"Suggested by @mira\"). The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
"tags": [
"live.agent_stream"
],
ddiff --git a/generated/openapi/media-hub.json b/generated/openapi/media-hub.json
iindex 658c7c2f..ba30e0fa 100644
---- a/generated/openapi/media-hub.json
++++ b/generated/openapi/media-hub.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.MediaHub API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/media.json b/generated/openapi/media.json
iindex 293f42ef..e1e8dce4 100644
---- a/generated/openapi/media.json
++++ b/generated/openapi/media.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Media API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/network.json b/generated/openapi/network.json
iindex cb0e0791..ee4429d1 100644
---- a/generated/openapi/network.json
++++ b/generated/openapi/network.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Network API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/news.json b/generated/openapi/news.json
iindex 2b09c498..63ced13b 100644
---- a/generated/openapi/news.json
++++ b/generated/openapi/news.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.News API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/openre.json b/generated/openapi/openre.json
iindex ad2937c6..7362753d 100644
---- a/generated/openapi/openre.json
++++ b/generated/openapi/openre.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenRestream API",
-- "version": "0.302.0",
-- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/play.json b/generated/openapi/play.json
iindex 0af4b9ad..0a09fa21 100644
---- a/generated/openapi/play.json
++++ b/generated/openapi/play.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Play API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/quest.json b/generated/openapi/quest.json
iindex 96262108..3497d1a8 100644
---- a/generated/openapi/quest.json
++++ b/generated/openapi/quest.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Quest API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/report.json b/generated/openapi/report.json
iindex 51151dfb..255f25c1 100644
---- a/generated/openapi/report.json
++++ b/generated/openapi/report.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Report API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/reviews.json b/generated/openapi/reviews.json
iindex 1f424b68..7e02fd8c 100644
---- a/generated/openapi/reviews.json
++++ b/generated/openapi/reviews.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Reviews API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/run.json b/generated/openapi/run.json
iindex d2ff1b99..d9374621 100644
---- a/generated/openapi/run.json
++++ b/generated/openapi/run.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Run API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/search.json b/generated/openapi/search.json
iindex 7241bead..1d551817 100644
---- a/generated/openapi/search.json
++++ b/generated/openapi/search.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Search API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/services.json b/generated/openapi/services.json
iindex 29c2ea7c..b4e679d4 100644
---- a/generated/openapi/services.json
++++ b/generated/openapi/services.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Services API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/sources.json b/generated/openapi/sources.json
iindex 8c20ea97..2aebbd64 100644
---- a/generated/openapi/sources.json
++++ b/generated/openapi/sources.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Sources API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/space.json b/generated/openapi/space.json
iindex 605b2497..9a549668 100644
---- a/generated/openapi/space.json
++++ b/generated/openapi/space.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Space API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/tips.json b/generated/openapi/tips.json
iindex 5b157fdf..cb541a24 100644
---- a/generated/openapi/tips.json
++++ b/generated/openapi/tips.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Tips API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/today.json b/generated/openapi/today.json
iindex 3b27b73c..77387de5 100644
---- a/generated/openapi/today.json
++++ b/generated/openapi/today.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Today API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/tools.json b/generated/openapi/tools.json
iindex 7e268977..95842723 100644
---- a/generated/openapi/tools.json
++++ b/generated/openapi/tools.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Tools API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/trade.json b/generated/openapi/trade.json
iindex e015b5a9..8e2026cf 100644
---- a/generated/openapi/trade.json
++++ b/generated/openapi/trade.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Trade API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/vip.json b/generated/openapi/vip.json
iindex 7f5a6c1c..237fb484 100644
---- a/generated/openapi/vip.json
++++ b/generated/openapi/vip.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.VIP API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/vision.json b/generated/openapi/vision.json
iindex 2b192c3e..35e8db48 100644
---- a/generated/openapi/vision.json
++++ b/generated/openapi/vision.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Vision API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/watch.json b/generated/openapi/watch.json
iindex b8d4057d..6a12ba35 100644
---- a/generated/openapi/watch.json
++++ b/generated/openapi/watch.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Watch API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/wiki.json b/generated/openapi/wiki.json
iindex c239dc5e..90def6b6 100644
---- a/generated/openapi/wiki.json
++++ b/generated/openapi/wiki.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Wiki API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/work.json b/generated/openapi/work.json
iindex 45edef6b..bb96a2f8 100644
---- a/generated/openapi/work.json
++++ b/generated/openapi/work.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Work API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/openapi/zone.json b/generated/openapi/zone.json
iindex f1ddca58..3ead4f72 100644
---- a/generated/openapi/zone.json
++++ b/generated/openapi/zone.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Zone API",
-- "version": "0.302.0",
-- "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.302.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
++ "version": "0.303.0",
++ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.303.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"name": "MIT",
"identifier": "MIT"
ddiff --git a/generated/typescript/index.d.ts b/generated/typescript/index.d.ts
iindex 75fb08e6..d2660179 100644
---- a/generated/typescript/index.d.ts
++++ b/generated/typescript/index.d.ts
@@@ -292529,6 +292529,2001 @@ export interface LiveAgentStreamStepRequest {
cost_usd?: number;
}
++/** [email protected] (owner: chat) */
++/**
++ * chat.agent-digest@1 (the Agent Streams plan, S5): what a chat room said to an agent between two of its steps, GET /api/v1/rooms/agent:<id>/digest?since= on OpenVibe.Chat. The agent never reads the raw room: it asks for a digest and gets at most ten items and 1,500 characters. Each item names who (the display name and the handle), what (cut to 200 characters), its kind (a question, a suggestion or a reaction) and its upvotes (a thumbs up on the message counts). Chat chooses what goes in: repeats are grouped, greetings and spam are dropped, and the most-upvoted questions and suggestions come first. Nobody is left out by accident: a person who is timed out or banned, whose account is younger than a day, or who set 'Agents don't read my messages' is never in the digest. A link is kept as its domain only ('a link to github.com'), because the agent never follows a link from chat. The code and the commands stay as text: the agent's own tools and approvals decide what may run, never chat.
++ */
++export interface ChatAgentDigest {
++ /**
++ * The room the digest came from: an agent stream's room, `agent:<ags_id>`.
++ */
++ room: string;
++ /**
++ * The start of the window: the moment the agent last read, or null for the first read.
++ */
++ since: string | null;
++ /**
++ * The end of the window: the moment this digest was made.
++ */
++ until: string;
++ /**
++ * What chat said, at most ten items. The order is the one Chat chose: the most-upvoted questions and suggestions first, the reactions last.
++ *
++ * @maxItems 10
++ */
++ items:
++ | []
++ | [
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ }
++ ]
++ | [
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ }
++ ]
++ | [
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ }
++ ]
++ | [
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ }
++ ]
++ | [
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ }
++ ]
++ | [
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ }
++ ]
++ | [
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ }
++ ]
++ | [
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ }
++ ]
++ | [
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ }
++ ]
++ | [
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ },
++ {
++ /**
++ * Who said it: the display name and the handle. Never an id, never an email, never anything else about them.
++ */
++ who: {
++ /**
++ * The name the room shows.
++ */
++ display_name: string;
++ /**
++ * The handle, so the agent can answer this person by name.
++ */
++ handle: string;
++ };
++ /**
++ * What they said, cut to 200 characters. A link is kept as its domain only.
++ */
++ what: string;
++ /**
++ * What the line is: a question the agent may answer, a suggestion it may take or leave, or a reaction (a thumbs up, a cheer) that says nothing to do.
++ */
++ kind: "question" | "suggestion" | "reaction";
++ /**
++ * How many thumbs up the message carries. The most-upvoted items come first.
++ */
++ upvotes: number;
++ /**
++ * How many people said this same thing. Chat groups repeats into one item; this is the number behind it.
++ */
++ repeated?: number;
++ }
++ ];
++ /**
++ * How many characters the items use together, at most 1,500.
++ */
++ chars?: number;
++ /**
++ * True when Chat left something out because of the ten-item or 1,500-character cap.
++ */
++ truncated?: boolean;
++}
++
++/** [email protected] (owner: chat) */
++/**
++ * chat.agent-poll@1 (the Agent Streams plan, S5): a poll the agent asks a Co-pilot room to run, and the result it gets back. The agent writes the question and two to four options ('Which name: parseRows or readTable?') and the poll is open for 30 to 120 seconds. Chat draws it as a poll card with buttons; `!1` to `!4` vote too. One vote per person, one poll at a time, and at most one poll every five minutes. The owner (or a moderator) can close a poll early or veto its result. A poll can only choose between the options the agent wrote: Chat never adds one. The result carries the counts, nothing else about who voted.
++ */
++export type ChatAgentPoll = {
++ [k: string]: unknown | undefined;
++} & {
++ /**
++ * The poll's id (ULID, Crockford base32).
++ */
++ poll_id: string;
++ /**
++ * The room the poll is in: an agent stream's room, `agent:<ags_id>`.
++ */
++ room: string;
++ /**
++ * The question the agent wrote.
++ */
++ question: string;
++ /**
++ * The options the agent wrote, two to four of them. Chat never adds an option of its own.
++ *
++ * @minItems 2
++ * @maxItems 4
++ */
++ options:
++ | [
++ {
++ /**
++ * The option's number: `!1` to `!4` vote for it.
++ */
++ id: string;
++ /**
++ * The option's text, as the agent wrote it.
++ */
++ label: string;
++ /**
++ * How many people chose it. The count is in the result; it says nothing about who.
++ */
++ votes?: number;
++ },
++ {
++ /**
++ * The option's number: `!1` to `!4` vote for it.
++ */
++ id: string;
++ /**
++ * The option's text, as the agent wrote it.
++ */
++ label: string;
++ /**
++ * How many people chose it. The count is in the result; it says nothing about who.
++ */
++ votes?: number;
++ }
++ ]
++ | [
++ {
++ /**
++ * The option's number: `!1` to `!4` vote for it.
++ */
++ id: string;
++ /**
++ * The option's text, as the agent wrote it.
++ */
++ label: string;
++ /**
++ * How many people chose it. The count is in the result; it says nothing about who.
++ */
++ votes?: number;
++ },
++ {
++ /**
++ * The option's number: `!1` to `!4` vote for it.
++ */
++ id: string;
++ /**
++ * The option's text, as the agent wrote it.
++ */
++ label: string;
++ /**
++ * How many people chose it. The count is in the result; it says nothing about who.
++ */
++ votes?: number;
++ },
++ {
++ /**
++ * The option's number: `!1` to `!4` vote for it.
++ */
++ id: string;
++ /**
++ * The option's text, as the agent wrote it.
++ */
++ label: string;
++ /**
++ * How many people chose it. The count is in the result; it says nothing about who.
++ */
++ votes?: number;
++ }
++ ]
++ | [
++ {
++ /**
++ * The option's number: `!1` to `!4` vote for it.
++ */
++ id: string;
++ /**
++ * The option's text, as the agent wrote it.
++ */
++ label: string;
++ /**
++ * How many people chose it. The count is in the result; it says nothing about who.
++ */
++ votes?: number;
++ },
++ {
++ /**
++ * The option's number: `!1` to `!4` vote for it.
++ */
++ id: string;
++ /**
++ * The option's text, as the agent wrote it.
++ */
++ label: string;
++ /**
++ * How many people chose it. The count is in the result; it says nothing about who.
++ */
++ votes?: number;
++ },
++ {
++ /**
++ * The option's number: `!1` to `!4` vote for it.
++ */
++ id: string;
++ /**
++ * The option's text, as the agent wrote it.
++ */
++ label: string;
++ /**
++ * How many people chose it. The count is in the result; it says nothing about who.
++ */
++ votes?: number;
++ },
++ {
++ /**
++ * The option's number: `!1` to `!4` vote for it.
++ */
++ id: string;
++ /**
++ * The option's text, as the agent wrote it.
++ */
++ label: string;
++ /**
++ * How many people chose it. The count is in the result; it says nothing about who.
++ */
++ votes?: number;
++ }
++ ];
++ /**
++ * When the poll opened.
++ */
++ opened_at: string;
++ /**
++ * When the poll closes on its own: 30 to 120 seconds after it opened.
++ */
++ closes_at: string;
++ /**
++ * When the poll was closed early by the owner or a moderator, or null when it ran its time.
++ */
++ closed_at?: string | null;
++ /**
++ * Where the poll stands: open, closed (the counts are final), or vetoed (the owner or a moderator refused the result).
++ */
++ state: "open" | "closed" | "vetoed";
++ /**
++ * The result, once the poll is closed: the counts and the winning option. Null while the poll is open.
++ */
++ result?: {
++ /**
++ * How many votes each option got, in the order the options were written.
++ *
++ * @minItems 2
++ * @maxItems 4
++ */
++ counts: [number, number] | [number, number, number] | [number, number, number, number];
++ /**
++ * How many people voted.
++ */
++ total: number;
++ /**
++ * The option that won, or null when there was no vote or no winner.
++ */
++ winner?: string | null;
++ } | null;
++ /**
++ * Who vetoed the result (the owner or a moderator), or null. Never a person's id: the handle only.
++ */
++ vetoed_by?: string | null;
++};
++
++/** [email protected] (owner: live) */
++/**
++ * live.agent-stream-control@1 (the Agent Streams plan, S5): one of the four controls the owner and the channel's moderators have over a live agent stream, with who pressed it. PAUSE tells the task's service (Codes or Actor) to pause at the next safe point; the overlay then says 'Paused by @mod'. CURTAIN is the S1 curtain, raised from the viewer side. MUTE is the S4 mute: the agent voice stops. END ends the stream and the room's Co-pilot reading, while the task itself keeps running off camera unless it was paused. A moderator may press all four, but may not resume a pause the owner set and may not change the mode or the task. Every press is logged: who, which control, when.
++ */
++export interface LiveAgentStreamControl {
++ /**
++ * Which control was pressed.
++ */
++ control: "pause" | "curtain" | "mute" | "end";
++ /**
++ * Who pressed it: the handle and the role they hold. Never an id, never anything else about them.
++ */
++ by: {
++ /**
++ * The handle of the person who pressed the control.
++ */
++ handle: string;
++ /**
++ * What they are on this stream: the owner, or one of the channel's moderators.
++ */
++ role: "owner" | "moderator";
++ };
++ /**
++ * When the control was pressed.
++ */
++ at: string;
++ /**
++ * The agent stream the control was pressed on.
++ */
++ stream?: string;
++ /**
++ * The handle of the person whose pause is in force, or null when the stream is not paused. A moderator cannot resume the owner's pause: this says whose it is.
++ */
++ paused_by?: string | null;
++}
++
/** [email protected] (owner: live) */
/**
* live.agent_stream.changed v1 (OpenVibe.Live, the Agent Streams plan, S1): an agent stream changed. KIND is what happened: started, ended, visibility (the owner switched it) or curtain (the desk's privacy screen went up or came down). It says what happened and never why: the curtain's reason stays with the owner, so a subscriber learns that a private screen is up, not that a password field was focused. VISIBILITY carries the new visibility when KIND is visibility, CURTAIN whether the screen is up when KIND is curtain. The publish ticket, the stream key and the task's contents never appear. Envelope: subject { type: agent_stream, id: ags_… }, actor the owner's subject when Live knows it, else service:live. The event type carries no hyphen (events.event-envelope@1's event_type pattern allows letters, digits and underscores only), so it is live.agent_stream.changed; the stream's contract ids keep live.agent-stream@1.
ddiff --git a/manifests/capabilities/chat.agent_digest.read.json b/manifests/capabilities/chat.agent_digest.read.json
nnew file mode 100644
iindex 00000000..03de085c
---- /dev/null
++++ b/manifests/capabilities/chat.agent_digest.read.json
@@@ -0,0 +1,21 @@
++{
++ "id": "chat.agent_digest.read",
++ "version": "1.0",
++ "owner": "chat",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Read what a Co-pilot room said, as a digest, on OpenVibe.Chat (the Agent Streams plan, S5). GET /internal/chat/rooms/agent:digest?since= answers chat.agent-digest@1: at most ten items and 1,500 characters, each with who (the display name and the handle), what (cut to 200 characters), its kind (a question, a suggestion or a reaction) and its upvotes. The caller is Codes or Actor, the task's service, acting for the stream's owner (a node token on_behalf_of), so the digest is the owner's room and nobody else's. Chat chooses what goes in and who is left out: a person who is timed out or banned, whose account is younger than a day, or who set 'Agents don't read my messages' is never in the digest, and a link is kept as its domain only. The agent never reads the raw room, and one read per step at most, twenty seconds apart.",
++ "permissions": [
++ "chat:read"
++ ],
++ "resourceConstraints": [
++ "owner"
++ ],
++ "quotaClass": "internal-read",
++ "events": [],
++ "implementedBy": [
++ "GET /internal/chat/rooms/agent:digest"
++ ],
++ "inputSchema": "chat.agent-digest@1",
++ "outputSchema": "chat.agent-digest@1"
++}
ddiff --git a/manifests/capabilities/chat.agent_poll.write.json b/manifests/capabilities/chat.agent_poll.write.json
nnew file mode 100644
iindex 00000000..8c6189c9
---- /dev/null
++++ b/manifests/capabilities/chat.agent_poll.write.json
@@@ -0,0 +1,22 @@
++{
++ "id": "chat.agent_poll.write",
++ "version": "1.0",
++ "owner": "chat",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Run a poll in a Co-pilot room on OpenVibe.Chat, and read its result (the Agent Streams plan, S5). POST /internal/chat/rooms/agent:poll takes chat.agent-poll@1: the question and two to four options the agent wrote, open for 30 to 120 seconds; Chat draws the poll card and `!1` to `!4` vote too. One vote per person, one poll at a time, and at most one poll every five minutes. The owner or a moderator can close a poll early or veto its result. The caller is Codes or Actor, the task's service, acting for the stream's owner (a node token on_behalf_of). Chat never adds an option of its own: a poll can only choose between the options the agent wrote.",
++ "permissions": [
++ "chat:write"
++ ],
++ "resourceConstraints": [
++ "owner"
++ ],
++ "quotaClass": "internal-write",
++ "events": [],
++ "implementedBy": [
++ "POST /internal/chat/rooms/agent:poll",
++ "GET /internal/chat/rooms/agent:poll"
++ ],
++ "inputSchema": "chat.agent-poll@1",
++ "outputSchema": "chat.agent-poll@1"
++}
ddiff --git a/manifests/capabilities/live.agent_stream.publish.json b/manifests/capabilities/live.agent_stream.publish.json
iindex 65ab3e5a..867f6800 100644
---- a/manifests/capabilities/live.agent_stream.publish.json
++++ b/manifests/capabilities/live.agent_stream.publish.json
@@@ -4,7 +4,7 @@
"owner": "live",
"status": "active",
"visibility": "public",
-- "description": "Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode. The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
++ "description": "Publish an agent's stream on OpenVibe.Live (the Agent Streams plan, S1). POST /api/streams/agent creates the agent stream and its publish ticket, POST /api/streams/agent/:id/ticket renews that ticket, DELETE /api/streams/agent/:id/ticket revokes it (the kill switch: Live ends the WHIP session at once), POST /api/streams/agent/:id/curtain raises the desk's privacy screen, PUT /api/streams/agent/:id/frame sends the last frame while Live wants no video, POST /api/streams/agent/:id/end ends the stream, and GET /api/streams/agent/:id/state tells the desk whether to encode, and POST /api/streams/agent/:id/control is one of the four controls (pause, curtain, mute, end) with the moderator rules of the Agent Streams plan, S5. The publish capability also covers the step credit: a step may name the suggestion it took (\"Suggested by @mira\"). The caller is the person's own session token, or a first-party service token acting for that person (a Node principal acting for its owner, Codes or Actor) that holds this capability for the audience openvibe.live: Network grants it to exactly those principals. Never a developer-app token, never another person's stream. The Agent Streams design calls it live.agent-stream.publish; a capability id in this system carries no hyphen (the capability pattern and the token claims' cap pattern both allow letters, digits and underscores only), so the id is live.agent_stream.publish.",
"inputSchema": "live.agent-stream-create-request@1",
"outputSchema": "live.agent-stream-publish@1",
"permissions": [
@@@ -24,7 +24,9 @@
"POST /api/streams/agent/:id/curtain",
"PUT /api/streams/agent/:id/frame",
"POST /api/streams/agent/:id/end",
-- "GET /api/streams/agent/:id/state"
++ "GET /api/streams/agent/:id/state",
++ "POST /api/streams/agent/:id/control",
++ "GET /api/streams/agent/:id/controls"
],
"sensitive": true
}
ddiff --git a/manifests/namespaces/chat.preferences.json b/manifests/namespaces/chat.preferences.json
iindex c5be573f..6a324205 100644
---- a/manifests/namespaces/chat.preferences.json
++++ b/manifests/namespaces/chat.preferences.json
@@@ -1,8 +1,9 @@
{
"namespace": "chat.preferences",
"owner": "chat",
-- "version": 1,
-- "description": "How chat looks for this person on every site with OpenVibe chat: timestamps, compact messages, text size, badges and emotes.",
++ "version": 2,
++ "migration": "v2 adds agents_read_messages (the Agent Streams plan, S5: 'Agents don't read my messages'). A v1 record upgrades to a v2 record that reads true: joining a Co-pilot room and writing in it is the choice, and the person can take it back.",
++ "description": "How chat looks for this person on every site with OpenVibe chat: timestamps, compact messages, text size, badges and emotes, and whether an AI agent may read their messages (the Co-pilot rooms of the Agent Streams plan, S5).",
"writers": [
"owner",
"user"
@@@ -30,7 +31,20 @@
},
"hide_emotes": {
"type": "boolean"
++ },
++ "agents_read_messages": {
++ "type": "boolean",
++ "description": "Whether an AI agent may read this person's chat messages. True by default: joining a Co-pilot room and writing in it is the choice, and it can be taken back. When false the digest leaves this person out entirely."
++ }
++ }
++ },
++ "migrations": [
++ {
++ "from": 1,
++ "to": 2,
++ "defaults": {
++ "agents_read_messages": true
}
}
-- }
++ ]
}
ddiff --git a/manifests/services/chat.json b/manifests/services/chat.json
iindex 3b6473d6..1ddc9153 100644
---- a/manifests/services/chat.json
++++ b/manifests/services/chat.json
@@@ -12,6 +12,8 @@
],
"publicOrigin": "https://openvibe.chat",
"capabilities": [
++ "chat.agent_digest.read",
++ "chat.agent_poll.write",
"chat.analysis.read",
"chat.cache.invalidate",
"chat.event.publish",
ddiff --git a/package-lock.json b/package-lock.json
iindex fb0a6990..b7f654c0 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -1,12 +1,12 @@
{
"name": "openvibe-contracts",
-- "version": "0.301.0",
++ "version": "0.302.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openvibe-contracts",
-- "version": "0.301.0",
++ "version": "0.302.0",
"license": "AGPL-3.0-only",
"dependencies": {
"ajv": "^8.17.1",
ddiff --git a/package.json b/package.json
iindex 99fdb35b..27e14667 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-contracts",
-- "version": "0.302.0",
++ "version": "0.303.0",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"main": "index.js",
"types": "generated/typescript/index.d.ts",