openvibers/openvibe.contracts Commits

0.340.0: actor.canvas@1, an agent's live panel as data (ADR-059) (#348)

OP OpenVibers committed 5 days ago · b8b3335356 · 61 files, +1838 −91

Blocks only (heading, text, list, checklist, table, metric, chart, picture from openvibe.media, link, button), drawn by
one Shared renderer; no HTML, style, script or frame; a button only sends its label to the agent as the person's
message.

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex e5a4187e..3da9c73d 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -1,4 +1,13 @@
# Changelog
++
++## 0.340.0 — 2026-11-08
++
++- Add `actor.canvas@1` (ADR-059, plan HO9): an agent's live panel beside the conversation, as data. A canvas is a
++ list of blocks (a heading, text, a list, a checklist, a table, a metric, a bar or line chart, a picture from
++ openvibe.media, a link, a button) drawn by one renderer (OpenVibe.Shared `ov-canvas.js`) wherever it is shown. There
++ is no HTML, style, script or frame; text is plain. A button only sends its label to the agent as the person's own
++ message, and ticking a checklist item tells the agent which item changed: a canvas never acts by itself. `version`
++ only grows, and the agent replaces the whole document.
## 0.339.0 — 2026-11-08
- The batches contract says what cancelling does. Only the prompts that never had a run are cancelled, and
ddiff --git a/contracts/actor/canvas.v1.json b/contracts/actor/canvas.v1.json
nnew file mode 100644
iindex 00000000..ad5b0c4c
---- /dev/null
++++ b/contracts/actor/canvas.v1.json
@@@ -0,0 +1,433 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/actor/canvas.v1.json",
++ "title": "ActorCanvas",
++ "description": "actor.canvas@1: an agent's live panel beside the conversation (ADR-059) — a board of a trip, a table of prices, a checklist of what is left. It is DATA: a list of blocks drawn by one renderer (OpenVibe.Shared ov-canvas.js) wherever it is shown (Chat, Surf, the Dock, Actor). There is no HTML, no style, no script and no frame: text is plain and the renderer escapes it; an image is only a picture on openvibe.media; a link opens in a new tab with its host shown. A canvas acts only through the person: a button sends its label to the agent as the person's own message (action ask), and ticking a checklist item tells the agent which item changed; the agent's next steps go through its level and approvals like any other. VERSION only grows: a viewer ignores an older one, and the agent replaces the whole document, never part of it.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "canvas",
++ "id",
++ "title",
++ "version",
++ "blocks",
++ "updated_at"
++ ],
++ "properties": {
++ "canvas": {
++ "const": "actor.canvas@1"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^cnv_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "title": {
++ "type": "string",
++ "maxLength": 120,
++ "minLength": 1
++ },
++ "version": {
++ "type": "integer",
++ "minimum": 1,
++ "description": "Grows with every update; a viewer ignores an older version."
++ },
++ "agent_id": {
++ "type": "string",
++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The agent that draws it."
++ },
++ "thread": {
++ "type": "string",
++ "maxLength": 64,
++ "description": "The conversation it sits beside (Actor's task or chat id)."
++ },
++ "updated_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "blocks": {
++ "type": "array",
++ "maxItems": 60,
++ "items": {
++ "oneOf": [
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "A heading.",
++ "required": [
++ "type",
++ "text"
++ ],
++ "properties": {
++ "type": {
++ "const": "heading"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "text": {
++ "type": "string",
++ "maxLength": 120,
++ "minLength": 1
++ },
++ "level": {
++ "type": "integer",
++ "enum": [
++ 1,
++ 2
++ ]
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "Plain text; line breaks are kept, and nothing in it is markup.",
++ "required": [
++ "type",
++ "text"
++ ],
++ "properties": {
++ "type": {
++ "const": "text"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "text": {
++ "type": "string",
++ "maxLength": 4000,
++ "minLength": 1
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "A list of short lines.",
++ "required": [
++ "type",
++ "items"
++ ],
++ "properties": {
++ "type": {
++ "const": "list"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "items": {
++ "type": "array",
++ "maxItems": 50,
++ "items": {
++ "type": "string",
++ "maxLength": 280,
++ "minLength": 1
++ }
++ },
++ "ordered": {
++ "type": "boolean"
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "Items the person can tick; a tick tells the agent which item changed.",
++ "required": [
++ "type",
++ "items"
++ ],
++ "properties": {
++ "type": {
++ "const": "checklist"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "items": {
++ "type": "array",
++ "maxItems": 50,
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "text",
++ "done"
++ ],
++ "properties": {
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$"
++ },
++ "text": {
++ "type": "string",
++ "maxLength": 280,
++ "minLength": 1
++ },
++ "done": {
++ "type": "boolean"
++ }
++ }
++ }
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "A table: up to 8 columns and 100 rows of text or numbers.",
++ "required": [
++ "type",
++ "columns",
++ "rows"
++ ],
++ "properties": {
++ "type": {
++ "const": "table"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "columns": {
++ "type": "array",
++ "minItems": 1,
++ "maxItems": 8,
++ "items": {
++ "type": "string",
++ "maxLength": 40,
++ "minLength": 1
++ }
++ },
++ "rows": {
++ "type": "array",
++ "maxItems": 100,
++ "items": {
++ "type": "array",
++ "maxItems": 8,
++ "items": {
++ "oneOf": [
++ {
++ "type": "string",
++ "maxLength": 200
++ },
++ {
++ "type": "number"
++ }
++ ]
++ }
++ }
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "One number that matters, with its label.",
++ "required": [
++ "type",
++ "label",
++ "value"
++ ],
++ "properties": {
++ "type": {
++ "const": "metric"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "label": {
++ "type": "string",
++ "maxLength": 60,
++ "minLength": 1
++ },
++ "value": {
++ "type": "string",
++ "maxLength": 40,
++ "minLength": 1
++ },
++ "note": {
++ "type": "string",
++ "maxLength": 120
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "A small bar or line chart; the renderer draws it, nothing is fetched.",
++ "required": [
++ "type",
++ "kind",
++ "series"
++ ],
++ "properties": {
++ "type": {
++ "const": "chart"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "kind": {
++ "type": "string",
++ "enum": [
++ "bar",
++ "line"
++ ]
++ },
++ "unit": {
++ "type": "string",
++ "maxLength": 12
++ },
++ "series": {
++ "type": "array",
++ "minItems": 1,
++ "maxItems": 4,
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "name",
++ "points"
++ ],
++ "properties": {
++ "name": {
++ "type": "string",
++ "maxLength": 40,
++ "minLength": 1
++ },
++ "points": {
++ "type": "array",
++ "minItems": 1,
++ "maxItems": 60,
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "x",
++ "y"
++ ],
++ "properties": {
++ "x": {
++ "type": "string",
++ "maxLength": 20,
++ "minLength": 1
++ },
++ "y": {
++ "type": "number"
++ }
++ }
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "A picture from OpenVibe.Media.",
++ "required": [
++ "type",
++ "src",
++ "alt"
++ ],
++ "properties": {
++ "type": {
++ "const": "image"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "src": {
++ "type": "string",
++ "pattern": "^https://openvibe\\.media/[A-Za-z0-9._~/%-]{1,1024}(\\?[A-Za-z0-9._~=&%-]{0,200})?$",
++ "description": "A picture on openvibe.media, and nowhere else."
++ },
++ "alt": {
++ "type": "string",
++ "maxLength": 200
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "A link: it opens in a new tab and shows its host.",
++ "required": [
++ "type",
++ "href",
++ "text"
++ ],
++ "properties": {
++ "type": {
++ "const": "link"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "href": {
++ "type": "string",
++ "pattern": "^https://[^\\s\"'<>]{1,2048}$"
++ },
++ "text": {
++ "type": "string",
++ "maxLength": 120,
++ "minLength": 1
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "A reply the person can send with one tap.",
++ "required": [
++ "type",
++ "label",
++ "action"
++ ],
++ "properties": {
++ "type": {
++ "const": "button"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "label": {
++ "type": "string",
++ "maxLength": 40,
++ "minLength": 1
++ },
++ "action": {
++ "const": "ask",
++ "description": "The label goes to the agent as the person's own message. A button never acts by itself."
++ }
++ }
++ }
++ ]
++ }
++ }
++ }
++}
ddiff --git a/contracts/catalog.json b/contracts/catalog.json
iindex 6e4fcf22..60ddea8b 100644
---- a/contracts/catalog.json
++++ b/contracts/catalog.json
@@@ -9979,6 +9979,16 @@
"status": "active",
"adr": "ADR-058"
},
++ {
++ "id": "actor.canvas",
++ "version": "1.0.0",
++ "owner": "actor",
++ "schema": "actor/canvas.v1.json",
++ "visibility": "public",
++ "compatibility": "backward",
++ "status": "active",
++ "adr": "ADR-059"
++ },
{
"id": "actor.skill",
"version": "1.0.0",
ddiff --git a/docs/adr/ADR-059-canvas.md b/docs/adr/ADR-059-canvas.md
nnew file mode 100644
iindex 00000000..67d7a548
---- /dev/null
++++ b/docs/adr/ADR-059-canvas.md
@@@ -0,0 +1,56 @@
++# ADR-059: The canvas — an agent's live panel is data, drawn by one renderer
++
++- **Status:** Accepted 2026-11-08 (plan track HO, wave 3: HO9).
++- **Owner:** OpenVibe.Actor (the canvas belongs to the person's agent and its thread). OpenVibe.Shared draws it, and
++ Chat, Surf, the Dock and Actor's own pages show it.
++- **Contract:** `actor.canvas@1`.
++
++## Evidence
++
++- The study of Hermes and OpenClaw (`plan-state-2026-10-22/hermes-openclaw-2026-11-07.md`, item 13) found that an
++ agent which can draw a panel beside the conversation feels like a product rather than a chat: a board of a trip,
++ a table of prices, a checklist of what is left.
++- OpenClaw's Canvas pushes an HTML document to a panel served by its gateway (`docs/platforms/mac/canvas.md`,
++ `src/canvas/`). That works for one person on one machine.
++- On OpenVibe a canvas is shown on several sites, to a signed-in person, beside their messages. HTML or script
++ written by a model, or by whatever page the model read, would be cross-site scripting by design.
++
++## Decision
++
++1. **A canvas is a document of blocks:**
++ - heading, text, list, checklist, table, metric, chart (bar or line), image, link and button;
++ - `actor.canvas@1` names each block and its limits.
++ - There is no HTML, no style, no script and no frame: text is plain and the renderer escapes it.
++2. **One renderer** (OpenVibe.Shared `ov-canvas.js`) draws it everywhere, so a canvas looks and behaves the same in
++ Chat, Surf, the Dock and Actor.
++3. **What leaves the page is bounded:**
++ - an image is only a picture on `openvibe.media`;
++ - a link opens in a new tab with its host shown;
++ - nothing loads from anywhere else.
++4. **A canvas acts only through the person:**
++ - a button sends its label to the agent as the person's own message (`action: "ask"`), and the agent's next steps
++ go through its level and approvals like any other;
++ - ticking a checklist item tells the agent which item changed.
++ - A canvas never performs an act by itself.
++5. **It is live and versioned:** `version` only grows. A viewer ignores an older version, and the agent replaces the
++ whole document (at most 60 blocks), never parts of it.
++
++## Alternatives
++
++- **HTML widgets (OpenClaw's way).** Rejected: script and style from a model, shown on every OpenVibe site.
++- **Markdown only.** Rejected: no tables with numbers, no checklists that answer back, no charts.
++- **A canvas per site.** Rejected: one renderer and one contract are what make it the same everywhere.
++
++## Migration and rollback
++
++A new contract. Nothing consumes it until Actor stores canvases and Shared draws them. Rollback is retiring the
++contract.
++
++## Tests
++
++`test/run.js`:
++- the fixtures validate, and the invalid ones are refused: an html block, a picture from elsewhere, a button that
++ runs something, too many blocks;
++- no block type is named html, script, style or iframe;
++- a button's only action is `ask`;
++- an image's `src` is on `openvibe.media` only.
ddiff --git a/docs/adr/README.md b/docs/adr/README.md
iindex 3dc9bddc..3ad58aa6 100644
---- a/docs/adr/README.md
++++ b/docs/adr/README.md
@@@ -52,3 +52,4 @@ Platform-wide decisions from the OpenVibe development roadmap (section 10). Each
| [ADR-056](ADR-056-project-secrets.md) | Project secrets: OpenVibe.Network keeps them per project and environment, write-only (no API returns a value to a person), AES-256-GCM under `NETWORK_SECRETS_KEY`; a service uses one only when the project granted it, to `sign` (HMAC, the value never leaves Network) or to `reveal` it for a listed https origin, through first-party `network.secret.use`; archiving a project shreds its secrets | Accepted 2026-10-15 (plan T13/T17) |
| [ADR-057](ADR-057-website-connect.md) | Website Connect: a focused Go `quic-go` edge relay for Share, a Node OpenVibe.Connect control API, headscale + embedded `tsnet` for the private mesh with a tenant-isolation proof, and Events-owned durable hook delivery; two-credential node/share tunnel handshake, immediate revocation, edge policy enforcement, no secret or body in audit, a thin MIT SDK boundary, and the single-label `agents-<owner>.openvibe.website` first proof (Universal SSL does not cover `agents.owner.openvibe.website`; deep labels are CON-2); registers 9 v1 contracts and 20 planned capabilities, grants and deploys nothing | Proposed 2026-10-29 (CON-1; plan 2026-10-28 §5.9b), for the owner's review |
| [ADR-058](ADR-058-skills.md) | Skills: a skill is a document (`actor.skill@1`: a prompt with text resources, an integrity hash that is not a signature, a `requires` that grants nothing and names HO1's approval kinds); the registry's listing (`actor.skill-listing@1`) vouches for the publisher and carries the scan and the curator's state to every site: passed installs in one tap, flagged after the person reads the findings, failed never | Accepted 2026-11-07 (plan HO2, the skeleton) |
++| [ADR-059](ADR-059-canvas.md) | The canvas: an agent's live panel is data (`actor.canvas@1`: headings, text, lists, checklists, tables, metrics, bar and line charts, pictures from openvibe.media, links, buttons; no HTML, style, script or frame), drawn by one Shared renderer on every site; a button only sends its label to the agent as the person's message, and a checklist tick tells the agent which item changed | Accepted 2026-11-08 (plan HO9) |
ddiff --git a/fixtures/actor.canvas/invalid/button-runs.json b/fixtures/actor.canvas/invalid/button-runs.json
nnew file mode 100644
iindex 00000000..6f88cb79
---- /dev/null
++++ b/fixtures/actor.canvas/invalid/button-runs.json
@@@ -0,0 +1,16 @@
++{
++ "canvas": "actor.canvas@1",
++ "id": "cnv_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "title": "Lisbon, 14–17 November",
++ "version": 3,
++ "agent_id": "agt_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "thread": "tsk_42",
++ "updated_at": "2026-11-08T01:00:00.000Z",
++ "blocks": [
++ {
++ "type": "button",
++ "label": "Delete everything",
++ "action": "run"
++ }
++ ]
++}
ddiff --git a/fixtures/actor.canvas/invalid/foreign-image.json b/fixtures/actor.canvas/invalid/foreign-image.json
nnew file mode 100644
iindex 00000000..7c84343e
---- /dev/null
++++ b/fixtures/actor.canvas/invalid/foreign-image.json
@@@ -0,0 +1,16 @@
++{
++ "canvas": "actor.canvas@1",
++ "id": "cnv_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "title": "Lisbon, 14–17 November",
++ "version": 3,
++ "agent_id": "agt_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "thread": "tsk_42",
++ "updated_at": "2026-11-08T01:00:00.000Z",
++ "blocks": [
++ {
++ "type": "image",
++ "src": "https://tracker.example/p.png",
++ "alt": "x"
++ }
++ ]
++}
ddiff --git a/fixtures/actor.canvas/invalid/html-block.json b/fixtures/actor.canvas/invalid/html-block.json
nnew file mode 100644
iindex 00000000..5a38d24c
---- /dev/null
++++ b/fixtures/actor.canvas/invalid/html-block.json
@@@ -0,0 +1,15 @@
++{
++ "canvas": "actor.canvas@1",
++ "id": "cnv_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "title": "Lisbon, 14–17 November",
++ "version": 3,
++ "agent_id": "agt_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "thread": "tsk_42",
++ "updated_at": "2026-11-08T01:00:00.000Z",
++ "blocks": [
++ {
++ "type": "html",
++ "html": "<script>alert(1)</script>"
++ }
++ ]
++}
ddiff --git a/fixtures/actor.canvas/invalid/http-link.json b/fixtures/actor.canvas/invalid/http-link.json
nnew file mode 100644
iindex 00000000..5e269711
---- /dev/null
++++ b/fixtures/actor.canvas/invalid/http-link.json
@@@ -0,0 +1,16 @@
++{
++ "canvas": "actor.canvas@1",
++ "id": "cnv_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "title": "Lisbon, 14–17 November",
++ "version": 3,
++ "agent_id": "agt_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "thread": "tsk_42",
++ "updated_at": "2026-11-08T01:00:00.000Z",
++ "blocks": [
++ {
++ "type": "link",
++ "href": "javascript:alert(1)",
++ "text": "x"
++ }
++ ]
++}
ddiff --git a/fixtures/actor.canvas/invalid/too-many-blocks.json b/fixtures/actor.canvas/invalid/too-many-blocks.json
nnew file mode 100644
iindex 00000000..338fbe7f
---- /dev/null
++++ b/fixtures/actor.canvas/invalid/too-many-blocks.json
@@@ -0,0 +1,255 @@
++{
++ "canvas": "actor.canvas@1",
++ "id": "cnv_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "title": "Lisbon, 14–17 November",
++ "version": 3,
++ "agent_id": "agt_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "thread": "tsk_42",
++ "updated_at": "2026-11-08T01:00:00.000Z",
++ "blocks": [
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ },
++ {
++ "type": "text",
++ "text": "x"
++ }
++ ]
++}
ddiff --git a/fixtures/actor.canvas/valid/minimal.json b/fixtures/actor.canvas/valid/minimal.json
nnew file mode 100644
iindex 00000000..ef8dad37
---- /dev/null
++++ b/fixtures/actor.canvas/valid/minimal.json
@@@ -0,0 +1,8 @@
++{
++ "canvas": "actor.canvas@1",
++ "id": "cnv_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "title": "Notes",
++ "version": 1,
++ "updated_at": "2026-11-08T01:00:00.000Z",
++ "blocks": []
++}
ddiff --git a/fixtures/actor.canvas/valid/trip.json b/fixtures/actor.canvas/valid/trip.json
nnew file mode 100644
iindex 00000000..2b748774
---- /dev/null
++++ b/fixtures/actor.canvas/valid/trip.json
@@@ -0,0 +1,103 @@
++{
++ "canvas": "actor.canvas@1",
++ "id": "cnv_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "title": "Lisbon, 14–17 November",
++ "version": 3,
++ "agent_id": "agt_01JAB2C3D4E5F6G7H8J9K0MNPQ",
++ "thread": "tsk_42",
++ "updated_at": "2026-11-08T01:00:00.000Z",
++ "blocks": [
++ {
++ "type": "heading",
++ "text": "Lisbon, 14–17 November",
++ "level": 1
++ },
++ {
++ "type": "metric",
++ "label": "Flights and hotel",
++ "value": "€612",
++ "note": "two people, booked nothing yet"
++ },
++ {
++ "type": "checklist",
++ "id": "todo",
++ "items": [
++ {
++ "id": "flights",
++ "text": "Pick the flights",
++ "done": true
++ },
++ {
++ "id": "hotel",
++ "text": "Choose a hotel near Alfama",
++ "done": false
++ }
++ ]
++ },
++ {
++ "type": "table",
++ "id": "hotels",
++ "columns": [
++ "Hotel",
++ "Night",
++ "Walk to the river"
++ ],
++ "rows": [
++ [
++ "Casa A",
++ 118,
++ "6 min"
++ ],
++ [
++ "Hotel B",
++ 96,
++ "14 min"
++ ]
++ ]
++ },
++ {
++ "type": "chart",
++ "id": "prices",
++ "kind": "line",
++ "unit": "€",
++ "series": [
++ {
++ "name": "Flights",
++ "points": [
++ {
++ "x": "Mon",
++ "y": 240
++ },
++ {
++ "x": "Tue",
++ "y": 212
++ },
++ {
++ "x": "Wed",
++ "y": 199
++ }
++ ]
++ }
++ ]
++ },
++ {
++ "type": "image",
++ "src": "https://openvibe.media/t/lisbon.jpg",
++ "alt": "The river at dusk"
++ },
++ {
++ "type": "link",
++ "href": "https://openvibe.video/watch?v=AbCdEfGhIjK",
++ "text": "The walking tour video"
++ },
++ {
++ "type": "text",
++ "text": "Casa A is closer to the river; Hotel B is cheaper.\nBoth have free cancellation until Friday."
++ },
++ {
++ "type": "button",
++ "label": "Book Casa A",
++ "action": "ask"
++ }
++ ]
++}
ddiff --git a/generated/json-schema/bundle.json b/generated/json-schema/bundle.json
iindex c93acbb6..0795fd3e 100644
---- a/generated/json-schema/bundle.json
++++ b/generated/json-schema/bundle.json
@@@ -110142,6 +110142,444 @@
}
}
},
++ "actor.canvas": {
++ "version": "1.0.0",
++ "owner": "actor",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/actor/canvas.v1.json",
++ "title": "ActorCanvas",
++ "description": "actor.canvas@1: an agent's live panel beside the conversation (ADR-059) — a board of a trip, a table of prices, a checklist of what is left. It is DATA: a list of blocks drawn by one renderer (OpenVibe.Shared ov-canvas.js) wherever it is shown (Chat, Surf, the Dock, Actor). There is no HTML, no style, no script and no frame: text is plain and the renderer escapes it; an image is only a picture on openvibe.media; a link opens in a new tab with its host shown. A canvas acts only through the person: a button sends its label to the agent as the person's own message (action ask), and ticking a checklist item tells the agent which item changed; the agent's next steps go through its level and approvals like any other. VERSION only grows: a viewer ignores an older one, and the agent replaces the whole document, never part of it.",
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "canvas",
++ "id",
++ "title",
++ "version",
++ "blocks",
++ "updated_at"
++ ],
++ "properties": {
++ "canvas": {
++ "const": "actor.canvas@1"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^cnv_[0-9A-HJKMNP-TV-Z]{26}$"
++ },
++ "title": {
++ "type": "string",
++ "maxLength": 120,
++ "minLength": 1
++ },
++ "version": {
++ "type": "integer",
++ "minimum": 1,
++ "description": "Grows with every update; a viewer ignores an older version."
++ },
++ "agent_id": {
++ "type": "string",
++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$",
++ "description": "The agent that draws it."
++ },
++ "thread": {
++ "type": "string",
++ "maxLength": 64,
++ "description": "The conversation it sits beside (Actor's task or chat id)."
++ },
++ "updated_at": {
++ "type": "string",
++ "format": "date-time"
++ },
++ "blocks": {
++ "type": "array",
++ "maxItems": 60,
++ "items": {
++ "oneOf": [
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "A heading.",
++ "required": [
++ "type",
++ "text"
++ ],
++ "properties": {
++ "type": {
++ "const": "heading"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "text": {
++ "type": "string",
++ "maxLength": 120,
++ "minLength": 1
++ },
++ "level": {
++ "type": "integer",
++ "enum": [
++ 1,
++ 2
++ ]
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "Plain text; line breaks are kept, and nothing in it is markup.",
++ "required": [
++ "type",
++ "text"
++ ],
++ "properties": {
++ "type": {
++ "const": "text"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "text": {
++ "type": "string",
++ "maxLength": 4000,
++ "minLength": 1
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "A list of short lines.",
++ "required": [
++ "type",
++ "items"
++ ],
++ "properties": {
++ "type": {
++ "const": "list"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "items": {
++ "type": "array",
++ "maxItems": 50,
++ "items": {
++ "type": "string",
++ "maxLength": 280,
++ "minLength": 1
++ }
++ },
++ "ordered": {
++ "type": "boolean"
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "Items the person can tick; a tick tells the agent which item changed.",
++ "required": [
++ "type",
++ "items"
++ ],
++ "properties": {
++ "type": {
++ "const": "checklist"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "items": {
++ "type": "array",
++ "maxItems": 50,
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "id",
++ "text",
++ "done"
++ ],
++ "properties": {
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$"
++ },
++ "text": {
++ "type": "string",
++ "maxLength": 280,
++ "minLength": 1
++ },
++ "done": {
++ "type": "boolean"
++ }
++ }
++ }
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "A table: up to 8 columns and 100 rows of text or numbers.",
++ "required": [
++ "type",
++ "columns",
++ "rows"
++ ],
++ "properties": {
++ "type": {
++ "const": "table"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "columns": {
++ "type": "array",
++ "minItems": 1,
++ "maxItems": 8,
++ "items": {
++ "type": "string",
++ "maxLength": 40,
++ "minLength": 1
++ }
++ },
++ "rows": {
++ "type": "array",
++ "maxItems": 100,
++ "items": {
++ "type": "array",
++ "maxItems": 8,
++ "items": {
++ "oneOf": [
++ {
++ "type": "string",
++ "maxLength": 200
++ },
++ {
++ "type": "number"
++ }
++ ]
++ }
++ }
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "One number that matters, with its label.",
++ "required": [
++ "type",
++ "label",
++ "value"
++ ],
++ "properties": {
++ "type": {
++ "const": "metric"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "label": {
++ "type": "string",
++ "maxLength": 60,
++ "minLength": 1
++ },
++ "value": {
++ "type": "string",
++ "maxLength": 40,
++ "minLength": 1
++ },
++ "note": {
++ "type": "string",
++ "maxLength": 120
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "A small bar or line chart; the renderer draws it, nothing is fetched.",
++ "required": [
++ "type",
++ "kind",
++ "series"
++ ],
++ "properties": {
++ "type": {
++ "const": "chart"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "kind": {
++ "type": "string",
++ "enum": [
++ "bar",
++ "line"
++ ]
++ },
++ "unit": {
++ "type": "string",
++ "maxLength": 12
++ },
++ "series": {
++ "type": "array",
++ "minItems": 1,
++ "maxItems": 4,
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "name",
++ "points"
++ ],
++ "properties": {
++ "name": {
++ "type": "string",
++ "maxLength": 40,
++ "minLength": 1
++ },
++ "points": {
++ "type": "array",
++ "minItems": 1,
++ "maxItems": 60,
++ "items": {
++ "type": "object",
++ "additionalProperties": false,
++ "required": [
++ "x",
++ "y"
++ ],
++ "properties": {
++ "x": {
++ "type": "string",
++ "maxLength": 20,
++ "minLength": 1
++ },
++ "y": {
++ "type": "number"
++ }
++ }
++ }
++ }
++ }
++ }
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "A picture from OpenVibe.Media.",
++ "required": [
++ "type",
++ "src",
++ "alt"
++ ],
++ "properties": {
++ "type": {
++ "const": "image"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "src": {
++ "type": "string",
++ "pattern": "^https://openvibe\\.media/[A-Za-z0-9._~/%-]{1,1024}(\\?[A-Za-z0-9._~=&%-]{0,200})?$",
++ "description": "A picture on openvibe.media, and nowhere else."
++ },
++ "alt": {
++ "type": "string",
++ "maxLength": 200
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "A link: it opens in a new tab and shows its host.",
++ "required": [
++ "type",
++ "href",
++ "text"
++ ],
++ "properties": {
++ "type": {
++ "const": "link"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "href": {
++ "type": "string",
++ "pattern": "^https://[^\\s\"'<>]{1,2048}$"
++ },
++ "text": {
++ "type": "string",
++ "maxLength": 120,
++ "minLength": 1
++ }
++ }
++ },
++ {
++ "type": "object",
++ "additionalProperties": false,
++ "description": "A reply the person can send with one tap.",
++ "required": [
++ "type",
++ "label",
++ "action"
++ ],
++ "properties": {
++ "type": {
++ "const": "button"
++ },
++ "id": {
++ "type": "string",
++ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
++ "description": "A stable id, so a viewer keeps its place when the canvas updates."
++ },
++ "label": {
++ "type": "string",
++ "maxLength": 40,
++ "minLength": 1
++ },
++ "action": {
++ "const": "ask",
++ "description": "The label goes to the agent as the person's own message. A button never acts by itself."
++ }
++ }
++ }
++ ]
++ }
++ }
++ }
++ }
++ },
"actor.skill": {
"version": "1.0.0",
"owner": "actor",
ddiff --git a/generated/openapi/actor.json b/generated/openapi/actor.json
iindex bb103bdb..44ebfb79 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.339.0",
-- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 5308e77b..923ab8bf 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.339.0",
-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 2b803079..7d903ac3 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.339.0",
-- "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 4cb3fad5..3d50190b 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.339.0",
-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 65fcced3..fd43e86b 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.339.0",
-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 120f0ceb..16842f2c 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.339.0",
-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 e49308a8..14dae654 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.339.0",
-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.340.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/codes.json b/generated/openapi/codes.json
iindex 5f072481..55fe0d02 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.339.0",
-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 8141594f..6087aa11 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.339.0",
-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.340.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/connect.json b/generated/openapi/connect.json
iindex cc7f57aa..1787fdca 100644
---- a/generated/openapi/connect.json
++++ b/generated/openapi/connect.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe Connect API",
-- "version": "0.339.0",
-- "description": "Routes of OpenVibe Connect as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe Connect as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 d800f683..3be50b34 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.339.0",
-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 ef2464a4..effe1032 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.339.0",
-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.340.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/design.json b/generated/openapi/design.json
iindex 29290304..7250e8e4 100644
---- a/generated/openapi/design.json
++++ b/generated/openapi/design.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Design API",
-- "version": "0.339.0",
-- "description": "Routes of OpenVibe.Design as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Design as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 eae13068..4ae150e0 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.339.0",
-- "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 6bca1ef3..31979543 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.339.0",
-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 ceb6c5ed..6f3a7c93 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.339.0",
-- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 cb72db48..e48ec220 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.339.0",
-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 7ac9a920..f0716705 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.339.0",
-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.340.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/inventory.json b/generated/openapi/inventory.json
iindex 453f5fcd..7c44360b 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.339.0",
-- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 4fb881eb..1eff048f 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.339.0",
-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.340.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/mail.json b/generated/openapi/mail.json
iindex 6e9471b1..056aa235 100644
---- a/generated/openapi/mail.json
++++ b/generated/openapi/mail.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Mail API",
-- "version": "0.339.0",
-- "description": "Routes of OpenVibe.Mail as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Mail as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 8d75adf5..8aa7fb9e 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.339.0",
-- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 ad30fdb7..2bcb9022 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.339.0",
-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 f4b247bc..959fa009 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.339.0",
-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 fedcf554..cdfde892 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.339.0",
-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 9e8d0f73..37ca2a17 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.339.0",
-- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 b0b83d57..03712ced 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.339.0",
-- "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 99a45b06..398bf8f4 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.339.0",
-- "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 69e57e09..1bceae71 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.339.0",
-- "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 e47615d1..6189495d 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.339.0",
-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 dcc2cbe5..ecbe49b4 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.339.0",
-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 732743b5..74b22cc1 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.339.0",
-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 29dfe1b9..ed743cc1 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.339.0",
-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 5babe948..937b9dd6 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.339.0",
-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 ac01fbda..21c907e7 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.339.0",
-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 b76854c8..f0aea18e 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.339.0",
-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 6b06827b..449ecc1a 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.339.0",
-- "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 af318d2c..42631b6a 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.339.0",
-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 c4ca1ede..1d547776 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.339.0",
-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 6ae84535..f981e145 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.339.0",
-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 bbbc1f24..2afa9d03 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.339.0",
-- "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 b2724bb4..bb9cc2da 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.339.0",
-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 4d40d8e2..23cebf10 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.339.0",
-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 2355115a..3c4a5cfe 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.339.0",
-- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 fc045c83..f96541a7 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.339.0",
-- "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.339.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.340.0",
++ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.340.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 d0f7e351..8021f504 100644
---- a/generated/typescript/index.d.ts
++++ b/generated/typescript/index.d.ts
@@@ -301554,6 +301554,363 @@ export interface ActorSkillListing {
retired_at?: string;
}
++/** [email protected] (owner: actor) */
++/**
++ * actor.canvas@1: an agent's live panel beside the conversation (ADR-059) — a board of a trip, a table of prices, a checklist of what is left. It is DATA: a list of blocks drawn by one renderer (OpenVibe.Shared ov-canvas.js) wherever it is shown (Chat, Surf, the Dock, Actor). There is no HTML, no style, no script and no frame: text is plain and the renderer escapes it; an image is only a picture on openvibe.media; a link opens in a new tab with its host shown. A canvas acts only through the person: a button sends its label to the agent as the person's own message (action ask), and ticking a checklist item tells the agent which item changed; the agent's next steps go through its level and approvals like any other. VERSION only grows: a viewer ignores an older one, and the agent replaces the whole document, never part of it.
++ */
++export interface ActorCanvas {
++ canvas: "actor.canvas@1";
++ id: string;
++ title: string;
++ /**
++ * Grows with every update; a viewer ignores an older version.
++ */
++ version: number;
++ /**
++ * The agent that draws it.
++ */
++ agent_id?: string;
++ /**
++ * The conversation it sits beside (Actor's task or chat id).
++ */
++ thread?: string;
++ updated_at: string;
++ /**
++ * @maxItems 60
++ */
++ blocks: (
++ | {
++ type: "heading";
++ /**
++ * A stable id, so a viewer keeps its place when the canvas updates.
++ */
++ id?: string;
++ text: string;
++ level?: 1 | 2;
++ }
++ | {
++ type: "text";
++ /**
++ * A stable id, so a viewer keeps its place when the canvas updates.
++ */
++ id?: string;
++ text: string;
++ }
++ | {
++ type: "list";
++ /**
++ * A stable id, so a viewer keeps its place when the canvas updates.
++ */
++ id?: string;
++ /**
++ * @maxItems 50
++ */
++ items: string[];
++ ordered?: boolean;
++ }
++ | {
++ type: "checklist";
++ /**
++ * A stable id, so a viewer keeps its place when the canvas updates.
++ */
++ id?: string;
++ /**
++ * @maxItems 50
++ */
++ items: {
++ id: string;
++ text: string;
++ done: boolean;
++ }[];
++ }
++ | {
++ type: "table";
++ /**
++ * A stable id, so a viewer keeps its place when the canvas updates.
++ */
++ id?: string;
++ /**
++ * @minItems 1
++ * @maxItems 8
++ */
++ columns:
++ | [string]
++ | [string, string]
++ | [string, string, string]
++ | [string, string, string, string]
++ | [string, string, string, string, string]
++ | [string, string, string, string, string, string]
++ | [string, string, string, string, string, string, string]
++ | [string, string, string, string, string, string, string, string];
++ /**
++ * @maxItems 100
++ */
++ rows:
++ | []
++ | [string | number]
++ | [string | number, string | number]
++ | [string | number, string | number, string | number]
++ | [string | number, string | number, string | number, string | number]
++ | [string | number, string | number, string | number, string | number, string | number]
++ | [string | number, string | number, string | number, string | number, string | number, string | number]
++ | [
++ string | number,
++ string | number,
++ string | number,
++ string | number,
++ string | number,
++ string | number,
++ string | number
++ ]
++ | [
++ string | number,
++ string | number,
++ string | number,
++ string | number,
++ string | number,
++ string | number,
++ string | number,
++ string | number
++ ][];
++ }
++ | {
++ type: "metric";
++ /**
++ * A stable id, so a viewer keeps its place when the canvas updates.
++ */
++ id?: string;
++ label: string;
++ value: string;
++ note?: string;
++ }
++ | {
++ type: "chart";
++ /**
++ * A stable id, so a viewer keeps its place when the canvas updates.
++ */
++ id?: string;
++ kind: "bar" | "line";
++ unit?: string;
++ /**
++ * @minItems 1
++ * @maxItems 4
++ */
++ series:
++ | [
++ {
++ name: string;
++ /**
++ * @minItems 1
++ * @maxItems 60
++ */
++ points: [
++ {
++ x: string;
++ y: number;
++ },
++ ...{
++ x: string;
++ y: number;
++ }[]
++ ];
++ }
++ ]
++ | [
++ {
++ name: string;
++ /**
++ * @minItems 1
++ * @maxItems 60
++ */
++ points: [
++ {
++ x: string;
++ y: number;
++ },
++ ...{
++ x: string;
++ y: number;
++ }[]
++ ];
++ },
++ {
++ name: string;
++ /**
++ * @minItems 1
++ * @maxItems 60
++ */
++ points: [
++ {
++ x: string;
++ y: number;
++ },
++ ...{
++ x: string;
++ y: number;
++ }[]
++ ];
++ }
++ ]
++ | [
++ {
++ name: string;
++ /**
++ * @minItems 1
++ * @maxItems 60
++ */
++ points: [
++ {
++ x: string;
++ y: number;
++ },
++ ...{
++ x: string;
++ y: number;
++ }[]
++ ];
++ },
++ {
++ name: string;
++ /**
++ * @minItems 1
++ * @maxItems 60
++ */
++ points: [
++ {
++ x: string;
++ y: number;
++ },
++ ...{
++ x: string;
++ y: number;
++ }[]
++ ];
++ },
++ {
++ name: string;
++ /**
++ * @minItems 1
++ * @maxItems 60
++ */
++ points: [
++ {
++ x: string;
++ y: number;
++ },
++ ...{
++ x: string;
++ y: number;
++ }[]
++ ];
++ }
++ ]
++ | [
++ {
++ name: string;
++ /**
++ * @minItems 1
++ * @maxItems 60
++ */
++ points: [
++ {
++ x: string;
++ y: number;
++ },
++ ...{
++ x: string;
++ y: number;
++ }[]
++ ];
++ },
++ {
++ name: string;
++ /**
++ * @minItems 1
++ * @maxItems 60
++ */
++ points: [
++ {
++ x: string;
++ y: number;
++ },
++ ...{
++ x: string;
++ y: number;
++ }[]
++ ];
++ },
++ {
++ name: string;
++ /**
++ * @minItems 1
++ * @maxItems 60
++ */
++ points: [
++ {
++ x: string;
++ y: number;
++ },
++ ...{
++ x: string;
++ y: number;
++ }[]
++ ];
++ },
++ {
++ name: string;
++ /**
++ * @minItems 1
++ * @maxItems 60
++ */
++ points: [
++ {
++ x: string;
++ y: number;
++ },
++ ...{
++ x: string;
++ y: number;
++ }[]
++ ];
++ }
++ ];
++ }
++ | {
++ type: "image";
++ /**
++ * A stable id, so a viewer keeps its place when the canvas updates.
++ */
++ id?: string;
++ /**
++ * A picture on openvibe.media, and nowhere else.
++ */
++ src: string;
++ alt: string;
++ }
++ | {
++ type: "link";
++ /**
++ * A stable id, so a viewer keeps its place when the canvas updates.
++ */
++ id?: string;
++ href: string;
++ text: string;
++ }
++ | {
++ type: "button";
++ /**
++ * A stable id, so a viewer keeps its place when the canvas updates.
++ */
++ id?: string;
++ label: string;
++ /**
++ * The label goes to the agent as the person's own message. A button never acts by itself.
++ */
++ action: "ask";
++ }
++ )[];
++}
++
/** [email protected] (owner: actor) */
/**
* actor.skill@1: a skill, the document an agent loads to do one kind of job well (ADR-058). It follows the open Markdown-skill shape (a name, a one-line description, Markdown instructions) with what OpenVibe adds: a slug, a version, up to ten TEXT resources, and REQUIRES, what the skill may ask an agent to do. A skill GRANTS NOTHING: requires only declares; the agent's level and approvals decide every step (actor approvals, HO1), and a step of a kind the skill did not declare is a scan finding (actor.skill-listing@1). A v1 skill RUNS AS A PROMPT and nothing else: no code, no binary, no install recipe (a skill that runs code waits for a sandbox and a new major). DESCRIPTION is the only part an agent reads before it loads the skill (progressive disclosure), so it says when to use it. The INTEGRITY block is a sha256 over the canonical JSON of the document: it shows the file was not edited after it was written and says nothing about who wrote it, so the AUTHOR is a claim; the registry's listing is the only place a publisher is vouched for. The canonical JSON is the document's own fields, in this order: skill, slug, name, description, version, instructions, resources, requires, runs, license, author; an optional field that is absent or empty is left out; resources are [{ path, media_type, text }] in the document's order; requires is { kinds, apps, hosts } with empty lists left out; author is { subject } or { subject, name }; the value is the compact JSON of that object (no spaces). CREATED_AT is not hashed.
ddiff --git a/package.json b/package.json
iindex 83fd8259..fbf4e8d2 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-contracts",
-- "version": "0.339.0",
++ "version": "0.340.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",
ddiff --git a/test/run.js b/test/run.js
iindex 5efd0a23..3af8c200 100644
---- a/test/run.js
++++ b/test/run.js
@@@ -1022,6 +1022,20 @@ sub(path.join(__dirname, 'changelog-history.test.js'));
ok(dangling.length === 0, `generated types reference undeclared names: ${dangling.join(', ')}`);
}
++// ── actor.canvas@1 (ADR-059): data, never markup; a button only asks ─────────
++{
++ const c = contracts.schema('actor.canvas@1');
++ const types = c.properties.blocks.items.oneOf.map((b) => b.properties.type.const);
++ ok(!types.some((t) => /^(html|script|style|iframe|frame|embed|markup)$/i.test(t)), 'no canvas block is markup, a script, a style or a frame');
++ const button = c.properties.blocks.items.oneOf.find((b) => b.properties.type.const === 'button');
++ ok(button.properties.action.const === 'ask', 'a button only sends its label to the agent as the person\'s message');
++ const image = c.properties.blocks.items.oneOf.find((b) => b.properties.type.const === 'image');
++ ok(new RegExp(image.properties.src.pattern).test('https://openvibe.media/t/x.jpg') && !new RegExp(image.properties.src.pattern).test('https://tracker.example/p.png'), 'a picture comes from openvibe.media only');
++ const link = c.properties.blocks.items.oneOf.find((b) => b.properties.type.const === 'link');
++ ok(!new RegExp(link.properties.href.pattern).test('javascript:alert(1)') && !new RegExp(link.properties.href.pattern).test('http://x.y'), 'a link is https only');
++ ok(c.properties.blocks.maxItems === 60, 'at most 60 blocks');
++}
++
// ── actor.skill@1 and actor.skill-listing@1 (ADR-058) ───────────────────────
// A skill is a document: its integrity hash covers its canonical content (not a signature); it runs as a prompt
// only; it grants nothing, and what it may ask for is HO1's kinds; the listing's scan and curator travel with it.