openvibers/openvibe.contracts Commits

Agent Streams S6: the contracts the chat plays mode needs. (#314)

OP OpenVibers committed 6 days ago · 5a2e5aa61e · 69 files, +2070 −97

* Agent Streams S6: the play request, the sandbox session and the queue capabilities (0.304.0)

* Contracts 0.304.0: the prefixes the agent streams need (ags, req, sbs)

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex b9ffeecd..8cc9aeaf 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -1,5 +1,16 @@
# Changelog
++## 0.304.0 — 2026-11-06
++
++- Give the id kinds the Agent Streams work needs a prefix: `newId('agent_stream')` is `ags_…` (an agent stream's id), `newId('play_request')` is `req_…` (a chat plays request) and `newId('sandbox_session')` is `sbs_…` (the sandbox desk). Without them a service that had to name one of these ids had to build the ULID itself, and `newId('ags')` threw.
++
++- Add `chat.play-request@1` (Agent Streams S6): one viewer's request for the chat plays desk to build or do something. A request is written with `!do <request>` (at most 200 characters) in an agent stream's room, `agent:<ags_id>`; one open request per person at a time, and an account younger than a day cannot ask. The request carries its votes, its state (`queued`, `running`, `done`, `failed`, `skipped`, `removed` or `refused`), whether it is pinned, and the one-line reason it was refused (with the rule that refused it) when it was. A refused request carries no result; a finished one carries the outcome, the line the overlay and the voice said, and the scratch site the run left.
++- Add `codes.sandbox-session@1`: the sandbox desk a chat plays session runs on. The desk is started with `--shared` — no profile, no keys, no person's files, public egress only and a fresh workspace, wiped at the end — and the session carries the owner's time box (5 to 30 minutes), the step budget, the cap (money and minutes), what it has spent, whether the owner armed it with Go, and the throwaway scratch site with the 24 hours it lives. `egress_public_only` is always true: no private address is reachable.
++- `live.agent-stream-control@1` gains the control `wipe` (additive): Wipe now stops the run and resets the sandbox. The other four controls (pause, curtain, mute, end) keep their shape.
++- `live.agent-stream@1` gains the mode `chatplays` (additive; the enum already carried it): a chat plays stream is public or unlisted, and it works the room's queue.
++- Add the capabilities `chat.play_queue.read` and `chat.play_queue.write` for Codes, the task's service, acting for the stream's owner (a node token `on_behalf_of`), and name both in Chat's service manifest. Chat chooses what the queue holds: a request enters it only after the house text filters and the safety check, one open request per person at a time, and the people and the votes in it belong to the room.
++- Valid and invalid fixtures cover the queued, finished and refused requests, a request that is too long, a refusal without a reason, an unknown state, the armed and running sandbox sessions, a session with no cap, a time box outside 5 to 30 minutes, a sandbox that is not public-only, and the wipe control, so a missing field or a bad value fails the run.
++
## 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.
ddiff --git a/STATUS.json b/STATUS.json
iindex 6b37df3a..ea0a4c7d 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.play-request@1 and codes.sandbox-session@1 (the Agent Streams plan, S6): the queue a chat plays room keeps and the sandbox desk that works it, with the owner's time box, cap and Go, and the scratch site that lives 24 hours",
++ "the capabilities chat.play_queue.read and chat.play_queue.write, for Codes acting for the stream's owner",
"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)",
ddiff --git a/contracts/catalog.json b/contracts/catalog.json
iindex aacaaf58..bc94200e 100644
---- a/contracts/catalog.json
++++ b/contracts/catalog.json
@@@ -9591,6 +9591,24 @@
"visibility": "first-party",
"compatibility": "backward",
"status": "active"
++ },
++ {
++ "id": "chat.play-request",
++ "version": "1.0.0",
++ "owner": "chat",
++ "schema": "chat/play-request.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
++ },
++ {
++ "id": "codes.sandbox-session",
++ "version": "1.0.0",
++ "owner": "codes",
++ "schema": "codes/sandbox-session.v1.json",
++ "visibility": "first-party",
++ "compatibility": "backward",
++ "status": "active"
}
]
}
ddiff --git a/contracts/chat/play-request.v1.json b/contracts/chat/play-request.v1.json
nnew file mode 100644
iindex 00000000..0999330d
---- /dev/null
++++ b/contracts/chat/play-request.v1.json
@@@ -0,0 +1,231 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/chat/play-request.v1.json",
++ "title": "ChatPlayRequest",
++ "description": "chat.play-request@1 (the Agent Streams plan, S6): one viewer's request for the chat plays desk to build or do something. A request is written with `!do <request>` (at most 200 characters) in an agent stream's room, `agent:<ags_id>`. One open request per person at a time, and an account younger than a day cannot ask. The request passes the house text filters and the safety check before it is listed; a refused request carries a one-line reason and never enters the queue. Viewers vote with a thumbs-up or `!vote <n>`, and every five minutes, or when the current request finishes, the top request becomes the next task. The owner or a moderator can skip, remove or pin a request. The result is the run's outcome and the scratch site it left behind.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "request_id",
++ "room",
++ "author",
++ "text",
++ "votes",
++ "state",
++ "pinned",
++ "created_at"
++ ],
++ "properties": {
++ "request_id": {
++ "type": "string",
++ "pattern": "^req_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The request's id (ULID, Crockford base32)."
++ },
++ "room": {
++ "type": "string",
++ "pattern": "^agent:ags_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The room the request was made in: an agent stream's room, `agent:<ags_id>`."
++ },
++ "author": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "handle"
++ ],
++ "description": "Who asked. The handle and the display name only, never an id.",
++ "properties": {
++ "handle": {
++ "type": "string",
++ "pattern": "^[a-z0-9_]{2,32}$",
++ "description": "The handle of the person who asked."
++ },
++ "display_name": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 64,
++ "description": "Their display name, when they have one."
++ }
++ }
++ },
++ "text": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "What the viewer asked for, as they typed it (at most 200 characters)."
++ },
++ "votes": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many votes the request has. The count says nothing about who voted."
++ },
++ "voters": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many people voted (one vote each)."
++ },
++ "state": {
++ "enum": [
++ "queued",
++ "running",
++ "done",
++ "failed",
++ "skipped",
++ "removed",
++ "refused"
++ ],
++ "description": "Where the request stands: queued (waiting), running (the desk is on it), done or failed (it ran), skipped (a moderator or the owner passed over it), removed (it left the queue) or refused (the safety check or the filters turned it down)."
++ },
++ "pinned": {
++ "type": "boolean",
++ "description": "A pinned request stays at the top of the queue until it is removed or it runs."
++ },
++ "created_at": {
++ "type": "string",
++ "format": "date-time",
++ "description": "When the request was asked for."
++ },
++ "started_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When the desk began it, or null while it waits."
++ },
++ "ended_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When it finished, was skipped or was removed, or null while it stands."
++ },
++ "refusal": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": false,
++ "required": [
++ "code",
++ "reason"
++ ],
++ "description": "Why the request was refused, when it was: the code and the one-line reason the room saw. Null when the request was accepted.",
++ "properties": {
++ "code": {
++ "enum": [
++ "platform_floor",
++ "harm",
++ "attack",
++ "account_or_money",
++ "account_too_new",
++ "too_long",
++ "already_open"
++ ],
++ "description": "Which rule turned it down."
++ },
++ "reason": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "The one-line reason shown in the room."
++ }
++ }
++ },
++ "result": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": false,
++ "required": [
++ "outcome",
++ "line"
++ ],
++ "description": "How the run ended, once it ran: the outcome and the line the overlay and the voice said.",
++ "properties": {
++ "outcome": {
++ "enum": [
++ "done",
++ "failed",
++ "timeout",
++ "budget",
++ "stopped"
++ ],
++ "description": "How it ended."
++ },
++ "line": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "The one line the room and the overlay saw, e.g. 'Done: a snake game at the scratch address'."
++ },
++ "scratch_url": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "uri",
++ "description": "The throwaway scratch site the run left, when it left one."
++ },
++ "task_id": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "description": "The Codes task that ran it."
++ }
++ }
++ }
++ },
++ "allOf": [
++ {
++ "if": {
++ "properties": {
++ "state": {
++ "const": "refused"
++ }
++ },
++ "required": [
++ "state"
++ ]
++ },
++ "then": {
++ "properties": {
++ "refusal": {
++ "type": "object"
++ },
++ "result": {
++ "type": "null"
++ }
++ },
++ "required": [
++ "refusal"
++ ]
++ }
++ },
++ {
++ "if": {
++ "properties": {
++ "state": {
++ "not": {
++ "const": "refused"
++ }
++ }
++ },
++ "required": [
++ "state"
++ ]
++ },
++ "then": {
++ "properties": {
++ "refusal": {
++ "type": "null"
++ }
++ }
++ }
++ }
++ ]
++}
ddiff --git a/contracts/codes/sandbox-session.v1.json b/contracts/codes/sandbox-session.v1.json
nnew file mode 100644
iindex 00000000..3f9bd314
---- /dev/null
++++ b/contracts/codes/sandbox-session.v1.json
@@@ -0,0 +1,187 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/codes/sandbox-session.v1.json",
++ "title": "CodesSandboxSession",
++ "description": "codes.sandbox-session@1 (the Agent Streams plan, S6): the sandbox desk a chat plays session runs on. The desk is started with `--shared`: no profile, no keys, no person's files, public egress only (no private address is reachable) and a fresh workspace, and it is wiped at the end of the session. The owner sets the time box (5 to 30 minutes) and the cap (money and minutes), and arms the session with Go before the first request may run. The scratch site is a throwaway OpenVibe.Space address that is removed after 24 hours unless the owner keeps it.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "session_id",
++ "task",
++ "owner",
++ "state",
++ "time_box_minutes",
++ "step_budget",
++ "cap",
++ "spend",
++ "armed",
++ "egress_public_only"
++ ],
++ "properties": {
++ "session_id": {
++ "type": "string",
++ "pattern": "^sbs_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The sandbox session's id (ULID, Crockford base32)."
++ },
++ "task": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "title"
++ ],
++ "description": "The task the desk is running for the queue.",
++ "properties": {
++ "id": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "The task's id in Codes."
++ },
++ "title": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200
++ }
++ }
++ },
++ "owner": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The person the session spends their own allowance for."
++ },
++ "state": {
++ "enum": [
++ "idle",
++ "armed",
++ "running",
++ "paused",
++ "wiped",
++ "ended"
++ ],
++ "description": "Where the desk stands: idle (nothing yet), armed (the owner pressed Go, the first request may run), running, paused, wiped (Wipe now reset the sandbox) or ended."
++ },
++ "time_box_minutes": {
++ "type": "integer",
++ "minimum": 5,
++ "maximum": 30,
++ "description": "How long one request may run, in minutes (the owner sets 5 to 30; the default is 10)."
++ },
++ "step_budget": {
++ "type": "integer",
++ "minimum": 1,
++ "description": "How many steps one request may take."
++ },
++ "cap": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "money_cents",
++ "minutes"
++ ],
++ "description": "The owner's cap for the session: the money and the minutes it may spend. When it is spent, the queue closes.",
++ "properties": {
++ "money_cents": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "The cap in cents."
++ },
++ "minutes": {
++ "type": "integer",
++ "minimum": 1,
++ "description": "The cap in minutes of work."
++ }
++ }
++ },
++ "spend": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "money_cents",
++ "seconds",
++ "steps"
++ ],
++ "description": "What the session has spent so far.",
++ "properties": {
++ "money_cents": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "seconds": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "steps": {
++ "type": "integer",
++ "minimum": 0
++ }
++ }
++ },
++ "armed": {
++ "type": "boolean",
++ "description": "Whether the owner pressed Go. The first request of a session waits for it."
++ },
++ "armed_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When the owner pressed Go."
++ },
++ "started_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "ended_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "wiped_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When Wipe now reset the sandbox."
++ },
++ "egress_public_only": {
++ "const": true,
++ "description": "Always true: the sandbox reaches public addresses only, and no private address is reachable."
++ },
++ "scratch_site": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": false,
++ "required": [
++ "url",
++ "expires_at",
++ "kept"
++ ],
++ "description": "The throwaway site the run left, or null before the first one.",
++ "properties": {
++ "url": {
++ "type": "string",
++ "format": "uri"
++ },
++ "expires_at": {
++ "type": "string",
++ "format": "date-time",
++ "description": "When it is removed (24 hours after it was made, unless it is kept)."
++ },
++ "kept": {
++ "type": "boolean",
++ "description": "Whether the owner kept it past its 24 hours."
++ }
++ }
++ }
++ }
++}
ddiff --git a/contracts/live/agent-stream-control.v1.json b/contracts/live/agent-stream-control.v1.json
iindex 2ec12ad5..7e254888 100644
---- a/contracts/live/agent-stream-control.v1.json
++++ b/contracts/live/agent-stream-control.v1.json
@@@ -2,7 +2,7 @@
"$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.",
++ "description": "live.agent-stream-control@1 (the Agent Streams plan, S5): one of the 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. WIPE is the chat plays control of S6: it stops the run and resets the sandbox desk. A moderator may press every control, 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": [
@@@ -16,7 +16,8 @@
"pause",
"curtain",
"mute",
-- "end"
++ "end",
++ "wipe"
],
"description": "Which control was pressed."
},
ddiff --git a/fixtures/chat.play-request/invalid/refused-without-a-reason.json b/fixtures/chat.play-request/invalid/refused-without-a-reason.json
nnew file mode 100644
iindex 00000000..9ae62404
---- /dev/null
++++ b/fixtures/chat.play-request/invalid/refused-without-a-reason.json
@@@ -0,0 +1,18 @@
++{
++ "request_id": "req_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "author": {
++ "handle": "mira",
++ "display_name": "Mira"
++ },
++ "text": "build me a thing",
++ "votes": 0,
++ "voters": 0,
++ "state": "refused",
++ "pinned": false,
++ "created_at": "2026-11-06T20:00:00.000Z",
++ "started_at": null,
++ "ended_at": null,
++ "refusal": null,
++ "result": null
++}
ddiff --git a/fixtures/chat.play-request/invalid/too-long.json b/fixtures/chat.play-request/invalid/too-long.json
nnew file mode 100644
iindex 00000000..c1dbad7c
---- /dev/null
++++ b/fixtures/chat.play-request/invalid/too-long.json
@@@ -0,0 +1,18 @@
++{
++ "request_id": "req_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "author": {
++ "handle": "mira",
++ "display_name": "Mira"
++ },
++ "text": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
++ "votes": 0,
++ "voters": 0,
++ "state": "queued",
++ "pinned": false,
++ "created_at": "2026-11-06T20:00:00.000Z",
++ "started_at": null,
++ "ended_at": null,
++ "refusal": null,
++ "result": null
++}
ddiff --git a/fixtures/chat.play-request/invalid/unknown-state.json b/fixtures/chat.play-request/invalid/unknown-state.json
nnew file mode 100644
iindex 00000000..43564d08
---- /dev/null
++++ b/fixtures/chat.play-request/invalid/unknown-state.json
@@@ -0,0 +1,18 @@
++{
++ "request_id": "req_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "author": {
++ "handle": "mira",
++ "display_name": "Mira"
++ },
++ "text": "build me a thing",
++ "votes": 0,
++ "voters": 0,
++ "state": "waiting",
++ "pinned": false,
++ "created_at": "2026-11-06T20:00:00.000Z",
++ "started_at": null,
++ "ended_at": null,
++ "refusal": null,
++ "result": null
++}
ddiff --git a/fixtures/chat.play-request/valid/done.json b/fixtures/chat.play-request/valid/done.json
nnew file mode 100644
iindex 00000000..2c119ca4
---- /dev/null
++++ b/fixtures/chat.play-request/valid/done.json
@@@ -0,0 +1,23 @@
++{
++ "request_id": "req_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "author": {
++ "handle": "mira",
++ "display_name": "Mira"
++ },
++ "text": "build a snake game I can play",
++ "votes": 12,
++ "voters": 12,
++ "state": "done",
++ "pinned": false,
++ "created_at": "2026-11-06T20:00:00.000Z",
++ "started_at": "2026-11-06T20:05:00.000Z",
++ "ended_at": "2026-11-06T20:09:12.000Z",
++ "refusal": null,
++ "result": {
++ "outcome": "done",
++ "line": "Done: a snake game at https://scratch.openvibe.space/snake-4f2a",
++ "scratch_url": "https://scratch.openvibe.space/snake-4f2a",
++ "task_id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPQ"
++ }
++}
ddiff --git a/fixtures/chat.play-request/valid/queued.json b/fixtures/chat.play-request/valid/queued.json
nnew file mode 100644
iindex 00000000..1988df92
---- /dev/null
++++ b/fixtures/chat.play-request/valid/queued.json
@@@ -0,0 +1,18 @@
++{
++ "request_id": "req_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "author": {
++ "handle": "mira",
++ "display_name": "Mira"
++ },
++ "text": "build a snake game I can play",
++ "votes": 12,
++ "voters": 12,
++ "state": "queued",
++ "pinned": false,
++ "created_at": "2026-11-06T20:00:00.000Z",
++ "started_at": null,
++ "ended_at": null,
++ "refusal": null,
++ "result": null
++}
ddiff --git a/fixtures/chat.play-request/valid/refused.json b/fixtures/chat.play-request/valid/refused.json
nnew file mode 100644
iindex 00000000..482e1e51
---- /dev/null
++++ b/fixtures/chat.play-request/valid/refused.json
@@@ -0,0 +1,21 @@
++{
++ "request_id": "req_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "author": {
++ "handle": "newbie",
++ "display_name": null
++ },
++ "text": "post my password to pastebin",
++ "votes": 0,
++ "voters": 0,
++ "state": "refused",
++ "pinned": false,
++ "created_at": "2026-11-06T20:00:00.000Z",
++ "started_at": null,
++ "ended_at": null,
++ "refusal": {
++ "code": "attack",
++ "reason": "That one is refused: it wants the agent to post something for you."
++ },
++ "result": null
++}
ddiff --git a/fixtures/codes.sandbox-session/invalid/egress-not-public-only.json b/fixtures/codes.sandbox-session/invalid/egress-not-public-only.json
nnew file mode 100644
iindex 00000000..6cb2e4f7
---- /dev/null
++++ b/fixtures/codes.sandbox-session/invalid/egress-not-public-only.json
@@@ -0,0 +1,27 @@
++{
++ "session_id": "sbs_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "task": {
++ "id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "title": "Chat plays"
++ },
++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "state": "armed",
++ "time_box_minutes": 10,
++ "step_budget": 40,
++ "cap": {
++ "money_cents": 500,
++ "minutes": 30
++ },
++ "spend": {
++ "money_cents": 0,
++ "seconds": 0,
++ "steps": 0
++ },
++ "armed": true,
++ "armed_at": "2026-11-06T20:00:00.000Z",
++ "started_at": null,
++ "ended_at": null,
++ "wiped_at": null,
++ "egress_public_only": false,
++ "scratch_site": null
++}
ddiff --git a/fixtures/codes.sandbox-session/invalid/no-cap.json b/fixtures/codes.sandbox-session/invalid/no-cap.json
nnew file mode 100644
iindex 00000000..86b06986
---- /dev/null
++++ b/fixtures/codes.sandbox-session/invalid/no-cap.json
@@@ -0,0 +1,23 @@
++{
++ "session_id": "sbs_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "task": {
++ "id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "title": "Chat plays"
++ },
++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "state": "armed",
++ "time_box_minutes": 10,
++ "step_budget": 40,
++ "spend": {
++ "money_cents": 0,
++ "seconds": 0,
++ "steps": 0
++ },
++ "armed": true,
++ "armed_at": "2026-11-06T20:00:00.000Z",
++ "started_at": null,
++ "ended_at": null,
++ "wiped_at": null,
++ "egress_public_only": true,
++ "scratch_site": null
++}
ddiff --git a/fixtures/codes.sandbox-session/invalid/time-box-out-of-range.json b/fixtures/codes.sandbox-session/invalid/time-box-out-of-range.json
nnew file mode 100644
iindex 00000000..85b2af5d
---- /dev/null
++++ b/fixtures/codes.sandbox-session/invalid/time-box-out-of-range.json
@@@ -0,0 +1,27 @@
++{
++ "session_id": "sbs_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "task": {
++ "id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "title": "Chat plays"
++ },
++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "state": "armed",
++ "time_box_minutes": 2,
++ "step_budget": 40,
++ "cap": {
++ "money_cents": 500,
++ "minutes": 30
++ },
++ "spend": {
++ "money_cents": 0,
++ "seconds": 0,
++ "steps": 0
++ },
++ "armed": true,
++ "armed_at": "2026-11-06T20:00:00.000Z",
++ "started_at": null,
++ "ended_at": null,
++ "wiped_at": null,
++ "egress_public_only": true,
++ "scratch_site": null
++}
ddiff --git a/fixtures/codes.sandbox-session/valid/armed.json b/fixtures/codes.sandbox-session/valid/armed.json
nnew file mode 100644
iindex 00000000..9591b7eb
---- /dev/null
++++ b/fixtures/codes.sandbox-session/valid/armed.json
@@@ -0,0 +1,27 @@
++{
++ "session_id": "sbs_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "task": {
++ "id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "title": "Chat plays"
++ },
++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "state": "armed",
++ "time_box_minutes": 10,
++ "step_budget": 40,
++ "cap": {
++ "money_cents": 500,
++ "minutes": 30
++ },
++ "spend": {
++ "money_cents": 0,
++ "seconds": 0,
++ "steps": 0
++ },
++ "armed": true,
++ "armed_at": "2026-11-06T20:00:00.000Z",
++ "started_at": null,
++ "ended_at": null,
++ "wiped_at": null,
++ "egress_public_only": true,
++ "scratch_site": null
++}
ddiff --git a/fixtures/codes.sandbox-session/valid/running.json b/fixtures/codes.sandbox-session/valid/running.json
nnew file mode 100644
iindex 00000000..c6519878
---- /dev/null
++++ b/fixtures/codes.sandbox-session/valid/running.json
@@@ -0,0 +1,31 @@
++{
++ "session_id": "sbs_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "task": {
++ "id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "title": "Chat plays"
++ },
++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "state": "running",
++ "time_box_minutes": 10,
++ "step_budget": 40,
++ "cap": {
++ "money_cents": 500,
++ "minutes": 30
++ },
++ "spend": {
++ "money_cents": 42,
++ "seconds": 180,
++ "steps": 6
++ },
++ "armed": true,
++ "armed_at": "2026-11-06T20:00:00.000Z",
++ "started_at": "2026-11-06T20:01:00.000Z",
++ "ended_at": null,
++ "wiped_at": null,
++ "egress_public_only": true,
++ "scratch_site": {
++ "url": "https://scratch.openvibe.space/snake-4f2a",
++ "expires_at": "2026-11-07T20:01:00.000Z",
++ "kept": false
++ }
++}
ddiff --git a/fixtures/live.agent-stream-control/valid/wipe.json b/fixtures/live.agent-stream-control/valid/wipe.json
nnew file mode 100644
iindex 00000000..3734f365
---- /dev/null
++++ b/fixtures/live.agent-stream-control/valid/wipe.json
@@@ -0,0 +1,10 @@
++{
++ "control": "wipe",
++ "by": {
++ "handle": "owner",
++ "role": "owner"
++ },
++ "at": "2026-11-06T20:30:00.000Z",
++ "stream": "ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "paused_by": null
++}
ddiff --git a/generated/json-schema/bundle.json b/generated/json-schema/bundle.json
iindex 9443a38e..7e409943 100644
---- a/generated/json-schema/bundle.json
++++ b/generated/json-schema/bundle.json
@@@ -104615,7 +104615,7 @@
"$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.",
++ "description": "live.agent-stream-control@1 (the Agent Streams plan, S5): one of the 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. WIPE is the chat plays control of S6: it stops the run and resets the sandbox desk. A moderator may press every control, 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": [
@@@ -104629,7 +104629,8 @@
"pause",
"curtain",
"mute",
-- "end"
++ "end",
++ "wipe"
],
"description": "Which control was pressed."
},
@@@ -105545,6 +105546,434 @@
}
}
}
++ },
++ "chat.play-request": {
++ "version": "1.0.0",
++ "owner": "chat",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/chat/play-request.v1.json",
++ "title": "ChatPlayRequest",
++ "description": "chat.play-request@1 (the Agent Streams plan, S6): one viewer's request for the chat plays desk to build or do something. A request is written with `!do <request>` (at most 200 characters) in an agent stream's room, `agent:<ags_id>`. One open request per person at a time, and an account younger than a day cannot ask. The request passes the house text filters and the safety check before it is listed; a refused request carries a one-line reason and never enters the queue. Viewers vote with a thumbs-up or `!vote <n>`, and every five minutes, or when the current request finishes, the top request becomes the next task. The owner or a moderator can skip, remove or pin a request. The result is the run's outcome and the scratch site it left behind.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "request_id",
++ "room",
++ "author",
++ "text",
++ "votes",
++ "state",
++ "pinned",
++ "created_at"
++ ],
++ "properties": {
++ "request_id": {
++ "type": "string",
++ "pattern": "^req_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The request's id (ULID, Crockford base32)."
++ },
++ "room": {
++ "type": "string",
++ "pattern": "^agent:ags_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The room the request was made in: an agent stream's room, `agent:<ags_id>`."
++ },
++ "author": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "handle"
++ ],
++ "description": "Who asked. The handle and the display name only, never an id.",
++ "properties": {
++ "handle": {
++ "type": "string",
++ "pattern": "^[a-z0-9_]{2,32}$",
++ "description": "The handle of the person who asked."
++ },
++ "display_name": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 64,
++ "description": "Their display name, when they have one."
++ }
++ }
++ },
++ "text": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "What the viewer asked for, as they typed it (at most 200 characters)."
++ },
++ "votes": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many votes the request has. The count says nothing about who voted."
++ },
++ "voters": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many people voted (one vote each)."
++ },
++ "state": {
++ "enum": [
++ "queued",
++ "running",
++ "done",
++ "failed",
++ "skipped",
++ "removed",
++ "refused"
++ ],
++ "description": "Where the request stands: queued (waiting), running (the desk is on it), done or failed (it ran), skipped (a moderator or the owner passed over it), removed (it left the queue) or refused (the safety check or the filters turned it down)."
++ },
++ "pinned": {
++ "type": "boolean",
++ "description": "A pinned request stays at the top of the queue until it is removed or it runs."
++ },
++ "created_at": {
++ "type": "string",
++ "format": "date-time",
++ "description": "When the request was asked for."
++ },
++ "started_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When the desk began it, or null while it waits."
++ },
++ "ended_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When it finished, was skipped or was removed, or null while it stands."
++ },
++ "refusal": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": false,
++ "required": [
++ "code",
++ "reason"
++ ],
++ "description": "Why the request was refused, when it was: the code and the one-line reason the room saw. Null when the request was accepted.",
++ "properties": {
++ "code": {
++ "enum": [
++ "platform_floor",
++ "harm",
++ "attack",
++ "account_or_money",
++ "account_too_new",
++ "too_long",
++ "already_open"
++ ],
++ "description": "Which rule turned it down."
++ },
++ "reason": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "The one-line reason shown in the room."
++ }
++ }
++ },
++ "result": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": false,
++ "required": [
++ "outcome",
++ "line"
++ ],
++ "description": "How the run ended, once it ran: the outcome and the line the overlay and the voice said.",
++ "properties": {
++ "outcome": {
++ "enum": [
++ "done",
++ "failed",
++ "timeout",
++ "budget",
++ "stopped"
++ ],
++ "description": "How it ended."
++ },
++ "line": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "The one line the room and the overlay saw, e.g. 'Done: a snake game at the scratch address'."
++ },
++ "scratch_url": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "uri",
++ "description": "The throwaway scratch site the run left, when it left one."
++ },
++ "task_id": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "description": "The Codes task that ran it."
++ }
++ }
++ }
++ },
++ "allOf": [
++ {
++ "if": {
++ "properties": {
++ "state": {
++ "const": "refused"
++ }
++ },
++ "required": [
++ "state"
++ ]
++ },
++ "then": {
++ "properties": {
++ "refusal": {
++ "type": "object"
++ },
++ "result": {
++ "type": "null"
++ }
++ },
++ "required": [
++ "refusal"
++ ]
++ }
++ },
++ {
++ "if": {
++ "properties": {
++ "state": {
++ "not": {
++ "const": "refused"
++ }
++ }
++ },
++ "required": [
++ "state"
++ ]
++ },
++ "then": {
++ "properties": {
++ "refusal": {
++ "type": "null"
++ }
++ }
++ }
++ }
++ ]
++ }
++ },
++ "codes.sandbox-session": {
++ "version": "1.0.0",
++ "owner": "codes",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/codes/sandbox-session.v1.json",
++ "title": "CodesSandboxSession",
++ "description": "codes.sandbox-session@1 (the Agent Streams plan, S6): the sandbox desk a chat plays session runs on. The desk is started with `--shared`: no profile, no keys, no person's files, public egress only (no private address is reachable) and a fresh workspace, and it is wiped at the end of the session. The owner sets the time box (5 to 30 minutes) and the cap (money and minutes), and arms the session with Go before the first request may run. The scratch site is a throwaway OpenVibe.Space address that is removed after 24 hours unless the owner keeps it.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "session_id",
++ "task",
++ "owner",
++ "state",
++ "time_box_minutes",
++ "step_budget",
++ "cap",
++ "spend",
++ "armed",
++ "egress_public_only"
++ ],
++ "properties": {
++ "session_id": {
++ "type": "string",
++ "pattern": "^sbs_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The sandbox session's id (ULID, Crockford base32)."
++ },
++ "task": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "title"
++ ],
++ "description": "The task the desk is running for the queue.",
++ "properties": {
++ "id": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "The task's id in Codes."
++ },
++ "title": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200
++ }
++ }
++ },
++ "owner": {
++ "type": "string",
++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The person the session spends their own allowance for."
++ },
++ "state": {
++ "enum": [
++ "idle",
++ "armed",
++ "running",
++ "paused",
++ "wiped",
++ "ended"
++ ],
++ "description": "Where the desk stands: idle (nothing yet), armed (the owner pressed Go, the first request may run), running, paused, wiped (Wipe now reset the sandbox) or ended."
++ },
++ "time_box_minutes": {
++ "type": "integer",
++ "minimum": 5,
++ "maximum": 30,
++ "description": "How long one request may run, in minutes (the owner sets 5 to 30; the default is 10)."
++ },
++ "step_budget": {
++ "type": "integer",
++ "minimum": 1,
++ "description": "How many steps one request may take."
++ },
++ "cap": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "money_cents",
++ "minutes"
++ ],
++ "description": "The owner's cap for the session: the money and the minutes it may spend. When it is spent, the queue closes.",
++ "properties": {
++ "money_cents": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "The cap in cents."
++ },
++ "minutes": {
++ "type": "integer",
++ "minimum": 1,
++ "description": "The cap in minutes of work."
++ }
++ }
++ },
++ "spend": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "money_cents",
++ "seconds",
++ "steps"
++ ],
++ "description": "What the session has spent so far.",
++ "properties": {
++ "money_cents": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "seconds": {
++ "type": "integer",
++ "minimum": 0
++ },
++ "steps": {
++ "type": "integer",
++ "minimum": 0
++ }
++ }
++ },
++ "armed": {
++ "type": "boolean",
++ "description": "Whether the owner pressed Go. The first request of a session waits for it."
++ },
++ "armed_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When the owner pressed Go."
++ },
++ "started_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "ended_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time"
++ },
++ "wiped_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When Wipe now reset the sandbox."
++ },
++ "egress_public_only": {
++ "const": true,
++ "description": "Always true: the sandbox reaches public addresses only, and no private address is reachable."
++ },
++ "scratch_site": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": false,
++ "required": [
++ "url",
++ "expires_at",
++ "kept"
++ ],
++ "description": "The throwaway site the run left, or null before the first one.",
++ "properties": {
++ "url": {
++ "type": "string",
++ "format": "uri"
++ },
++ "expires_at": {
++ "type": "string",
++ "format": "date-time",
++ "description": "When it is removed (24 hours after it was made, unless it is kept)."
++ },
++ "kept": {
++ "type": "boolean",
++ "description": "Whether the owner kept it past its 24 hours."
++ }
++ }
++ }
++ }
++ }
}
}
}
ddiff --git a/generated/openapi/actor.json b/generated/openapi/actor.json
iindex f7bfa666..88e52048 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 cd0bbc5a..a0ed7e70 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 fd613f54..097c5cda 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 94039a1d..bbfdc22f 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 90c34718..94d5e631 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 3a5676cd..78e65065 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 64f4e797..642cc1cc 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.304.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"
@@@ -3893,6 +3893,380 @@
}
}
},
++ "/internal/chat/rooms/agent{play}-queue": {
++ "get": {
++ "operationId": "get_internal_chat_rooms_agentplay_queue",
++ "summary": "Read a chat plays queue on OpenVibe.Chat (the Agent Streams plan, S6).",
++ "description": "**chat.play_queue.read** (first-party): Read a chat plays queue on OpenVibe.Chat (the Agent Streams plan, S6). GET /internal/chat/rooms/agent:play-queue answers chat.play-request@1: the requests of the room `agent:<ags_id>`, each with its votes and its state, the current request and the top of the queue. The caller is Codes, the task's service, acting for the stream's owner (a node token on_behalf_of), so the queue it reads is the owner's room and nobody else's. Chat chooses what the queue holds: a request enters it only after the house text filters and the safety check, one open request per person at a time.",
++ "tags": [
++ "chat.play_queue"
++ ],
++ "x-openvibe-capabilities": [
++ "chat.play_queue.read"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "internal-read",
++ "security": [
++ {
++ "openvibeToken": [
++ "chat.play_queue.read"
++ ]
++ }
++ ],
++ "x-openvibe-input": {
++ "$ref": "#/components/schemas/chat.play-request.v1"
++ },
++ "parameters": [
++ {
++ "name": "play",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ },
++ {
++ "name": "request_id",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "string",
++ "pattern": "^req_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The request's id (ULID, Crockford base32)."
++ },
++ "description": "The request'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 request was made in: an agent stream's room, `agent:<ags_id>`."
++ },
++ "description": "The room the request was made in: an agent stream's room, `agent:<ags_id>`."
++ },
++ {
++ "name": "author",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "handle"
++ ],
++ "description": "Who asked. The handle and the display name only, never an id.",
++ "properties": {
++ "handle": {
++ "type": "string",
++ "pattern": "^[a-z0-9_]{2,32}$",
++ "description": "The handle of the person who asked."
++ },
++ "display_name": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 64,
++ "description": "Their display name, when they have one."
++ }
++ }
++ },
++ "description": "Who asked. The handle and the display name only, never an id."
++ },
++ {
++ "name": "text",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "What the viewer asked for, as they typed it (at most 200 characters)."
++ },
++ "description": "What the viewer asked for, as they typed it (at most 200 characters)."
++ },
++ {
++ "name": "votes",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many votes the request has. The count says nothing about who voted."
++ },
++ "description": "How many votes the request has. The count says nothing about who voted."
++ },
++ {
++ "name": "voters",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many people voted (one vote each)."
++ },
++ "description": "How many people voted (one vote each)."
++ },
++ {
++ "name": "state",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "enum": [
++ "queued",
++ "running",
++ "done",
++ "failed",
++ "skipped",
++ "removed",
++ "refused"
++ ],
++ "description": "Where the request stands: queued (waiting), running (the desk is on it), done or failed (it ran), skipped (a moderator or the owner passed over it), removed (it left the queue) or refused (the safety check or the filters turned it down)."
++ },
++ "description": "Where the request stands: queued (waiting), running (the desk is on it), done or failed (it ran), skipped (a moderator or the owner passed over it), removed (it left the queue) or refused (the safety check or the filters turned it down)."
++ },
++ {
++ "name": "pinned",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "boolean",
++ "description": "A pinned request stays at the top of the queue until it is removed or it runs."
++ },
++ "description": "A pinned request stays at the top of the queue until it is removed or it runs."
++ },
++ {
++ "name": "created_at",
++ "in": "query",
++ "required": true,
++ "schema": {
++ "type": "string",
++ "format": "date-time",
++ "description": "When the request was asked for."
++ },
++ "description": "When the request was asked for."
++ },
++ {
++ "name": "started_at",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When the desk began it, or null while it waits."
++ },
++ "description": "When the desk began it, or null while it waits."
++ },
++ {
++ "name": "ended_at",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When it finished, was skipped or was removed, or null while it stands."
++ },
++ "description": "When it finished, was skipped or was removed, or null while it stands."
++ },
++ {
++ "name": "refusal",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": false,
++ "required": [
++ "code",
++ "reason"
++ ],
++ "description": "Why the request was refused, when it was: the code and the one-line reason the room saw. Null when the request was accepted.",
++ "properties": {
++ "code": {
++ "enum": [
++ "platform_floor",
++ "harm",
++ "attack",
++ "account_or_money",
++ "account_too_new",
++ "too_long",
++ "already_open"
++ ],
++ "description": "Which rule turned it down."
++ },
++ "reason": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "The one-line reason shown in the room."
++ }
++ }
++ },
++ "description": "Why the request was refused, when it was: the code and the one-line reason the room saw. Null when the request was accepted."
++ },
++ {
++ "name": "result",
++ "in": "query",
++ "required": false,
++ "schema": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": false,
++ "required": [
++ "outcome",
++ "line"
++ ],
++ "description": "How the run ended, once it ran: the outcome and the line the overlay and the voice said.",
++ "properties": {
++ "outcome": {
++ "enum": [
++ "done",
++ "failed",
++ "timeout",
++ "budget",
++ "stopped"
++ ],
++ "description": "How it ended."
++ },
++ "line": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "The one line the room and the overlay saw, e.g. 'Done: a snake game at the scratch address'."
++ },
++ "scratch_url": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "uri",
++ "description": "The throwaway scratch site the run left, when it left one."
++ },
++ "task_id": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "description": "The Codes task that ran it."
++ }
++ }
++ },
++ "description": "How the run ended, once it ran: the outcome and the line the overlay and the voice said."
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/chat.play-request.v1"
++ }
++ }
++ }
++ },
++ "default": {
++ "description": "An error (RFC 9457 problem details)",
++ "content": {
++ "application/problem+json": {
++ "schema": {
++ "$ref": "#/components/schemas/errors.problem.v1"
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ "/internal/chat/rooms/agent{play}-queue/{request_id}": {
++ "post": {
++ "operationId": "post_internal_chat_rooms_agentplay_queue_request_id",
++ "summary": "Write a chat plays queue on OpenVibe.Chat (the Agent Streams plan, S6): record how a request ran.",
++ "description": "**chat.play_queue.write** (first-party): Write a chat plays queue on OpenVibe.Chat (the Agent Streams plan, S6): record how a request ran. POST /internal/chat/rooms/agent:play-queue/:request_id takes chat.play-request@1 and moves the request from queued to running, and records its outcome (done, failed, timeout, budget or stopped) with the one line the overlay and the voice say and the scratch site the run left. The caller is Codes, the task's service, acting for the stream's owner (a node token on_behalf_of). Chat never lets this capability ask for money, add a person to the queue or change a vote: the queue's people and votes belong to the room.",
++ "tags": [
++ "chat.play_queue"
++ ],
++ "x-openvibe-capabilities": [
++ "chat.play_queue.write"
++ ],
++ "x-openvibe-visibility": [
++ "first-party"
++ ],
++ "x-openvibe-quota-class": "internal-write",
++ "security": [
++ {
++ "openvibeToken": [
++ "chat.play_queue.write"
++ ]
++ }
++ ],
++ "requestBody": {
++ "required": false,
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/chat.play-request.v1"
++ }
++ }
++ }
++ },
++ "parameters": [
++ {
++ "name": "play",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ },
++ {
++ "name": "request_id",
++ "in": "path",
++ "required": true,
++ "schema": {
++ "type": "string"
++ }
++ }
++ ],
++ "responses": {
++ "2XX": {
++ "description": "Success",
++ "content": {
++ "application/json": {
++ "schema": {
++ "$ref": "#/components/schemas/chat.play-request.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",
@@@ -8055,6 +8429,235 @@
}
]
},
++ "chat.play-request.v1": {
++ "title": "ChatPlayRequest",
++ "description": "chat.play-request@1 (the Agent Streams plan, S6): one viewer's request for the chat plays desk to build or do something. A request is written with `!do <request>` (at most 200 characters) in an agent stream's room, `agent:<ags_id>`. One open request per person at a time, and an account younger than a day cannot ask. The request passes the house text filters and the safety check before it is listed; a refused request carries a one-line reason and never enters the queue. Viewers vote with a thumbs-up or `!vote <n>`, and every five minutes, or when the current request finishes, the top request becomes the next task. The owner or a moderator can skip, remove or pin a request. The result is the run's outcome and the scratch site it left behind.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "request_id",
++ "room",
++ "author",
++ "text",
++ "votes",
++ "state",
++ "pinned",
++ "created_at"
++ ],
++ "properties": {
++ "request_id": {
++ "type": "string",
++ "pattern": "^req_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The request's id (ULID, Crockford base32)."
++ },
++ "room": {
++ "type": "string",
++ "pattern": "^agent:ags_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The room the request was made in: an agent stream's room, `agent:<ags_id>`."
++ },
++ "author": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "handle"
++ ],
++ "description": "Who asked. The handle and the display name only, never an id.",
++ "properties": {
++ "handle": {
++ "type": "string",
++ "pattern": "^[a-z0-9_]{2,32}$",
++ "description": "The handle of the person who asked."
++ },
++ "display_name": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "maxLength": 64,
++ "description": "Their display name, when they have one."
++ }
++ }
++ },
++ "text": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "What the viewer asked for, as they typed it (at most 200 characters)."
++ },
++ "votes": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many votes the request has. The count says nothing about who voted."
++ },
++ "voters": {
++ "type": "integer",
++ "minimum": 0,
++ "description": "How many people voted (one vote each)."
++ },
++ "state": {
++ "enum": [
++ "queued",
++ "running",
++ "done",
++ "failed",
++ "skipped",
++ "removed",
++ "refused"
++ ],
++ "description": "Where the request stands: queued (waiting), running (the desk is on it), done or failed (it ran), skipped (a moderator or the owner passed over it), removed (it left the queue) or refused (the safety check or the filters turned it down)."
++ },
++ "pinned": {
++ "type": "boolean",
++ "description": "A pinned request stays at the top of the queue until it is removed or it runs."
++ },
++ "created_at": {
++ "type": "string",
++ "format": "date-time",
++ "description": "When the request was asked for."
++ },
++ "started_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When the desk began it, or null while it waits."
++ },
++ "ended_at": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "date-time",
++ "description": "When it finished, was skipped or was removed, or null while it stands."
++ },
++ "refusal": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": false,
++ "required": [
++ "code",
++ "reason"
++ ],
++ "description": "Why the request was refused, when it was: the code and the one-line reason the room saw. Null when the request was accepted.",
++ "properties": {
++ "code": {
++ "enum": [
++ "platform_floor",
++ "harm",
++ "attack",
++ "account_or_money",
++ "account_too_new",
++ "too_long",
++ "already_open"
++ ],
++ "description": "Which rule turned it down."
++ },
++ "reason": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "The one-line reason shown in the room."
++ }
++ }
++ },
++ "result": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "additionalProperties": false,
++ "required": [
++ "outcome",
++ "line"
++ ],
++ "description": "How the run ended, once it ran: the outcome and the line the overlay and the voice said.",
++ "properties": {
++ "outcome": {
++ "enum": [
++ "done",
++ "failed",
++ "timeout",
++ "budget",
++ "stopped"
++ ],
++ "description": "How it ended."
++ },
++ "line": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 200,
++ "description": "The one line the room and the overlay saw, e.g. 'Done: a snake game at the scratch address'."
++ },
++ "scratch_url": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "uri",
++ "description": "The throwaway scratch site the run left, when it left one."
++ },
++ "task_id": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "description": "The Codes task that ran it."
++ }
++ }
++ }
++ },
++ "allOf": [
++ {
++ "if": {
++ "properties": {
++ "state": {
++ "const": "refused"
++ }
++ },
++ "required": [
++ "state"
++ ]
++ },
++ "then": {
++ "properties": {
++ "refusal": {
++ "type": "object"
++ },
++ "result": {
++ "type": "null"
++ }
++ },
++ "required": [
++ "refusal"
++ ]
++ }
++ },
++ {
++ "if": {
++ "properties": {
++ "state": {
++ "not": {
++ "const": "refused"
++ }
++ }
++ },
++ "required": [
++ "state"
++ ]
++ },
++ "then": {
++ "properties": {
++ "refusal": {
++ "type": "null"
++ }
++ }
++ }
++ }
++ ]
++ },
"chat.send-request.v1": {
"title": "ChatSendRequest",
"description": "chat.send-request@1: what a sender hands OpenVibe.Chat to post a chat line (chat.message.send). From a browser, a frame on WS /ws/chat of type chat in the room the socket joined (the text is trimmed; empty or longer than 6000 characters is dropped, the channel's own max_message_length is enforced after that; a line starting with ! or / is a command, not a message). From Live's bridge, POST /internal/live/calls: a chat.live_bridge.write batch in which every op that sends a message (a chat or dm frame, db saveChatMessage, deployNotice) also needs chat.message.send; without it that op alone is refused. From a first-party service, POST /internal/chat/messages: one line (or one DM) that Chat stores, broadcasts to the room (with mirror, also to global chat and the streamer rooms), reads aloud (tts) and answers with the real id; applied once per (caller, key), the same key with another body gets 409.",
ddiff --git a/generated/openapi/codes.json b/generated/openapi/codes.json
iindex a476e828..44517d1d 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 42b5c15f..1ed9fcab 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 55b30c9d..b0fd6b19 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 1e531982..94a537b8 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 f6bb2698..586ec0e2 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 62bf9012..42d56535 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 a8e98237..1e57be24 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 5e249f12..1f33a14f 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 446db208..b2e91737 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 2fcca6e7..c4e29cba 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": 58,
-- "capabilities": 22
++ "operations": 60,
++ "capabilities": 24
},
{
"service": "codes",
ddiff --git a/generated/openapi/inventory.json b/generated/openapi/inventory.json
iindex 02eb193d..5d777532 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 665b4b01..f1f3f3fa 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.304.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-hub.json b/generated/openapi/media-hub.json
iindex ba30e0fa..25ea2826 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 e1e8dce4..2393543d 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 ee4429d1..af58f769 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 63ced13b..d0bf2b93 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 7362753d..998d1a28 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 0a09fa21..0e1e0fc1 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 3497d1a8..a9d853be 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 255f25c1..a5d699aa 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 7e02fd8c..94e3c441 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 d9374621..ffff1e13 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 1d551817..5cd08a78 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 b4e679d4..ecfbac78 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 2aebbd64..34da8a63 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 9a549668..ef8b46e3 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 cb541a24..42d2b9e3 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 77387de5..ecb57d6b 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 95842723..abacad50 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 8e2026cf..a2f49c49 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 237fb484..5c503236 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 35e8db48..4128c66b 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 6a12ba35..f235b649 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 90def6b6..9d8a4a6d 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 bb96a2f8..98b89ffc 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 3ead4f72..475ca072 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.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.",
++ "version": "0.304.0",
++ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.304.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 d2660179..fa83c78a 100644
---- a/generated/typescript/index.d.ts
++++ b/generated/typescript/index.d.ts
@@@ -294490,13 +294490,13 @@ export type ChatAgentPoll = {
/** [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.
++ * live.agent-stream-control@1 (the Agent Streams plan, S5): one of the 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. WIPE is the chat plays control of S6: it stops the run and resets the sandbox desk. A moderator may press every control, 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";
++ control: "pause" | "curtain" | "mute" | "end" | "wipe";
/**
* Who pressed it: the handle and the role they hold. Never an id, never anything else about them.
*/
@@@ -294786,3 +294786,189 @@ export interface ChatTTSSpeakResult {
rate?: number;
voice_id?: string | null;
}
++
++/** [email protected] (owner: chat) */
++/**
++ * chat.play-request@1 (the Agent Streams plan, S6): one viewer's request for the chat plays desk to build or do something. A request is written with `!do <request>` (at most 200 characters) in an agent stream's room, `agent:<ags_id>`. One open request per person at a time, and an account younger than a day cannot ask. The request passes the house text filters and the safety check before it is listed; a refused request carries a one-line reason and never enters the queue. Viewers vote with a thumbs-up or `!vote <n>`, and every five minutes, or when the current request finishes, the top request becomes the next task. The owner or a moderator can skip, remove or pin a request. The result is the run's outcome and the scratch site it left behind.
++ */
++export type ChatPlayRequest = {
++ [k: string]: unknown | undefined;
++} & {
++ /**
++ * The request's id (ULID, Crockford base32).
++ */
++ request_id: string;
++ /**
++ * The room the request was made in: an agent stream's room, `agent:<ags_id>`.
++ */
++ room: string;
++ /**
++ * Who asked. The handle and the display name only, never an id.
++ */
++ author: {
++ /**
++ * The handle of the person who asked.
++ */
++ handle: string;
++ /**
++ * Their display name, when they have one.
++ */
++ display_name?: string | null;
++ };
++ /**
++ * What the viewer asked for, as they typed it (at most 200 characters).
++ */
++ text: string;
++ /**
++ * How many votes the request has. The count says nothing about who voted.
++ */
++ votes: number;
++ /**
++ * How many people voted (one vote each).
++ */
++ voters?: number;
++ /**
++ * Where the request stands: queued (waiting), running (the desk is on it), done or failed (it ran), skipped (a moderator or the owner passed over it), removed (it left the queue) or refused (the safety check or the filters turned it down).
++ */
++ state: "queued" | "running" | "done" | "failed" | "skipped" | "removed" | "refused";
++ /**
++ * A pinned request stays at the top of the queue until it is removed or it runs.
++ */
++ pinned: boolean;
++ /**
++ * When the request was asked for.
++ */
++ created_at: string;
++ /**
++ * When the desk began it, or null while it waits.
++ */
++ started_at?: string | null;
++ /**
++ * When it finished, was skipped or was removed, or null while it stands.
++ */
++ ended_at?: string | null;
++ /**
++ * Why the request was refused, when it was: the code and the one-line reason the room saw. Null when the request was accepted.
++ */
++ refusal?: {
++ /**
++ * Which rule turned it down.
++ */
++ code: "platform_floor" | "harm" | "attack" | "account_or_money" | "account_too_new" | "too_long" | "already_open";
++ /**
++ * The one-line reason shown in the room.
++ */
++ reason: string;
++ } | null;
++ /**
++ * How the run ended, once it ran: the outcome and the line the overlay and the voice said.
++ */
++ result?: {
++ /**
++ * How it ended.
++ */
++ outcome: "done" | "failed" | "timeout" | "budget" | "stopped";
++ /**
++ * The one line the room and the overlay saw, e.g. 'Done: a snake game at the scratch address'.
++ */
++ line: string;
++ /**
++ * The throwaway scratch site the run left, when it left one.
++ */
++ scratch_url?: string | null;
++ /**
++ * The Codes task that ran it.
++ */
++ task_id?: string | null;
++ } | null;
++};
++
++/** [email protected] (owner: codes) */
++/**
++ * codes.sandbox-session@1 (the Agent Streams plan, S6): the sandbox desk a chat plays session runs on. The desk is started with `--shared`: no profile, no keys, no person's files, public egress only (no private address is reachable) and a fresh workspace, and it is wiped at the end of the session. The owner sets the time box (5 to 30 minutes) and the cap (money and minutes), and arms the session with Go before the first request may run. The scratch site is a throwaway OpenVibe.Space address that is removed after 24 hours unless the owner keeps it.
++ */
++export interface CodesSandboxSession {
++ /**
++ * The sandbox session's id (ULID, Crockford base32).
++ */
++ session_id: string;
++ /**
++ * The task the desk is running for the queue.
++ */
++ task: {
++ /**
++ * The task's id in Codes.
++ */
++ id: string;
++ title: string;
++ };
++ /**
++ * The person the session spends their own allowance for.
++ */
++ owner: string;
++ /**
++ * Where the desk stands: idle (nothing yet), armed (the owner pressed Go, the first request may run), running, paused, wiped (Wipe now reset the sandbox) or ended.
++ */
++ state: "idle" | "armed" | "running" | "paused" | "wiped" | "ended";
++ /**
++ * How long one request may run, in minutes (the owner sets 5 to 30; the default is 10).
++ */
++ time_box_minutes: number;
++ /**
++ * How many steps one request may take.
++ */
++ step_budget: number;
++ /**
++ * The owner's cap for the session: the money and the minutes it may spend. When it is spent, the queue closes.
++ */
++ cap: {
++ /**
++ * The cap in cents.
++ */
++ money_cents: number;
++ /**
++ * The cap in minutes of work.
++ */
++ minutes: number;
++ };
++ /**
++ * What the session has spent so far.
++ */
++ spend: {
++ money_cents: number;
++ seconds: number;
++ steps: number;
++ };
++ /**
++ * Whether the owner pressed Go. The first request of a session waits for it.
++ */
++ armed: boolean;
++ /**
++ * When the owner pressed Go.
++ */
++ armed_at?: string | null;
++ started_at?: string | null;
++ ended_at?: string | null;
++ /**
++ * When Wipe now reset the sandbox.
++ */
++ wiped_at?: string | null;
++ /**
++ * Always true: the sandbox reaches public addresses only, and no private address is reachable.
++ */
++ egress_public_only: true;
++ /**
++ * The throwaway site the run left, or null before the first one.
++ */
++ scratch_site?: {
++ url: string;
++ /**
++ * When it is removed (24 hours after it was made, unless it is kept).
++ */
++ expires_at: string;
++ /**
++ * Whether the owner kept it past its 24 hours.
++ */
++ kept: boolean;
++ } | null;
++}
ddiff --git a/lib/ids.js b/lib/ids.js
iindex 4b8d8336..b5a6fa06 100644
---- a/lib/ids.js
++++ b/lib/ids.js
@@@ -6,7 +6,7 @@
const crypto = require('crypto');
const ALPHABET = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
--const PREFIX = { user: 'usr', guest: 'gst', app: 'app', mod: 'mod', agent: 'agt', node: 'nod', event: 'evt', media: 'med', project: 'prj', confirmation: 'cnf', consent: 'cns', job: 'job', task: 'tsk', watch: 'wch', wait: 'wai', observation: 'wco', check: 'ckr', subscription: 'sub', deploy: 'dpl', domain: 'dom', manifest: 'mfs', release: 'rel', site: 'sit', robot: 'rob', stream: 'std', webhook_delivery: 'whd', share: 'shr', device: 'dev', hook: 'hok', policy: 'pol', audit: 'aud' };
++const PREFIX = { user: 'usr', guest: 'gst', app: 'app', mod: 'mod', agent: 'agt', node: 'nod', event: 'evt', media: 'med', project: 'prj', confirmation: 'cnf', consent: 'cns', job: 'job', task: 'tsk', watch: 'wch', wait: 'wai', observation: 'wco', check: 'ckr', subscription: 'sub', deploy: 'dpl', domain: 'dom', manifest: 'mfs', release: 'rel', site: 'sit', robot: 'rob', stream: 'std', webhook_delivery: 'whd', share: 'shr', device: 'dev', hook: 'hok', policy: 'pol', audit: 'aud', agent_stream: 'ags', play_request: 'req', sandbox_session: 'sbs' };
const SLUG_TYPES = new Set(['service', 'system']);
const ULID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/;
const SLUG_RE = /^[a-z][a-z0-9-]{1,39}$/;
ddiff --git a/manifests/capabilities/chat.play_queue.read.json b/manifests/capabilities/chat.play_queue.read.json
nnew file mode 100644
iindex 00000000..246b67cf
---- /dev/null
++++ b/manifests/capabilities/chat.play_queue.read.json
@@@ -0,0 +1,21 @@
++{
++ "id": "chat.play_queue.read",
++ "version": "1.0",
++ "owner": "chat",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Read a chat plays queue on OpenVibe.Chat (the Agent Streams plan, S6). GET /internal/chat/rooms/agent:play-queue answers chat.play-request@1: the requests of the room `agent:<ags_id>`, each with its votes and its state, the current request and the top of the queue. The caller is Codes, the task's service, acting for the stream's owner (a node token on_behalf_of), so the queue it reads is the owner's room and nobody else's. Chat chooses what the queue holds: a request enters it only after the house text filters and the safety check, one open request per person at a time.",
++ "inputSchema": "chat.play-request@1",
++ "outputSchema": "chat.play-request@1",
++ "permissions": [
++ "chat:read"
++ ],
++ "resourceConstraints": [
++ "owner"
++ ],
++ "quotaClass": "internal-read",
++ "events": [],
++ "implementedBy": [
++ "GET /internal/chat/rooms/agent:play-queue"
++ ]
++}
ddiff --git a/manifests/capabilities/chat.play_queue.write.json b/manifests/capabilities/chat.play_queue.write.json
nnew file mode 100644
iindex 00000000..1108e164
---- /dev/null
++++ b/manifests/capabilities/chat.play_queue.write.json
@@@ -0,0 +1,21 @@
++{
++ "id": "chat.play_queue.write",
++ "version": "1.0",
++ "owner": "chat",
++ "status": "active",
++ "visibility": "first-party",
++ "description": "Write a chat plays queue on OpenVibe.Chat (the Agent Streams plan, S6): record how a request ran. POST /internal/chat/rooms/agent:play-queue/:request_id takes chat.play-request@1 and moves the request from queued to running, and records its outcome (done, failed, timeout, budget or stopped) with the one line the overlay and the voice say and the scratch site the run left. The caller is Codes, the task's service, acting for the stream's owner (a node token on_behalf_of). Chat never lets this capability ask for money, add a person to the queue or change a vote: the queue's people and votes belong to the room.",
++ "inputSchema": "chat.play-request@1",
++ "outputSchema": "chat.play-request@1",
++ "permissions": [
++ "chat:write"
++ ],
++ "resourceConstraints": [
++ "owner"
++ ],
++ "quotaClass": "internal-write",
++ "events": [],
++ "implementedBy": [
++ "POST /internal/chat/rooms/agent:play-queue/:request_id"
++ ]
++}
ddiff --git a/manifests/services/chat.json b/manifests/services/chat.json
iindex 1ddc9153..89624789 100644
---- a/manifests/services/chat.json
++++ b/manifests/services/chat.json
@@@ -1,5 +1,5 @@
{
-- "version": "0.4.0",
++ "version": "0.5.0",
"contractRanges": {
"openvibe-contracts": ">=0.13.0 <1.0.0"
},
@@@ -21,6 +21,8 @@
"chat.inbox.read",
"chat.inbox.reply",
"chat.live_bridge.write",
++ "chat.play_queue.read",
++ "chat.play_queue.write",
"chat.message.send",
"chat.messages.read",
"chat.moderation.queue.read",
ddiff --git a/package-lock.json b/package-lock.json
iindex b7f654c0..11c085fd 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -1,12 +1,12 @@
{
"name": "openvibe-contracts",
-- "version": "0.302.0",
++ "version": "0.304.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openvibe-contracts",
-- "version": "0.302.0",
++ "version": "0.304.0",
"license": "AGPL-3.0-only",
"dependencies": {
"ajv": "^8.17.1",
ddiff --git a/package.json b/package.json
iindex 27e14667..d1ddd66c 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-contracts",
-- "version": "0.303.0",
++ "version": "0.304.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",